diff --git a/Android.bp b/Android.bp index c1c30498743d4f18ec604dec7f40cefc49da027d..34a646915fd96b60049826a9dc9a7b244a9c07a0 100644 --- a/Android.bp +++ b/Android.bp @@ -10,8 +10,6 @@ art_static_dependencies = [ "libz", "libbacktrace", "libcutils", - "libunwindbacktrace", - "libunwind", "libunwindstack", "libutils", "libbase", @@ -38,6 +36,7 @@ subdirs = [ "imgdiag", "libartbase", "libdexfile", + "libprofile", "oatdump", "openjdkjvm", "openjdkjvmti", diff --git a/Android.mk b/Android.mk index e4f4e74cb2c6541c8361134912ba2e5cf49a11d8..1c946292ef7f36755243387bf120f7c0204e52c7 100644 --- a/Android.mk +++ b/Android.mk @@ -110,22 +110,33 @@ TEST_ART_ADB_ROOT_AND_REMOUNT := \ # Sync test files to the target, depends upon all things that must be pushed to the target. .PHONY: test-art-target-sync -# Check if we need to sync. In case ART_TEST_ANDROID_ROOT is not empty, -# the code below uses 'adb push' instead of 'adb sync', which does not -# check if the files on the device have changed. +# Check if we need to sync. In case ART_TEST_CHROOT or ART_TEST_ANDROID_ROOT +# is not empty, the code below uses 'adb push' instead of 'adb sync', +# which does not check if the files on the device have changed. +# TODO: Remove support for ART_TEST_ANDROID_ROOT when it is no longer needed. ifneq ($(ART_TEST_NO_SYNC),true) +# Sync system and data partitions. ifeq ($(ART_TEST_ANDROID_ROOT),) +ifeq ($(ART_TEST_CHROOT),) test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS) $(TEST_ART_ADB_ROOT_AND_REMOUNT) adb sync system && adb sync data else test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS) $(TEST_ART_ADB_ROOT_AND_REMOUNT) - adb wait-for-device push $(PRODUCT_OUT)/system $(ART_TEST_ANDROID_ROOT) -# Push the contents of the `data` dir into `/data` on the device. If -# `/data` already exists on the device, it is not overwritten, but its -# contents are updated. - adb push $(PRODUCT_OUT)/data / + adb wait-for-device + adb push $(PRODUCT_OUT)/system $(ART_TEST_CHROOT)/ + adb push $(PRODUCT_OUT)/data $(ART_TEST_CHROOT)/ +endif +else +test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS) + $(TEST_ART_ADB_ROOT_AND_REMOUNT) + adb wait-for-device + adb push $(PRODUCT_OUT)/system $(ART_TEST_CHROOT)$(ART_TEST_ANDROID_ROOT) +# Push the contents of the `data` dir into `$(ART_TEST_CHROOT)/data` on the device (note +# that $(ART_TEST_CHROOT) can be empty). If `$(ART_TEST_CHROOT)/data` already exists on +# the device, it is not overwritten, but its content is updated. + adb push $(PRODUCT_OUT)/data $(ART_TEST_CHROOT)/ endif endif @@ -234,19 +245,6 @@ endif 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 @@ -298,7 +296,7 @@ test-art-target-jit$(ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-run-test-jit $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) # Secondary target architecture variants: -ifdef TARGET_2ND_ARCH +ifdef 2ND_ART_PHONY_TEST_TARGET_SUFFIX .PHONY: test-art-target$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) test-art-target$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-gtest$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) \ test-art-target-run-test$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) @@ -321,19 +319,6 @@ test-art-target-jit$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-run-test $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) endif -# Valgrind. -.PHONY: valgrind-test-art-target -valgrind-test-art-target: valgrind-test-art-target-gtest - $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) - -.PHONY: valgrind-test-art-target32 -valgrind-test-art-target32: valgrind-test-art-target-gtest32 - $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) - -.PHONY: valgrind-test-art-target64 -valgrind-test-art-target64: valgrind-test-art-target-gtest64 - $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) - ####################### # Fake packages for ART @@ -471,10 +456,12 @@ build-art-target-golem: dex2oat dalvikvm patchoat linker libstdc++ \ $(ART_TARGET_SHARED_LIBRARY_BENCHMARK) \ $(TARGET_CORE_IMG_OUT_BASE).art \ $(TARGET_CORE_IMG_OUT_BASE)-interpreter.art - # remove libartd.so and libdexfiled.so from public.libraries.txt because golem builds + # remove debug libraries from public.libraries.txt because golem builds # won't have it. sed -i '/libartd.so/d' $(TARGET_OUT)/etc/public.libraries.txt sed -i '/libdexfiled.so/d' $(TARGET_OUT)/etc/public.libraries.txt + sed -i '/libprofiled.so/d' $(TARGET_OUT)/etc/public.libraries.txt + sed -i '/libartbased.so/d' $(TARGET_OUT)/etc/public.libraries.txt ######################################################################## # Phony target for building what go/lem requires on host. diff --git a/adbconnection/Android.bp b/adbconnection/Android.bp index 441b706556a63e80a1b3de75e27d4473d1e60e62..95fc27428690636672aeabfabb6af45f5f1584e0 100644 --- a/adbconnection/Android.bp +++ b/adbconnection/Android.bp @@ -65,6 +65,7 @@ art_cc_library { defaults: ["adbconnection-defaults"], shared_libs: [ "libart", + "libartbase", ], } @@ -76,5 +77,6 @@ art_cc_library { ], shared_libs: [ "libartd", + "libartbased", ], } diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc index 4c2d4d72d89f798071b75dab4a196960b4412ac6..ad941481fd2f69536f55759ffe4e905f4aa5f34c 100644 --- a/adbconnection/adbconnection.cc +++ b/adbconnection/adbconnection.cc @@ -23,8 +23,8 @@ #include "base/logging.h" #include "base/macros.h" #include "base/mutex.h" -#include "java_vm_ext.h" -#include "jni_env_ext.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_env_ext.h" #include "mirror/throwable.h" #include "nativehelper/ScopedLocalRef.h" #include "runtime-inl.h" @@ -489,7 +489,7 @@ bool AdbConnectionState::SetupAdbConnection() { int sleep_ms = 500; const int sleep_max_ms = 2*1000; - android::base::unique_fd sock(socket(AF_UNIX, SOCK_SEQPACKET, 0)); + android::base::unique_fd sock(socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0)); if (sock < 0) { PLOG(ERROR) << "Could not create ADB control socket"; return false; @@ -872,7 +872,7 @@ std::string AdbConnectionState::MakeAgentArg() { (ContainsArgument(opts, "server=y") ? "" : "server=y,") + // See the comment above for why we need to be suspend=n. Since the agent defaults to // suspend=y we will add it if it wasn't already present. - (ContainsArgument(opts, "suspend=n") ? "" : "suspend=n") + + (ContainsArgument(opts, "suspend=n") ? "" : "suspend=n,") + "transport=dt_fd_forward,address=" + std::to_string(remote_agent_control_sock_); } diff --git a/benchmark/jobject-benchmark/jobject_benchmark.cc b/benchmark/jobject-benchmark/jobject_benchmark.cc index 7e0a5362c9e5578e9ce45c8bde888fb059d52055..2f38b78eaf4ad2bf68f0a8b2131320d4d9f791a6 100644 --- a/benchmark/jobject-benchmark/jobject_benchmark.cc +++ b/benchmark/jobject-benchmark/jobject_benchmark.cc @@ -16,7 +16,7 @@ #include "jni.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "mirror/class-inl.h" #include "scoped_thread_state_change-inl.h" diff --git a/build/Android.bp b/build/Android.bp index 14a112f0c7fe97144f4edba2776cf16e83b8ac32..b6c1ba242233a54bd227414c26d0ae80e77c9b09 100644 --- a/build/Android.bp +++ b/build/Android.bp @@ -17,6 +17,27 @@ bootstrap_go_package { pluginFor: ["soong_build"], } +art_clang_tidy_errors = [ + // Protect scoped things like MutexLock. + "bugprone-unused-raii", +] +// Should be: strings.Join(art_clang_tidy_errors, ","). +art_clang_tidy_errors_str = "bugprone-unused-raii" + +art_clang_tidy_disabled = [ + "-google-default-arguments", + // We have local stores that are only used for debug checks. + "-clang-analyzer-deadcode.DeadStores", + // We are OK with some static globals and that they can, in theory, throw. + "-cert-err58-cpp", + // We have lots of C-style variadic functions, and are OK with them. JNI ensures + // that working around this warning would be extra-painful. + "-cert-dcl50-cpp", + // No exceptions. + "-misc-noexcept-move-constructor", + "-performance-noexcept-move-constructor", +] + art_global_defaults { // Additional flags are computed by art.go @@ -128,23 +149,10 @@ art_global_defaults { }, include_dirs: [ - "external/valgrind/include", - "external/valgrind", "external/vixl/src", ], - tidy_checks: [ - "-google-default-arguments", - // We have local stores that are only used for debug checks. - "-clang-analyzer-deadcode.DeadStores", - // We are OK with some static globals and that they can, in theory, throw. - "-cert-err58-cpp", - // We have lots of C-style variadic functions, and are OK with them. JNI ensures - // that working around this warning would be extra-painful. - "-cert-dcl50-cpp", - // No exceptions. - "-misc-noexcept-move-constructor", - ], + tidy_checks: art_clang_tidy_errors + art_clang_tidy_disabled, tidy_flags: [ // The static analyzer treats DCHECK as always enabled; we sometimes get @@ -154,6 +162,8 @@ art_global_defaults { // void foo() { CHECK(kIsFooEnabled); /* do foo... */ } // not being marked noreturn if kIsFooEnabled is false. "-extra-arg=-Wno-missing-noreturn", + // Use art_clang_tidy_errors for build errors. + "-warnings-as-errors=" + art_clang_tidy_errors_str, ], } diff --git a/build/Android.common.mk b/build/Android.common.mk index b0fa124e48348b585e100faa6c40a939efc3e1cb..a6a9f0fc479635616edd32a8f985884b94bb9baf 100644 --- a/build/Android.common.mk +++ b/build/Android.common.mk @@ -59,8 +59,6 @@ ifdef TARGET_2ND_ARCH ART_PHONY_TEST_TARGET_SUFFIX := 64 2ND_ART_PHONY_TEST_TARGET_SUFFIX := 32 else - # TODO: ??? - $(warning Do not know what to do with this multi-target configuration!) ART_PHONY_TEST_TARGET_SUFFIX := 32 2ND_ART_PHONY_TEST_TARGET_SUFFIX := endif diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index b483e5f6f2924b2c57e279cb47b2b941fbf82e71..7d1115eec007837f5dea8d62f791c31c2c20fe56 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -89,43 +89,59 @@ ART_TEST_TARGET_GTEST_EmptyUncompressed_DEX := $(basename $(ART_TEST_TARGET_GTES ART_TEST_HOST_GTEST_MultiDexUncompressed_DEX := $(basename $(ART_TEST_HOST_GTEST_MultiDex_DEX))Uncompressed$(suffix $(ART_TEST_HOST_GTEST_MultiDex_DEX)) ART_TEST_TARGET_GTEST_MultiDexUncompressed_DEX := $(basename $(ART_TEST_TARGET_GTEST_MultiDex_DEX))Uncompressed$(suffix $(ART_TEST_TARGET_GTEST_MultiDex_DEX)) +ifdef ART_TEST_HOST_GTEST_Main_DEX $(ART_TEST_HOST_GTEST_MainStripped_DEX): $(ART_TEST_HOST_GTEST_Main_DEX) cp $< $@ $(call dexpreopt-remove-classes.dex,$@) +endif +ifdef ART_TEST_TARGET_GTEST_Main_DEX $(ART_TEST_TARGET_GTEST_MainStripped_DEX): $(ART_TEST_TARGET_GTEST_Main_DEX) cp $< $@ $(call dexpreopt-remove-classes.dex,$@) +endif +ifdef ART_TEST_HOST_GTEST_Main_DEX $(ART_TEST_HOST_GTEST_MainUncompressed_DEX): $(ART_TEST_HOST_GTEST_Main_DEX) $(ZIPALIGN) cp $< $@ $(call uncompress-dexs, $@) $(call align-package, $@) +endif +ifdef ART_TEST_TARGET_GTEST_Main_DEX $(ART_TEST_TARGET_GTEST_MainUncompressed_DEX): $(ART_TEST_TARGET_GTEST_Main_DEX) $(ZIPALIGN) cp $< $@ $(call uncompress-dexs, $@) $(call align-package, $@) +endif +ifdef ART_TEST_HOST_GTEST_Main_DEX $(ART_TEST_HOST_GTEST_EmptyUncompressed_DEX): $(ZIPALIGN) touch $(dir $@)classes.dex zip -j -qD -X -0 $@ $(dir $@)classes.dex rm $(dir $@)classes.dex +endif +ifdef ART_TEST_TARGET_GTEST_Main_DEX $(ART_TEST_TARGET_GTEST_EmptyUncompressed_DEX): $(ZIPALIGN) touch $(dir $@)classes.dex zip -j -qD -X -0 $@ $(dir $@)classes.dex rm $(dir $@)classes.dex +endif +ifdef ART_TEST_HOST_GTEST_MultiDex_DEX $(ART_TEST_HOST_GTEST_MultiDexUncompressed_DEX): $(ART_TEST_HOST_GTEST_MultiDex_DEX) $(ZIPALIGN) cp $< $@ $(call uncompress-dexs, $@) $(call align-package, $@) +endif +ifdef ART_TEST_TARGET_GTEST_MultiDex_DEX $(ART_TEST_TARGET_GTEST_MultiDexUncompressed_DEX): $(ART_TEST_TARGET_GTEST_MultiDex_DEX) $(ZIPALIGN) cp $< $@ $(call uncompress-dexs, $@) $(call align-package, $@) +endif ART_TEST_GTEST_VerifierDeps_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDeps/*.smali)) ART_TEST_GTEST_VerifierDepsMulti_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDepsMulti/*.smali)) @@ -156,6 +172,7 @@ ART_GTEST_class_loader_context_test_DEX_DEPS := Main MultiDex MyClass ForClassLo ART_GTEST_class_table_test_DEX_DEPS := XandY ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes +ART_GTEST_dexanalyze_test_DEX_DEPS := MultiDex ART_GTEST_dexlayout_test_DEX_DEPS := ManyMethods ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ManyMethods Statics VerifierDeps MainUncompressed EmptyUncompressed ART_GTEST_dex2oat_image_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps @@ -260,6 +277,16 @@ ART_GTEST_dexdump_test_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_DEFAULT_32) \ dexdump2-target +# The dexanalyze test requires an image and the dexanalyze utility. +ART_GTEST_dexanalyze_test_HOST_DEPS := \ + $(HOST_CORE_IMAGE_DEFAULT_64) \ + $(HOST_CORE_IMAGE_DEFAULT_32) \ + dexanalyze-host +ART_GTEST_dexanalyze_test_TARGET_DEPS := \ + $(TARGET_CORE_IMAGE_DEFAULT_64) \ + $(TARGET_CORE_IMAGE_DEFAULT_32) \ + dexanalyze-target + # The dexlayout test requires an image and the dexlayout utility. # TODO: rename into dexdump when migration completes ART_GTEST_dexlayout_test_HOST_DEPS := \ @@ -295,6 +322,12 @@ ART_GTEST_imgdiag_test_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_DEFAULT_32) \ imgdiagd-target +# Dex analyze test requires dexanalyze. +ART_GTEST_dexanalyze_test_HOST_DEPS := \ + dexanalyze-host +ART_GTEST_dexanalyze_test_TARGET_DEPS := \ + dexanalyze-target + # Oatdump test requires an image and oatfile to dump. ART_GTEST_oatdump_test_HOST_DEPS := \ $(HOST_CORE_IMAGE_DEFAULT_64) \ @@ -337,6 +370,7 @@ ART_TEST_MODULES := \ art_compiler_tests \ art_compiler_host_tests \ art_dex2oat_tests \ + art_dexanalyze_tests \ art_dexdiag_tests \ art_dexdump_tests \ art_dexlayout_tests \ @@ -346,6 +380,7 @@ ART_TEST_MODULES := \ art_imgdiag_tests \ art_libartbase_tests \ art_libdexfile_tests \ + art_libprofile_tests \ art_oatdump_tests \ art_patchoat_tests \ art_profman_tests \ @@ -373,15 +408,9 @@ endif 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' @@ -389,46 +418,22 @@ ifneq ($(ART_TEST_ANDROID_ROOT),) 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 # $(3): 2ND_ or undefined - used to differentiate between the primary and secondary architecture. # $(4): LD_LIBRARY_PATH or undefined - used in case libartd.so is not in /system/lib/ define define-art-gtest-rule-target + ifeq ($(ART_TEST_CHROOT),) + # Non-chroot configuration. + maybe_art_test_chroot := + maybe_chroot_command := + else + # Chroot configuration. + maybe_art_test_chroot := $(ART_TEST_CHROOT) + maybe_chroot_command := chroot $(ART_TEST_CHROOT) + endif + gtest_rule := test-art-target-gtest-$(1)$$($(3)ART_PHONY_TEST_TARGET_SUFFIX) gtest_exe := $(OUT_DIR)/$(2) gtest_target_exe := $$(patsubst $(PRODUCT_OUT)/%,/%,$$(gtest_exe)) @@ -442,22 +447,27 @@ define define-art-gtest-rule-target $$($(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): PRIVATE_TARGET_EXE := $$(gtest_target_exe) +$$(gtest_rule): PRIVATE_MAYBE_CHROOT_COMMAND := $$(maybe_chroot_command) -$$(gtest_rule) valgrind-$$(gtest_rule): PRIVATE_TARGET_EXE := $$(gtest_target_exe) +# File witnessing the success of the gtest, the presence of which means the gtest's success. +gtest_witness := \ + $$(maybe_art_test_chroot)$(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$(gtest_rule)-$$$$PPID + +$$(gtest_rule): PRIVATE_GTEST_WITNESS := $$(gtest_witness) .PHONY: $$(gtest_rule) $$(gtest_rule): test-art-target-sync - $(hide) adb shell touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID - $(hide) adb shell rm $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID - $(hide) adb shell chmod 755 $$(PRIVATE_TARGET_EXE) + $(hide) adb shell touch $$(PRIVATE_GTEST_WITNESS) + $(hide) adb shell rm $$(PRIVATE_GTEST_WITNESS) + $(hide) adb shell $$(PRIVATE_MAYBE_CHROOT_COMMAND) chmod 755 $$(PRIVATE_TARGET_EXE) $(hide) $$(call ART_TEST_SKIP,$$@) && \ - (adb shell "env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ + (adb shell "$$(PRIVATE_MAYBE_CHROOT_COMMAND) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) $$(PRIVATE_TARGET_EXE) \ - && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID" \ - && (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID /tmp/ \ - && $$(call ART_TEST_PASSED,$$@)) \ + && touch $$(PRIVATE_GTEST_WITNESS)" \ + && (adb pull $$(PRIVATE_GTEST_WITNESS) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \ || $$(call ART_TEST_FAILED,$$@)) $(hide) rm -f /tmp/$$@-$$$$PPID @@ -465,46 +475,15 @@ $$(gtest_rule): test-art-target-sync ART_TEST_TARGET_GTEST_RULES += $$(gtest_rule) ART_TEST_TARGET_GTEST_$(1)_RULES += $$(gtest_rule) -.PHONY: valgrind-$$(gtest_rule) -valgrind-$$(gtest_rule): $(ART_VALGRIND_TARGET_DEPENDENCIES) test-art-target-sync - $(hide) adb shell touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID - $(hide) adb shell rm $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID - $(hide) adb shell chmod 755 $$(PRIVATE_TARGET_EXE) - $(hide) $$(call ART_TEST_SKIP,$$@) && \ - (adb shell "env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ - ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \ - $$$$ANDROID_ROOT/bin/valgrind \ - --leak-check=full --error-exitcode=1 --workaround-gcc296-bugs=yes \ - --suppressions=$(ART_TARGET_TEST_DIR)/valgrind-target-suppressions.txt \ - --num-callers=50 --show-mismatched-frees=no $$(PRIVATE_TARGET_EXE) \ - && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID" \ - && (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID /tmp/ \ - && $$(call ART_TEST_PASSED,$$@)) \ - || $$(call ART_TEST_FAILED,$$@)) - $(hide) rm -f /tmp/$$@-$$$$PPID - - ART_TEST_TARGET_VALGRIND_GTEST$$($(3)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += \ - valgrind-$$(gtest_rule) - ART_TEST_TARGET_VALGRIND_GTEST_RULES += valgrind-$$(gtest_rule) - ART_TEST_TARGET_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule) - # Clear locally defined variables. - valgrind_gtest_rule := - gtest_rule := - gtest_exe := + gtest_witness := + maybe_chroot_command := + maybe_art_test_chroot := gtest_target_exe := + gtest_exe := + gtest_rule := endef # define-art-gtest-rule-target -ART_VALGRIND_DEPENDENCIES := \ - $(HOST_OUT_EXECUTABLES)/valgrind \ - $(HOST_OUT)/lib64/valgrind/memcheck-amd64-linux \ - $(HOST_OUT)/lib64/valgrind/memcheck-x86-linux \ - $(HOST_OUT)/lib64/valgrind/default.supp \ - $(HOST_OUT)/lib64/valgrind/vgpreload_core-amd64-linux.so \ - $(HOST_OUT)/lib64/valgrind/vgpreload_core-x86-linux.so \ - $(HOST_OUT)/lib64/valgrind/vgpreload_memcheck-amd64-linux.so \ - $(HOST_OUT)/lib64/valgrind/vgpreload_memcheck-x86-linux.so - # Define make rules for a host gtests. # $(1): gtest name - the name of the test we're building such as leb128_test. # $(2): path relative to $OUT to the test binary @@ -556,24 +535,10 @@ endif ART_TEST_HOST_GTEST_$(1)_RULES += $$(gtest_rule) -.PHONY: valgrind-$$(gtest_rule) -valgrind-$$(gtest_rule): $$(gtest_exe) $$(gtest_deps) $(ART_VALGRIND_DEPENDENCIES) - $(hide) $$(call ART_TEST_SKIP,$$@) && \ - VALGRIND_LIB=$(HOST_OUT)/lib64/valgrind \ - $(HOST_OUT_EXECUTABLES)/valgrind --leak-check=full --error-exitcode=1 \ - --suppressions=art/test/valgrind-suppressions.txt --num-callers=50 \ - $$< && \ - $$(call ART_TEST_PASSED,$$@) || $$(call ART_TEST_FAILED,$$@) - - ART_TEST_HOST_VALGRIND_GTEST$$($(3)ART_PHONY_TEST_HOST_SUFFIX)_RULES += valgrind-$$(gtest_rule) - ART_TEST_HOST_VALGRIND_GTEST_RULES += valgrind-$$(gtest_rule) - ART_TEST_HOST_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule) - # Clear locally defined variables. - valgrind_gtest_rule := - gtest_rule := - gtest_exe := gtest_deps := + gtest_exe := + gtest_rule := endef # define-art-gtest-rule-host # Define the rules to build and run host and target gtests. @@ -602,7 +567,6 @@ define define-art-gtest-target 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))) @@ -622,7 +586,6 @@ define define-art-gtest-host 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))) @@ -641,13 +604,8 @@ define define-art-gtest-target-both 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 @@ -660,19 +618,14 @@ define define-art-gtest-host-both 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 ifeq ($(ART_BUILD_TARGET),true) $(foreach file,$(ART_TARGET_GTEST_FILES), $(eval $(call define-art-gtest-target,$(file),))) - ifdef TARGET_2ND_ARCH + ifdef 2ND_ART_PHONY_TEST_TARGET_SUFFIX $(foreach file,$(2ND_ART_TARGET_GTEST_FILES), $(eval $(call define-art-gtest-target,$(file),2ND_))) endif # Rules to run the different architecture versions of the gtest. @@ -689,15 +642,14 @@ endif # Used outside the art project to get a list of the current tests RUNTIME_TARGET_GTEST_MAKE_TARGETS := -$(foreach file, $(ART_TARGET_GTEST_FILES), $(eval RUNTIME_TARGET_GTEST_MAKE_TARGETS += $$(notdir $$(basename $$(file))))) +$(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) @@ -712,15 +664,11 @@ define define-test-art-gtest-combination 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 +$$(rule_name): $$(dependencies) d8 d8-compat-dx $(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) # Clear locally defined variables. @@ -728,21 +676,15 @@ $$(rule_name): $$(dependencies) dx d8-compat-dx desugar 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))) -ifdef TARGET_2ND_ARCH -$(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,)) +$(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))) 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. @@ -759,15 +701,9 @@ COMPILER_GTEST_HOST_SRC_FILES := 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 := @@ -782,6 +718,7 @@ ART_GTEST_jni_internal_test_DEX_DEPS := ART_GTEST_oat_file_assistant_test_DEX_DEPS := ART_GTEST_oat_file_assistant_test_HOST_DEPS := ART_GTEST_oat_file_assistant_test_TARGET_DEPS := +ART_GTEST_dexanalyze_test_DEX_DEPS := ART_GTEST_dexoptanalyzer_test_DEX_DEPS := ART_GTEST_dexoptanalyzer_test_HOST_DEPS := ART_GTEST_dexoptanalyzer_test_TARGET_DEPS := @@ -805,8 +742,6 @@ ART_GTEST_transaction_test_DEX_DEPS := 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 517ac5c28da12cc008aadf1ba129f41a74e097e7..08b1e10268338364b6410a4532444b34f92abc87 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -37,11 +37,9 @@ else 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 @@ define create-core-oat-host-rules 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 @@ define create-core-oat-host-rules 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): PRIVATE_CORE_MULTI_PARAM := $$(core_multi_param) $$(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) ANDROID_LOG_TAGS="*:e" $$(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_oat_name): $$(core_image_name) 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 @@ define create-core-oat-target-rules 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_oat_name): $$(core_image_name) core_infix := endef # create-core-oat-target-rules -# $(1): compiler - optimizing, interpreter or interpreter-access-checks. -# $(2): wrapper. -# $(3): dex2oat suffix. +# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks). define create-core-oat-target-rule-combination - $(call create-core-oat-target-rules,$(1),,$(2),$(3)) + $(call create-core-oat-target-rules,$(1),) ifdef TARGET_2ND_ARCH - $(call create-core-oat-target-rules,$(1),2ND_,$(2),$(3)) + $(call create-core-oat-target-rules,$(1),2ND_) endif endef -$(eval $(call create-core-oat-target-rule-combination,optimizing,,)) -$(eval $(call create-core-oat-target-rule-combination,interpreter,,)) -$(eval $(call create-core-oat-target-rule-combination,interp-ac,,)) - -valgrindTARGET_CORE_IMG_OUTS := -valgrindTARGET_CORE_OAT_OUTS := -$(eval $(call create-core-oat-target-rule-combination,optimizing,valgrind,32)) -$(eval $(call create-core-oat-target-rule-combination,interpreter,valgrind,32)) -$(eval $(call create-core-oat-target-rule-combination,interp-ac,valgrind,32)) - -valgrind-test-art-host-dex2oat-target: $(valgrindTARGET_CORE_IMG_OUTS) - -valgrind-test-art-host-dex2oat: valgrind-test-art-host-dex2oat-host valgrind-test-art-host-dex2oat-target +$(eval $(call create-core-oat-target-rule-combination,optimizing)) +$(eval $(call create-core-oat-target-rule-combination,interpreter)) +$(eval $(call create-core-oat-target-rule-combination,interp-ac)) # Define a default core image that can be used for things like gtests that # need some image to run, but don't otherwise care which image is used. diff --git a/build/art.go b/build/art.go index 59480a0d0f5beee59c3a642bcdc863a1f05b101e..3dabce39759c69b7a54272f93be7668b7eaf7512 100644 --- a/build/art.go +++ b/build/art.go @@ -278,6 +278,7 @@ func init() { android.RegisterModuleType("art_cc_test", artTest) android.RegisterModuleType("art_cc_test_library", artTestLibrary) android.RegisterModuleType("art_cc_defaults", artDefaultsFactory) + android.RegisterModuleType("libart_cc_defaults", libartDefaultsFactory) android.RegisterModuleType("art_global_defaults", artGlobalDefaultsFactory) android.RegisterModuleType("art_debug_defaults", artDebugDefaultsFactory) } @@ -304,6 +305,33 @@ func artDefaultsFactory() android.Module { return module } +func libartDefaultsFactory() android.Module { + c := &codegenProperties{} + module := cc.DefaultsFactory(c) + android.AddLoadHook(module, func(ctx android.LoadHookContext) { + codegen(ctx, c, true) + + type props struct { + Target struct { + Android struct { + Shared_libs []string + } + } + } + + p := &props{} + // TODO: express this in .bp instead b/79671158 + if !envTrue(ctx, "ART_TARGET_LINUX") { + p.Target.Android.Shared_libs = []string { + "libmetricslogger", + } + } + ctx.AppendProperties(p) + }) + + return module +} + func artLibrary() android.Module { m, _ := cc.NewLibrary(android.HostAndDeviceSupported) module := m.Init() diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index 235a2aa90e8dc525db2a131835e10fb10c1b6da0..a52e16328acbf898035e74fb5f4df7791d80aa22 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -371,7 +371,7 @@ TEST_F(CmdlineParserTest, DISABLED_TestXGcOption) { */ TEST_F(CmdlineParserTest, TestJdwpProviderEmpty) { { - EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kNone, "", M::JdwpProvider); + EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kUnset, "", M::JdwpProvider); } } // TEST_F diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index cb3140a091d9acb5511d0ade8253e202b51068a5..48da7551a76f1a647bd9eaada904f3a4e2029d80 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -416,8 +416,6 @@ static gc::CollectorType ParseCollectorType(const std::string& option) { return gc::kCollectorTypeGSS; } else if (option == "CC") { return gc::kCollectorTypeCC; - } else if (option == "MC") { - return gc::kCollectorTypeMC; } else { return gc::kCollectorTypeNone; } diff --git a/cmdline/unit.h b/cmdline/unit.h index ad6a03d12f92697c5673b468f83788bb3cd7bbdf..f73981fbd32e40e7bbb5653dfbe367b0fa586648 100644 --- a/cmdline/unit.h +++ b/cmdline/unit.h @@ -21,8 +21,9 @@ namespace art { // Used for arguments that simply indicate presence (e.g. "-help") without any values. struct Unit { - // Avoid 'Conditional jump or move depends on uninitialised value(s)' errors - // when running valgrind by specifying a user-defined constructor. + // Historical note: We specified a user-defined constructor to avoid + // 'Conditional jump or move depends on uninitialised value(s)' errors + // when running Valgrind. Unit() {} Unit(const Unit&) = default; ~Unit() {} diff --git a/compiler/Android.bp b/compiler/Android.bp index 32e42bc02af8f99795ffc21d9138d3110a3fe8bb..eff4955d44f90dc6e6c9e14a7f52a9352dae155f 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -39,7 +39,6 @@ art_cc_defaults { "linker/file_output_stream.cc", "linker/output_stream.cc", "linker/vector_output_stream.cc", - "linker/relative_patcher.cc", "jit/jit_compiler.cc", "jit/jit_logger.cc", "jni/quick/calling_convention.cc", @@ -64,12 +63,14 @@ art_cc_defaults { "optimizing/inliner.cc", "optimizing/instruction_builder.cc", "optimizing/instruction_simplifier.cc", + "optimizing/intrinsic_objects.cc", "optimizing/intrinsics.cc", "optimizing/licm.cc", "optimizing/linear_order.cc", "optimizing/load_store_analysis.cc", "optimizing/load_store_elimination.cc", "optimizing/locations.cc", + "optimizing/loop_analysis.cc", "optimizing/loop_optimization.cc", "optimizing/nodes.cc", "optimizing/optimization.cc", @@ -101,8 +102,6 @@ art_cc_defaults { arm: { srcs: [ "jni/quick/arm/calling_convention_arm.cc", - "linker/arm/relative_patcher_arm_base.cc", - "linker/arm/relative_patcher_thumb2.cc", "optimizing/code_generator_arm_vixl.cc", "optimizing/code_generator_vector_arm_vixl.cc", "optimizing/instruction_simplifier_arm.cc", @@ -119,7 +118,6 @@ art_cc_defaults { arm64: { srcs: [ "jni/quick/arm64/calling_convention_arm64.cc", - "linker/arm64/relative_patcher_arm64.cc", "optimizing/code_generator_arm64.cc", "optimizing/code_generator_vector_arm64.cc", "optimizing/scheduler_arm64.cc", @@ -133,7 +131,6 @@ art_cc_defaults { mips: { srcs: [ "jni/quick/mips/calling_convention_mips.cc", - "linker/mips/relative_patcher_mips.cc", "optimizing/code_generator_mips.cc", "optimizing/code_generator_vector_mips.cc", "optimizing/instruction_simplifier_mips.cc", @@ -146,7 +143,6 @@ art_cc_defaults { mips64: { srcs: [ "jni/quick/mips64/calling_convention_mips64.cc", - "linker/mips64/relative_patcher_mips64.cc", "optimizing/code_generator_mips64.cc", "optimizing/code_generator_vector_mips64.cc", "optimizing/intrinsics_mips64.cc", @@ -157,8 +153,6 @@ art_cc_defaults { x86: { srcs: [ "jni/quick/x86/calling_convention_x86.cc", - "linker/x86/relative_patcher_x86.cc", - "linker/x86/relative_patcher_x86_base.cc", "optimizing/code_generator_x86.cc", "optimizing/code_generator_vector_x86.cc", "optimizing/intrinsics_x86.cc", @@ -172,7 +166,6 @@ art_cc_defaults { x86_64: { srcs: [ "jni/quick/x86_64/calling_convention_x86_64.cc", - "linker/x86_64/relative_patcher_x86_64.cc", "optimizing/intrinsics_x86_64.cc", "optimizing/code_generator_x86_64.cc", "optimizing/code_generator_vector_x86_64.cc", @@ -252,7 +245,9 @@ art_cc_library { }, shared_libs: [ "libart", + "libprofile", "libdexfile", + "libartbase", ], target: { @@ -300,7 +295,9 @@ art_cc_library { }, shared_libs: [ "libartd", + "libprofiled", "libdexfiled", + "libartbased", ], } @@ -349,6 +346,7 @@ art_cc_test { "optimizing/parallel_move_test.cc", "optimizing/pretty_printer_test.cc", "optimizing/reference_type_propagation_test.cc", + "optimizing/select_generator_test.cc", "optimizing/side_effects_test.cc", "optimizing/ssa_liveness_analysis_test.cc", "optimizing/ssa_test.cc", @@ -370,31 +368,25 @@ art_cc_test { codegen: { arm: { srcs: [ - "linker/arm/relative_patcher_thumb2_test.cc", "utils/arm/managed_register_arm_test.cc", ], }, arm64: { srcs: [ - "linker/arm64/relative_patcher_arm64_test.cc", "utils/arm64/managed_register_arm64_test.cc", ], }, mips: { srcs: [ - "linker/mips/relative_patcher_mips_test.cc", - "linker/mips/relative_patcher_mips32r6_test.cc", ], }, mips64: { srcs: [ - "linker/mips64/relative_patcher_mips64_test.cc", "utils/mips64/managed_register_mips64_test.cc", ], }, x86: { srcs: [ - "linker/x86/relative_patcher_x86_test.cc", "utils/x86/managed_register_x86_test.cc", // These tests are testing architecture-independent @@ -410,7 +402,8 @@ art_cc_test { }, x86_64: { srcs: [ - "linker/x86_64/relative_patcher_x86_64_test.cc", + // Is this test a bit-rotten copy of the x86 test? b/77951326 + // "utils/x86_64/managed_register_x86_64_test.cc", ], }, }, @@ -421,6 +414,7 @@ art_cc_test { ], shared_libs: [ + "libprofiled", "libartd-compiler", "libartd-simulator-container", "libvixld-arm", diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h index 29ff235cea70a8a3260c350e4406b386a01f7d23..581edaa773c998e33b1e111584c8aaa9c87b3800 100644 --- a/compiler/cfi_test.h +++ b/compiler/cfi_test.h @@ -37,8 +37,8 @@ constexpr dwarf::CFIFormat kCFIFormat = dwarf::DW_DEBUG_FRAME_FORMAT; class CFITest : public dwarf::DwarfTest { public: void GenerateExpected(FILE* f, InstructionSet isa, const char* isa_str, - const std::vector& actual_asm, - const std::vector& actual_cfi) { + ArrayRef actual_asm, + ArrayRef actual_cfi) { std::vector lines; // Print the raw bytes. fprintf(f, "static constexpr uint8_t expected_asm_%s[] = {", isa_str); @@ -50,11 +50,18 @@ class CFITest : public dwarf::DwarfTest { // Pretty-print CFI opcodes. constexpr bool is64bit = false; dwarf::DebugFrameOpCodeWriter<> initial_opcodes; - dwarf::WriteCIE(is64bit, dwarf::Reg(8), - initial_opcodes, kCFIFormat, &debug_frame_data_); + dwarf::WriteCIE(is64bit, dwarf::Reg(8), initial_opcodes, kCFIFormat, &debug_frame_data_); std::vector debug_frame_patches; - dwarf::WriteFDE(is64bit, 0, 0, 0, actual_asm.size(), ArrayRef(actual_cfi), - kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches); + dwarf::WriteFDE(is64bit, + /* section_address */ 0, + /* cie_address */ 0, + /* code_address */ 0, + actual_asm.size(), + actual_cfi, + kCFIFormat, + /* buffer_address */ 0, + &debug_frame_data_, + &debug_frame_patches); ReformatCfi(Objdump(false, "-W"), &lines); // Pretty-print assembly. const uint8_t* asm_base = actual_asm.data(); @@ -142,7 +149,7 @@ class CFITest : public dwarf::DwarfTest { } // Pretty-print byte array. 12 bytes per line. - static void HexDump(FILE* f, const std::vector& data) { + static void HexDump(FILE* f, ArrayRef data) { for (size_t i = 0; i < data.size(); i++) { fprintf(f, i % 12 == 0 ? "\n " : " "); // Whitespace. fprintf(f, "0x%02X,", data[i]); diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index d3e3a51f7a5bf21d4eef98b144015d838e79e4e0..e8e1d408efd50c867ef23abd5c1fa889bdc60f2e 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -16,6 +16,8 @@ #include "common_compiler_test.h" +#include + #include "arch/instruction_set_features.h" #include "art_field-inl.h" #include "art_method-inl.h" @@ -29,6 +31,7 @@ #include "dex/verification_results.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" +#include "jni/java_vm_ext.h" #include "interpreter/interpreter.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" @@ -37,6 +40,7 @@ #include "oat_quick_method_header.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" +#include "utils/atomic_dex_ref_map-inl.h" namespace art { @@ -79,6 +83,7 @@ void CommonCompilerTest::MakeExecutable(ArtMethod* method) { const size_t size = method_info.size() + vmap_table.size() + sizeof(method_header) + code_size; chunk->reserve(size + max_padding); chunk->resize(sizeof(method_header)); + static_assert(std::is_trivially_copyable::value, "Cannot use memcpy"); memcpy(&(*chunk)[0], &method_header, sizeof(method_header)); chunk->insert(chunk->begin(), vmap_table.begin(), vmap_table.end()); chunk->insert(chunk->begin(), method_info.begin(), method_info.end()); @@ -123,7 +128,7 @@ void CommonCompilerTest::MakeExecutable(ObjPtr class_loader Thread* self = Thread::Current(); StackHandleScope<1> hs(self); Handle loader(hs.NewHandle(class_loader)); - mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), loader); + ObjPtr klass = class_linker_->FindClass(self, class_descriptor.c_str(), loader); CHECK(klass != nullptr) << "Class not found " << class_name; PointerSize pointer_size = class_linker_->GetImagePointerSize(); for (auto& m : klass->GetMethods(pointer_size)) { @@ -131,25 +136,10 @@ void CommonCompilerTest::MakeExecutable(ObjPtr class_loader } } -// Get the set of image classes given to the compiler-driver in SetUp. Note: the compiler -// driver assumes ownership of the set, so the test should properly release the set. -std::unordered_set* CommonCompilerTest::GetImageClasses() { +// Get the set of image classes given to the compiler options in SetUp. +std::unique_ptr> CommonCompilerTest::GetImageClasses() { // Empty set: by default no classes are retained in the image. - return new std::unordered_set(); -} - -// Get the set of compiled classes given to the compiler-driver in SetUp. Note: the compiler -// driver assumes ownership of the set, so the test should properly release the set. -std::unordered_set* CommonCompilerTest::GetCompiledClasses() { - // Null, no selection of compiled-classes. - return nullptr; -} - -// Get the set of compiled methods given to the compiler-driver in SetUp. Note: the compiler -// driver assumes ownership of the set, so the test should properly release the set. -std::unordered_set* CommonCompilerTest::GetCompiledMethods() { - // Null, no selection of compiled-methods. - return nullptr; + return std::make_unique>(); } // Get ProfileCompilationInfo that should be passed to the driver. @@ -163,11 +153,7 @@ void CommonCompilerTest::SetUp() { { ScopedObjectAccess soa(Thread::Current()); - const InstructionSet instruction_set = kRuntimeISA; - // Take the default set of instruction features from the build. - instruction_set_features_ = InstructionSetFeatures::FromCppDefines(); - - runtime_->SetInstructionSet(instruction_set); + runtime_->SetInstructionSet(instruction_set_); for (uint32_t i = 0; i < static_cast(CalleeSaveType::kLastCalleeSaveType); ++i) { CalleeSaveType type = CalleeSaveType(i); if (!runtime_->HasCalleeSaveMethod(type)) { @@ -175,28 +161,51 @@ void CommonCompilerTest::SetUp() { } } - CreateCompilerDriver(compiler_kind_, instruction_set); + CreateCompilerDriver(); + } +} + +void CommonCompilerTest::ApplyInstructionSet() { + // Copy local instruction_set_ and instruction_set_features_ to *compiler_options_; + CHECK(instruction_set_features_ != nullptr); + if (instruction_set_ == InstructionSet::kThumb2) { + CHECK_EQ(InstructionSet::kArm, instruction_set_features_->GetInstructionSet()); + } else { + CHECK_EQ(instruction_set_, instruction_set_features_->GetInstructionSet()); + } + compiler_options_->instruction_set_ = instruction_set_; + compiler_options_->instruction_set_features_ = + InstructionSetFeatures::FromBitmap(instruction_set_, instruction_set_features_->AsBitmap()); + CHECK(compiler_options_->instruction_set_features_->Equals(instruction_set_features_.get())); +} + +void CommonCompilerTest::OverrideInstructionSetFeatures(InstructionSet instruction_set, + const std::string& variant) { + instruction_set_ = instruction_set; + std::string error_msg; + instruction_set_features_ = + InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg); + CHECK(instruction_set_features_ != nullptr) << error_msg; + + if (compiler_options_ != nullptr) { + ApplyInstructionSet(); } } -void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, - InstructionSet isa, - size_t number_of_threads) { +void CommonCompilerTest::CreateCompilerDriver() { + ApplyInstructionSet(); + compiler_options_->boot_image_ = true; + compiler_options_->compile_pic_ = false; // Non-PIC boot image is a test configuration. compiler_options_->SetCompilerFilter(GetCompilerFilter()); + compiler_options_->image_classes_.swap(*GetImageClasses()); compiler_driver_.reset(new CompilerDriver(compiler_options_.get(), verification_results_.get(), - kind, - isa, - instruction_set_features_.get(), - GetImageClasses(), - GetCompiledClasses(), - GetCompiledMethods(), - number_of_threads, + compiler_kind_, + &compiler_options_->image_classes_, + number_of_threads_, /* swap_fd */ -1, GetProfileCompilationInfo())); - // We typically don't generate an image in unit tests, disable this optimization by default. - compiler_driver_->SetSupportBootImageFixup(false); } void CommonCompilerTest::SetUpRuntimeOptions(RuntimeOptions* options) { @@ -218,11 +227,6 @@ void CommonCompilerTest::SetCompilerKind(Compiler::Kind compiler_kind) { compiler_kind_ = compiler_kind; } -InstructionSet CommonCompilerTest::GetInstructionSet() const { - DCHECK(compiler_driver_.get() != nullptr); - return compiler_driver_->GetInstructionSet(); -} - void CommonCompilerTest::TearDown() { compiler_driver_.reset(); callbacks_.reset(); @@ -238,7 +242,7 @@ void CommonCompilerTest::CompileClass(mirror::ClassLoader* class_loader, const c Thread* self = Thread::Current(); StackHandleScope<1> hs(self); Handle loader(hs.NewHandle(class_loader)); - mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), loader); + ObjPtr klass = class_linker_->FindClass(self, class_descriptor.c_str(), loader); CHECK(klass != nullptr) << "Class not found " << class_name; auto pointer_size = class_linker_->GetImagePointerSize(); for (auto& m : klass->GetMethods(pointer_size)) { @@ -248,9 +252,49 @@ void CommonCompilerTest::CompileClass(mirror::ClassLoader* class_loader, const c void CommonCompilerTest::CompileMethod(ArtMethod* method) { CHECK(method != nullptr); - TimingLogger timings("CommonTest::CompileMethod", false, false); + TimingLogger timings("CommonCompilerTest::CompileMethod", false, false); TimingLogger::ScopedTiming t(__FUNCTION__, &timings); - compiler_driver_->CompileOne(Thread::Current(), method, &timings); + { + Thread* self = Thread::Current(); + jobject class_loader = self->GetJniEnv()->GetVm()->AddGlobalRef(self, method->GetClassLoader()); + + DCHECK(!Runtime::Current()->IsStarted()); + const DexFile* dex_file = method->GetDexFile(); + uint16_t class_def_idx = method->GetClassDefIndex(); + uint32_t method_idx = method->GetDexMethodIndex(); + uint32_t access_flags = method->GetAccessFlags(); + InvokeType invoke_type = method->GetInvokeType(); + StackHandleScope<2> hs(self); + Handle dex_cache(hs.NewHandle(method->GetDexCache())); + Handle h_class_loader = hs.NewHandle( + self->DecodeJObject(class_loader)->AsClassLoader()); + const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset()); + + std::vector dex_files; + dex_files.push_back(dex_file); + + // Go to native so that we don't block GC during compilation. + ScopedThreadSuspension sts(self, kNative); + + compiler_driver_->InitializeThreadPools(); + + compiler_driver_->PreCompile(class_loader, dex_files, &timings); + + compiler_driver_->CompileOne(self, + class_loader, + *dex_file, + class_def_idx, + method_idx, + access_flags, + invoke_type, + code_item, + dex_cache, + h_class_loader); + + compiler_driver_->FreeThreadPools(); + + self->GetJniEnv()->DeleteGlobalRef(class_loader); + } TimingLogger::ScopedTiming t2("MakeExecutable", &timings); MakeExecutable(method); } @@ -260,7 +304,8 @@ void CommonCompilerTest::CompileDirectMethod(Handle class_l const char* signature) { std::string class_descriptor(DotToDescriptor(class_name)); Thread* self = Thread::Current(); - mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), class_loader); + ObjPtr klass = + class_linker_->FindClass(self, class_descriptor.c_str(), class_loader); CHECK(klass != nullptr) << "Class not found " << class_name; auto pointer_size = class_linker_->GetImagePointerSize(); ArtMethod* method = klass->FindClassMethod(method_name, signature, pointer_size); @@ -274,7 +319,8 @@ void CommonCompilerTest::CompileVirtualMethod(Handle class_ const char* signature) { std::string class_descriptor(DotToDescriptor(class_name)); Thread* self = Thread::Current(); - mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), class_loader); + ObjPtr klass = + class_linker_->FindClass(self, class_descriptor.c_str(), class_loader); CHECK(klass != nullptr) << "Class not found " << class_name; auto pointer_size = class_linker_->GetImagePointerSize(); ArtMethod* method = klass->FindClassMethod(method_name, signature, pointer_size); @@ -302,4 +348,14 @@ void CommonCompilerTest::UnreserveImageSpace() { image_reservation_.reset(); } +void CommonCompilerTest::SetDexFilesForOatFile(const std::vector& dex_files) { + compiler_options_->dex_files_for_oat_file_ = dex_files; + compiler_driver_->compiled_classes_.AddDexFiles(dex_files); + compiler_driver_->dex_to_dex_compiler_.SetDexFiles(dex_files); +} + +void CommonCompilerTest::ClearBootImageOption() { + compiler_options_->boot_image_ = false; +} + } // namespace art diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h index 8af29d44f0c9cbf678e993c516661aaf85c946a5..db38110400b18d2ed3cbd6386a0af0eb52e4b177 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -18,9 +18,11 @@ #define ART_COMPILER_COMMON_COMPILER_TEST_H_ #include -#include #include +#include "arch/instruction_set.h" +#include "arch/instruction_set_features.h" +#include "base/hash_set.h" #include "common_runtime_test.h" #include "compiler.h" #include "oat_file.h" @@ -33,6 +35,7 @@ class ClassLoader; class CompilerDriver; class CompilerOptions; class CumulativeLogger; +class DexFile; class ProfileCompilationInfo; class VerificationResults; @@ -54,26 +57,15 @@ class CommonCompilerTest : public CommonRuntimeTest { REQUIRES_SHARED(Locks::mutator_lock_); protected: - virtual void SetUp(); + void SetUp() OVERRIDE; - virtual void SetUpRuntimeOptions(RuntimeOptions* options); + void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE; Compiler::Kind GetCompilerKind() const; void SetCompilerKind(Compiler::Kind compiler_kind); - InstructionSet GetInstructionSet() const; - - // Get the set of image classes given to the compiler-driver in SetUp. Note: the compiler - // driver assumes ownership of the set, so the test should properly release the set. - virtual std::unordered_set* GetImageClasses(); - - // Get the set of compiled classes given to the compiler-driver in SetUp. Note: the compiler - // driver assumes ownership of the set, so the test should properly release the set. - virtual std::unordered_set* GetCompiledClasses(); - - // Get the set of compiled methods given to the compiler-driver in SetUp. Note: the compiler - // driver assumes ownership of the set, so the test should properly release the set. - virtual std::unordered_set* GetCompiledMethods(); + // Get the set of image classes given to the compiler-driver in SetUp. + virtual std::unique_ptr> GetImageClasses(); virtual ProfileCompilationInfo* GetProfileCompilationInfo(); @@ -81,7 +73,7 @@ class CommonCompilerTest : public CommonRuntimeTest { return CompilerFilter::kDefaultCompilerFilter; } - virtual void TearDown(); + void TearDown() OVERRIDE; void CompileClass(mirror::ClassLoader* class_loader, const char* class_name) REQUIRES_SHARED(Locks::mutator_lock_); @@ -96,18 +88,31 @@ class CommonCompilerTest : public CommonRuntimeTest { const char* method_name, const char* signature) REQUIRES_SHARED(Locks::mutator_lock_); - void CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa, size_t number_of_threads = 2U); + void ApplyInstructionSet(); + void OverrideInstructionSetFeatures(InstructionSet instruction_set, const std::string& variant); + + void CreateCompilerDriver(); void ReserveImageSpace(); void UnreserveImageSpace(); + void SetDexFilesForOatFile(const std::vector& dex_files); + + void ClearBootImageOption(); + Compiler::Kind compiler_kind_ = Compiler::kOptimizing; + size_t number_of_threads_ = 2u; + + InstructionSet instruction_set_ = + (kRuntimeISA == InstructionSet::kArm) ? InstructionSet::kThumb2 : kRuntimeISA; + // Take the default set of instruction features from the build. + std::unique_ptr instruction_set_features_ + = InstructionSetFeatures::FromCppDefines(); + std::unique_ptr compiler_options_; std::unique_ptr verification_results_; std::unique_ptr compiler_driver_; - std::unique_ptr instruction_set_features_; - private: std::unique_ptr image_reservation_; diff --git a/compiler/compiler.h b/compiler/compiler.h index f2ec3a9fa3b0d54287b477f3de762e0ccebd41ea..ef3d87f02bba665aab85ca97dd385843006b958d 100644 --- a/compiler/compiler.h +++ b/compiler/compiler.h @@ -39,12 +39,6 @@ template class Handle; class OatWriter; class Thread; -enum class CopyOption { - kNever, - kAlways, - kOnlyIfCompressed -}; - class Compiler { public: enum Kind { diff --git a/compiler/debug/dwarf/dwarf_test.h b/compiler/debug/dwarf/dwarf_test.h index 9a7c604ca11bcdd2bb933ad1ef1cf434f79415c5..6b039a7b5f5e3ed95938107261c5ae9c7157e812 100644 --- a/compiler/debug/dwarf/dwarf_test.h +++ b/compiler/debug/dwarf/dwarf_test.h @@ -28,7 +28,7 @@ #include "base/os.h" #include "base/unix_file/fd_file.h" -#include "common_runtime_test.h" +#include "common_compiler_test.h" #include "gtest/gtest.h" #include "linker/elf_builder.h" #include "linker/file_output_stream.h" @@ -39,7 +39,7 @@ namespace dwarf { #define DW_CHECK(substring) Check(substring, false, __FILE__, __LINE__) #define DW_CHECK_NEXT(substring) Check(substring, true, __FILE__, __LINE__) -class DwarfTest : public CommonRuntimeTest { +class DwarfTest : public CommonCompilerTest { public: static constexpr bool kPrintObjdumpOutput = false; // debugging. diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index 893cad288b3dbeb9c3fae11b5e573605fa6aa7e9..bda7108c740d5422fa7d6d72c269746d6b892d2e 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -204,14 +204,12 @@ class ElfCompilationUnitWriter { // Decode dex register locations for all stack maps. // It might be expensive, so do it just once and reuse the result. + std::unique_ptr code_info; std::vector dex_reg_maps; if (accessor.HasCodeItem() && mi->code_info != nullptr) { - const CodeInfo code_info(mi->code_info); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - for (size_t s = 0; s < code_info.GetNumberOfStackMaps(encoding); ++s) { - const StackMap& stack_map = code_info.GetStackMapAt(s, encoding); - dex_reg_maps.push_back(code_info.GetDexRegisterMapOf( - stack_map, encoding, accessor.RegistersSize())); + code_info.reset(new CodeInfo(mi->code_info)); + for (StackMap stack_map : code_info->GetStackMaps()) { + dex_reg_maps.push_back(code_info->GetDexRegisterMapOf(stack_map)); } } diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h index 44504c1efb9fc16332c1e3c7f9a98bc7f824dba3..3d78943cd04475ae37edfac51e1f439d5c7df88f 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -100,15 +100,12 @@ class ElfDebugLineWriter { if (mi->code_info != nullptr) { // Use stack maps to create mapping table from pc to dex. const CodeInfo code_info(mi->code_info); - const CodeInfoEncoding encoding = code_info.ExtractEncoding(); - pc2dex_map.reserve(code_info.GetNumberOfStackMaps(encoding)); - for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(encoding); s++) { - StackMap stack_map = code_info.GetStackMapAt(s, encoding); - DCHECK(stack_map.IsValid()); - const uint32_t pc = stack_map.GetNativePcOffset(encoding.stack_map.encoding, isa); - const int32_t dex = stack_map.GetDexPc(encoding.stack_map.encoding); + pc2dex_map.reserve(code_info.GetNumberOfStackMaps()); + for (StackMap stack_map : code_info.GetStackMaps()) { + const uint32_t pc = stack_map.GetNativePcOffset(isa); + const int32_t dex = stack_map.GetDexPc(); pc2dex_map.push_back({pc, dex}); - if (stack_map.HasDexRegisterMap(encoding.stack_map.encoding)) { + if (stack_map.HasDexRegisterMap()) { // Guess that the first map with local variables is the end of prologue. prologue_end = std::min(prologue_end, pc); } diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h index 9ea9f01cd96f8c03041bc53e5482b22be4c11df8..b663291b4da5bc6f5092ece73504736ca7490ab4 100644 --- a/compiler/debug/elf_debug_loc_writer.h +++ b/compiler/debug/elf_debug_loc_writer.h @@ -99,12 +99,11 @@ static std::vector GetVariableLocations( // Get stack maps sorted by pc (they might not be sorted internally). // TODO(dsrbecky) Remove this once stackmaps get sorted by pc. const CodeInfo code_info(method_info->code_info); - const CodeInfoEncoding encoding = code_info.ExtractEncoding(); std::map stack_maps; // low_pc -> stack_map_index. - for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(encoding); s++) { - StackMap stack_map = code_info.GetStackMapAt(s, encoding); + for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) { + StackMap stack_map = code_info.GetStackMapAt(s); DCHECK(stack_map.IsValid()); - if (!stack_map.HasDexRegisterMap(encoding.stack_map.encoding)) { + if (!stack_map.HasDexRegisterMap()) { // The compiler creates stackmaps without register maps at the start of // basic blocks in order to keep instruction-accurate line number mapping. // However, we never stop at those (breakpoint locations always have map). @@ -112,7 +111,7 @@ static std::vector GetVariableLocations( // The main reason for this is to save space by avoiding undefined gaps. continue; } - const uint32_t pc_offset = stack_map.GetNativePcOffset(encoding.stack_map.encoding, isa); + const uint32_t pc_offset = stack_map.GetNativePcOffset(isa); DCHECK_LE(pc_offset, method_info->code_size); DCHECK_LE(compilation_unit_code_address, method_info->code_address); const uint32_t low_pc = dchecked_integral_cast( @@ -124,7 +123,7 @@ static std::vector GetVariableLocations( for (auto it = stack_maps.begin(); it != stack_maps.end(); it++) { const uint32_t low_pc = it->first; const uint32_t stack_map_index = it->second; - const StackMap& stack_map = code_info.GetStackMapAt(stack_map_index, encoding); + const StackMap stack_map = code_info.GetStackMapAt(stack_map_index); auto next_it = it; next_it++; const uint32_t high_pc = next_it != stack_maps.end() @@ -136,7 +135,7 @@ static std::vector GetVariableLocations( } // Check that the stack map is in the requested range. - uint32_t dex_pc = stack_map.GetDexPc(encoding.stack_map.encoding); + uint32_t dex_pc = stack_map.GetDexPc(); if (!(dex_pc_low <= dex_pc && dex_pc < dex_pc_high)) { // The variable is not in scope at this PC. Therefore omit the entry. // Note that this is different to None() entry which means in scope, but unknown location. @@ -148,13 +147,11 @@ static std::vector GetVariableLocations( DexRegisterLocation reg_hi = DexRegisterLocation::None(); DCHECK_LT(stack_map_index, dex_register_maps.size()); DexRegisterMap dex_register_map = dex_register_maps[stack_map_index]; - DCHECK(dex_register_map.IsValid()); + DCHECK(!dex_register_map.empty()); CodeItemDataAccessor accessor(*method_info->dex_file, method_info->code_item); - reg_lo = dex_register_map.GetDexRegisterLocation( - vreg, accessor.RegistersSize(), code_info, encoding); + reg_lo = dex_register_map[vreg]; if (is64bitValue) { - reg_hi = dex_register_map.GetDexRegisterLocation( - vreg + 1, accessor.RegistersSize(), code_info, encoding); + reg_hi = dex_register_map[vreg + 1]; } // Add location entry for this address range. diff --git a/compiler/debug/elf_gnu_debugdata_writer.h b/compiler/debug/elf_gnu_debugdata_writer.h index a88c5cb21302fdd98752035c1378519e8d744d4a..fd132f4ac4942a9c38d31f7c0735b8c81b2e410f 100644 --- a/compiler/debug/elf_gnu_debugdata_writer.h +++ b/compiler/debug/elf_gnu_debugdata_writer.h @@ -41,23 +41,23 @@ static void XzCompress(const std::vector* src, std::vector* ds Lzma2EncProps_Normalize(&lzma2Props); CXzProps props; XzProps_Init(&props); - props.lzma2Props = &lzma2Props; + props.lzma2Props = lzma2Props; // Implement the required interface for communication (written in C so no virtual methods). struct XzCallbacks : public ISeqInStream, public ISeqOutStream, public ICompressProgress { - static SRes ReadImpl(void* p, void* buf, size_t* size) { - auto* ctx = static_cast(reinterpret_cast(p)); + static SRes ReadImpl(const ISeqInStream* p, void* buf, size_t* size) { + auto* ctx = static_cast(const_cast(p)); *size = std::min(*size, ctx->src_->size() - ctx->src_pos_); memcpy(buf, ctx->src_->data() + ctx->src_pos_, *size); ctx->src_pos_ += *size; return SZ_OK; } - static size_t WriteImpl(void* p, const void* buf, size_t size) { - auto* ctx = static_cast(reinterpret_cast(p)); + static size_t WriteImpl(const ISeqOutStream* p, const void* buf, size_t size) { + auto* ctx = static_cast(p); const uint8_t* buffer = reinterpret_cast(buf); ctx->dst_->insert(ctx->dst_->end(), buffer, buffer + size); return size; } - static SRes ProgressImpl(void* , UInt64, UInt64) { + static SRes ProgressImpl(const ICompressProgress* , UInt64, UInt64) { return SZ_OK; } size_t src_pos_; @@ -113,4 +113,3 @@ static std::vector MakeMiniDebugInfoInternal( } // namespace art #endif // ART_COMPILER_DEBUG_ELF_GNU_DEBUGDATA_WRITER_H_ - diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index be8641fd869dbb90f5e35697f2d642557bd11bcf..fcaa0cdd07738a349bb5ffe78495fe164f809908 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -26,10 +26,12 @@ #include "base/mutex.h" #include "compiled_method.h" #include "dex/bytecode_utils.h" +#include "dex/class_accessor-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_instruction-inl.h" #include "dex_to_dex_decompiler.h" #include "driver/compiler_driver.h" +#include "driver/compiler_options.h" #include "driver/dex_compilation_unit.h" #include "mirror/dex_cache.h" #include "quicken_info.h" @@ -608,7 +610,7 @@ CompiledMethod* DexToDexCompiler::CompileMethod( } // Create a `CompiledMethod`, with the quickened information in the vmap table. - InstructionSet instruction_set = driver_->GetInstructionSet(); + InstructionSet instruction_set = driver_->GetCompilerOptions().GetInstructionSet(); if (instruction_set == InstructionSet::kThumb2) { // Don't use the thumb2 instruction set to avoid the one off code delta. instruction_set = InstructionSet::kArm; @@ -633,16 +635,9 @@ void DexToDexCompiler::SetDexFiles(const std::vector& dex_files) // item. std::unordered_set seen_code_items; for (const DexFile* dex_file : dex_files) { - for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - continue; - } - ClassDataItemIterator it(*dex_file, class_data); - it.SkipAllFields(); - for (; it.HasNextMethod(); it.Next()) { - const DexFile::CodeItem* code_item = it.GetMethodCodeItem(); + for (ClassAccessor accessor : dex_file->GetClasses()) { + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + const DexFile::CodeItem* code_item = method.GetCodeItem(); // Detect the shared code items. if (!seen_code_items.insert(code_item).second) { shared_code_items_.insert(code_item); diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc index 19b190093f7551f6f10b08bb7aa5d06dbae2328d..4f83d605a3060b5dd901074297d8a89efc7f4c86 100644 --- a/compiler/dex/dex_to_dex_decompiler_test.cc +++ b/compiler/dex/dex_to_dex_decompiler_test.cc @@ -16,15 +16,18 @@ #include "dex_to_dex_decompiler.h" +#include "base/casts.h" #include "class_linker.h" #include "common_compiler_test.h" #include "compiled_method-inl.h" #include "compiler_callbacks.h" +#include "dex/class_accessor-inl.h" #include "dex/dex_file.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "handle_scope-inl.h" #include "mirror/class_loader.h" +#include "quick_compiler_callbacks.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "thread.h" @@ -42,9 +45,9 @@ class DexToDexDecompilerTest : public CommonCompilerTest { compiler_options_->SetCompilerFilter(CompilerFilter::kQuicken); // Create the main VerifierDeps, here instead of in the compiler since we want to aggregate // the results for all the dex files, not just the results for the current dex file. - Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps( + down_cast(Runtime::Current()->GetCompilerCallbacks())->SetVerifierDeps( new verifier::VerifierDeps(GetDexFiles(class_loader))); - compiler_driver_->SetDexFilesForOatFile(GetDexFiles(class_loader)); + SetDexFilesForOatFile(GetDexFiles(class_loader)); compiler_driver_->CompileAll(class_loader, GetDexFiles(class_loader), &timings); } @@ -66,7 +69,7 @@ class DexToDexDecompilerTest : public CommonCompilerTest { class_loader = LoadDex(dex_name); updated_dex_file = GetDexFiles(class_loader)[0]; Runtime::Current()->GetClassLinker()->RegisterDexFile( - *updated_dex_file, soa.Decode(class_loader).Ptr()); + *updated_dex_file, soa.Decode(class_loader)); } // The dex files should be identical. int cmp = memcmp(original_dex_file->Begin(), @@ -81,31 +84,20 @@ class DexToDexDecompilerTest : public CommonCompilerTest { ASSERT_NE(0, cmp); // Unquicken the dex file. - for (uint32_t i = 0; i < updated_dex_file->NumClassDefs(); ++i) { - const DexFile::ClassDef& class_def = updated_dex_file->GetClassDef(i); - const uint8_t* class_data = updated_dex_file->GetClassData(class_def); - if (class_data == nullptr) { - continue; - } - ClassDataItemIterator it(*updated_dex_file, class_data); - it.SkipAllFields(); - + for (ClassAccessor accessor : updated_dex_file->GetClasses()) { // Unquicken each method. - while (it.HasNextMethod()) { - uint32_t method_idx = it.GetMemberIndex(); - CompiledMethod* compiled_method = - compiler_driver_->GetCompiledMethod(MethodReference(updated_dex_file, method_idx)); + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + CompiledMethod* compiled_method = compiler_driver_->GetCompiledMethod( + method.GetReference()); ArrayRef table; if (compiled_method != nullptr) { table = compiled_method->GetVmapTable(); } optimizer::ArtDecompileDEX(*updated_dex_file, - *it.GetMethodCodeItem(), + *accessor.GetCodeItem(method), table, /* decompile_return_instruction */ true); - it.Next(); } - DCHECK(!it.HasNext()); } // Make sure after unquickening we go back to the same contents as the original dex file. diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc index dc044c1210944f41dc8a9a74345b12b2982123fe..fe8b766d0fb9e7fed4610c5e6b802833a77bd7a8 100644 --- a/compiler/dex/inline_method_analyser.cc +++ b/compiler/dex/inline_method_analyser.cc @@ -724,7 +724,8 @@ bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(ArtMethod* method, 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/compiled_method_storage.cc b/compiler/driver/compiled_method_storage.cc index a26a985ff9b15a25db841f0df4033a4ac0856982..d56b135aca3cbf51cc5f8add65f5018937d7f3ab 100644 --- a/compiler/driver/compiled_method_storage.cc +++ b/compiler/driver/compiled_method_storage.cc @@ -21,6 +21,7 @@ #include +#include "base/data_hash.h" #include "base/utils.h" #include "compiled_method.h" #include "linker/linker_patch.h" @@ -80,65 +81,7 @@ class CompiledMethodStorage::DedupeHashFunc { public: size_t operator()(const ArrayRef& array) const { - const uint8_t* data = reinterpret_cast(array.data()); - // TODO: More reasonable assertion. - // static_assert(IsPowerOfTwo(sizeof(ContentType)), - // "ContentType is not power of two, don't know whether array layout is as assumed"); - uint32_t len = sizeof(ContentType) * array.size(); - if (kUseMurmur3Hash) { - static constexpr uint32_t c1 = 0xcc9e2d51; - static constexpr uint32_t c2 = 0x1b873593; - static constexpr uint32_t r1 = 15; - static constexpr uint32_t r2 = 13; - static constexpr uint32_t m = 5; - static constexpr uint32_t n = 0xe6546b64; - - uint32_t hash = 0; - - const int nblocks = len / 4; - typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t; - const unaligned_uint32_t *blocks = reinterpret_cast(data); - int i; - for (i = 0; i < nblocks; i++) { - uint32_t k = blocks[i]; - k *= c1; - k = (k << r1) | (k >> (32 - r1)); - k *= c2; - - hash ^= k; - hash = ((hash << r2) | (hash >> (32 - r2))) * m + n; - } - - const uint8_t *tail = reinterpret_cast(data + nblocks * 4); - uint32_t k1 = 0; - - switch (len & 3) { - case 3: - k1 ^= tail[2] << 16; - FALLTHROUGH_INTENDED; - case 2: - k1 ^= tail[1] << 8; - FALLTHROUGH_INTENDED; - case 1: - k1 ^= tail[0]; - - k1 *= c1; - k1 = (k1 << r1) | (k1 >> (32 - r1)); - k1 *= c2; - hash ^= k1; - } - - hash ^= len; - hash ^= (hash >> 16); - hash *= 0x85ebca6b; - hash ^= (hash >> 13); - hash *= 0xc2b2ae35; - hash ^= (hash >> 16); - - return hash; - } else { - return HashBytes(data, len); - } + return DataHash()(array); } }; @@ -161,6 +104,46 @@ class CompiledMethodStorage::LengthPrefixedArrayAlloc { SwapSpace* const swap_space_; }; +class CompiledMethodStorage::ThunkMapKey { + public: + ThunkMapKey(linker::LinkerPatch::Type type, uint32_t custom_value1, uint32_t custom_value2) + : type_(type), custom_value1_(custom_value1), custom_value2_(custom_value2) {} + + bool operator<(const ThunkMapKey& other) const { + if (custom_value1_ != other.custom_value1_) { + return custom_value1_ < other.custom_value1_; + } + if (custom_value2_ != other.custom_value2_) { + return custom_value2_ < other.custom_value2_; + } + return type_ < other.type_; + } + + private: + linker::LinkerPatch::Type type_; + uint32_t custom_value1_; + uint32_t custom_value2_; +}; + +class CompiledMethodStorage::ThunkMapValue { + public: + ThunkMapValue(std::vector>&& code, + const std::string& debug_name) + : code_(std::move(code)), debug_name_(debug_name) {} + + ArrayRef GetCode() const { + return ArrayRef(code_); + } + + const std::string& GetDebugName() const { + return debug_name_; + } + + private: + std::vector> code_; + std::string debug_name_; +}; + CompiledMethodStorage::CompiledMethodStorage(int swap_fd) : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)), dedupe_enabled_(true), @@ -171,7 +154,9 @@ CompiledMethodStorage::CompiledMethodStorage(int swap_fd) LengthPrefixedArrayAlloc(swap_space_.get())), dedupe_cfi_info_("dedupe cfi info", LengthPrefixedArrayAlloc(swap_space_.get())), dedupe_linker_patches_("dedupe cfi info", - LengthPrefixedArrayAlloc(swap_space_.get())) { + LengthPrefixedArrayAlloc(swap_space_.get())), + thunk_map_lock_("thunk_map_lock"), + thunk_map_(std::less(), SwapAllocator(swap_space_.get())) { } CompiledMethodStorage::~CompiledMethodStorage() { @@ -237,4 +222,55 @@ void CompiledMethodStorage::ReleaseLinkerPatches( ReleaseArrayIfNotDeduplicated(linker_patches); } +CompiledMethodStorage::ThunkMapKey CompiledMethodStorage::GetThunkMapKey( + const linker::LinkerPatch& linker_patch) { + uint32_t custom_value1 = 0u; + uint32_t custom_value2 = 0u; + switch (linker_patch.GetType()) { + case linker::LinkerPatch::Type::kBakerReadBarrierBranch: + custom_value1 = linker_patch.GetBakerCustomValue1(); + custom_value2 = linker_patch.GetBakerCustomValue2(); + break; + case linker::LinkerPatch::Type::kCallRelative: + // No custom values. + break; + default: + LOG(FATAL) << "Unexpected patch type: " << linker_patch.GetType(); + UNREACHABLE(); + } + return ThunkMapKey(linker_patch.GetType(), custom_value1, custom_value2); +} + +ArrayRef CompiledMethodStorage::GetThunkCode(const linker::LinkerPatch& linker_patch, + /*out*/ std::string* debug_name) { + ThunkMapKey key = GetThunkMapKey(linker_patch); + MutexLock lock(Thread::Current(), thunk_map_lock_); + auto it = thunk_map_.find(key); + if (it != thunk_map_.end()) { + const ThunkMapValue& value = it->second; + if (debug_name != nullptr) { + *debug_name = value.GetDebugName(); + } + return value.GetCode(); + } else { + if (debug_name != nullptr) { + *debug_name = std::string(); + } + return ArrayRef(); + } +} + +void CompiledMethodStorage::SetThunkCode(const linker::LinkerPatch& linker_patch, + ArrayRef code, + const std::string& debug_name) { + DCHECK(!code.empty()); + ThunkMapKey key = GetThunkMapKey(linker_patch); + std::vector> code_copy( + code.begin(), code.end(), SwapAllocator(swap_space_.get())); + ThunkMapValue value(std::move(code_copy), debug_name); + MutexLock lock(Thread::Current(), thunk_map_lock_); + // Note: Multiple threads can try and compile the same thunk, so this may not create a new entry. + thunk_map_.emplace(key, std::move(value)); +} + } // namespace art diff --git a/compiler/driver/compiled_method_storage.h b/compiler/driver/compiled_method_storage.h index 249f06c20f35c5954f0ae168e027ff06ed55a9d5..1634facb7ca1088bb9fd4bbf43e9f5ad222abeff 100644 --- a/compiler/driver/compiled_method_storage.h +++ b/compiler/driver/compiled_method_storage.h @@ -18,6 +18,7 @@ #define ART_COMPILER_DRIVER_COMPILED_METHOD_STORAGE_H_ #include +#include #include #include "base/array_ref.h" @@ -67,7 +68,29 @@ class CompiledMethodStorage { const ArrayRef& linker_patches); void ReleaseLinkerPatches(const LengthPrefixedArray* linker_patches); + // Returns the code associated with the given patch. + // If the code has not been set, returns empty data. + // If `debug_name` is not null, stores the associated debug name in `*debug_name`. + ArrayRef GetThunkCode(const linker::LinkerPatch& linker_patch, + /*out*/ std::string* debug_name = nullptr); + + // Sets the code and debug name associated with the given patch. + void SetThunkCode(const linker::LinkerPatch& linker_patch, + ArrayRef code, + const std::string& debug_name); + private: + class ThunkMapKey; + class ThunkMapValue; + using ThunkMapValueType = std::pair; + using ThunkMap = std::map, + SwapAllocator>; + static_assert(std::is_same::value, "Value type check."); + + static ThunkMapKey GetThunkMapKey(const linker::LinkerPatch& linker_patch); + template const LengthPrefixedArray* AllocateOrDeduplicateArray(const ArrayRef& data, DedupeSetType* dedupe_set); @@ -102,6 +125,9 @@ class CompiledMethodStorage { ArrayDedupeSet dedupe_cfi_info_; ArrayDedupeSet dedupe_linker_patches_; + Mutex thunk_map_lock_; + ThunkMap thunk_map_ GUARDED_BY(thunk_map_lock_); + DISALLOW_COPY_AND_ASSIGN(CompiledMethodStorage); }; diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc index 0769561d0ed6e2fb8fe12566147152fb7e1f9ac3..aed04f9c757b2022af9dbb20066d7a81a893f5eb 100644 --- a/compiler/driver/compiled_method_storage_test.cc +++ b/compiler/driver/compiled_method_storage_test.cc @@ -31,11 +31,7 @@ TEST(CompiledMethodStorage, Deduplicate) { CompilerDriver driver(&compiler_options, &verification_results, Compiler::kOptimizing, - /* instruction_set_ */ InstructionSet::kNone, - /* instruction_set_features */ nullptr, /* image_classes */ nullptr, - /* compiled_classes */ nullptr, - /* compiled_methods */ nullptr, /* thread_count */ 1u, /* swap_fd */ -1, /* profile_compilation_info */ nullptr); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 53604761d12949fe39f06cd1321e4fd533396a21..b24b0362a37a13d96db71badb7d7b654eff8b54d 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -42,6 +42,7 @@ #include "compiler.h" #include "compiler_callbacks.h" #include "compiler_driver-inl.h" +#include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_annotations.h" @@ -57,8 +58,7 @@ #include "gc/space/space.h" #include "handle_scope-inl.h" #include "intrinsics_enum.h" -#include "jit/profile_compilation_info.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "linker/linker_patch.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" @@ -67,8 +67,8 @@ #include "mirror/object-refvisitor-inl.h" #include "mirror/object_array-inl.h" #include "mirror/throwable.h" -#include "nativehelper/ScopedLocalRef.h" #include "object_lock.h" +#include "profile/profile_compilation_info.h" #include "runtime.h" #include "runtime_intrinsics.h" #include "scoped_thread_state_change-inl.h" @@ -112,7 +112,6 @@ class CompilerDriver::AOTCompilationStats { public: AOTCompilationStats() : stats_lock_("AOT compilation statistics lock"), - resolved_types_(0), unresolved_types_(0), resolved_instance_fields_(0), unresolved_instance_fields_(0), resolved_local_static_fields_(0), resolved_static_fields_(0), unresolved_static_fields_(0), type_based_devirtualization_(0), @@ -127,7 +126,6 @@ class CompilerDriver::AOTCompilationStats { } void Dump() { - DumpStat(resolved_types_, unresolved_types_, "types resolved"); DumpStat(resolved_instance_fields_, unresolved_instance_fields_, "instance fields resolved"); DumpStat(resolved_local_static_fields_ + resolved_static_fields_, unresolved_static_fields_, "static fields resolved"); @@ -177,16 +175,6 @@ class CompilerDriver::AOTCompilationStats { #define STATS_LOCK() #endif - void TypeDoesntNeedAccessCheck() REQUIRES(!stats_lock_) { - STATS_LOCK(); - resolved_types_++; - } - - void TypeNeedsAccessCheck() REQUIRES(!stats_lock_) { - STATS_LOCK(); - unresolved_types_++; - } - void ResolvedInstanceField() REQUIRES(!stats_lock_) { STATS_LOCK(); resolved_instance_fields_++; @@ -233,9 +221,6 @@ class CompilerDriver::AOTCompilationStats { private: Mutex stats_lock_; - size_t resolved_types_; - size_t unresolved_types_; - size_t resolved_instance_fields_; size_t unresolved_instance_fields_; @@ -261,11 +246,7 @@ CompilerDriver::CompilerDriver( const CompilerOptions* compiler_options, VerificationResults* verification_results, Compiler::Kind compiler_kind, - InstructionSet instruction_set, - const InstructionSetFeatures* instruction_set_features, - std::unordered_set* image_classes, - std::unordered_set* compiled_classes, - std::unordered_set* compiled_methods, + HashSet* image_classes, size_t thread_count, int swap_fd, const ProfileCompilationInfo* profile_compilation_info) @@ -273,20 +254,14 @@ CompilerDriver::CompilerDriver( verification_results_(verification_results), compiler_(Compiler::Create(this, compiler_kind)), compiler_kind_(compiler_kind), - instruction_set_( - instruction_set == InstructionSet::kArm ? InstructionSet::kThumb2 : instruction_set), - instruction_set_features_(instruction_set_features), requires_constructor_barrier_lock_("constructor barrier lock"), non_relative_linker_patch_count_(0u), - image_classes_(image_classes), - classes_to_compile_(compiled_classes), - methods_to_compile_(compiled_methods), + image_classes_(std::move(image_classes)), number_of_soft_verifier_failures_(0), had_hard_verifier_failure_(false), parallel_thread_count_(thread_count), stats_(new AOTCompilationStats), compiler_context_(nullptr), - support_boot_image_fixup_(true), compiled_method_storage_(swap_fd), profile_compilation_info_(profile_compilation_info), max_arena_alloc_(0), @@ -296,7 +271,7 @@ CompilerDriver::CompilerDriver( compiler_->Init(); if (GetCompilerOptions().IsBootImage()) { - CHECK(image_classes_.get() != nullptr) << "Expected image classes for boot image"; + CHECK(image_classes_ != nullptr) << "Expected image classes for boot image"; } compiled_method_storage_.SetDedupeEnabled(compiler_options_->DeduplicateCode()); @@ -313,13 +288,15 @@ CompilerDriver::~CompilerDriver() { } -#define CREATE_TRAMPOLINE(type, abi, offset) \ - if (Is64BitInstructionSet(instruction_set_)) { \ - return CreateTrampoline64(instruction_set_, abi, \ - type ## _ENTRYPOINT_OFFSET(PointerSize::k64, offset)); \ - } else { \ - return CreateTrampoline32(instruction_set_, abi, \ - type ## _ENTRYPOINT_OFFSET(PointerSize::k32, offset)); \ +#define CREATE_TRAMPOLINE(type, abi, offset) \ + if (Is64BitInstructionSet(GetCompilerOptions().GetInstructionSet())) { \ + return CreateTrampoline64(GetCompilerOptions().GetInstructionSet(), \ + abi, \ + type ## _ENTRYPOINT_OFFSET(PointerSize::k64, offset)); \ + } else { \ + return CreateTrampoline32(GetCompilerOptions().GetInstructionSet(), \ + abi, \ + type ## _ENTRYPOINT_OFFSET(PointerSize::k32, offset)); \ } std::unique_ptr> CompilerDriver::CreateJniDlsymLookup() const { @@ -354,12 +331,6 @@ void CompilerDriver::CompileAll(jobject class_loader, InitializeThreadPools(); - VLOG(compiler) << "Before precompile " << GetMemoryUsageString(false); - // Precompile: - // 1) Load image classes - // 2) Resolve all classes - // 3) Attempt to verify all classes - // 4) Attempt to initialize image classes, and trivially initialized classes PreCompile(class_loader, dex_files, timings); if (GetCompilerOptions().IsBootImage()) { // We don't need to setup the intrinsics for non boot image compilation, as @@ -394,7 +365,7 @@ static optimizer::DexToDexCompiler::CompilationLevel GetDexToDexCompilationLevel DCHECK(driver.GetCompilerOptions().IsQuickeningCompilationEnabled()); const char* descriptor = dex_file.GetClassDescriptor(class_def); ClassLinker* class_linker = runtime->GetClassLinker(); - mirror::Class* klass = class_linker->FindClass(self, descriptor, class_loader); + ObjPtr klass = class_linker->FindClass(self, descriptor, class_loader); if (klass == nullptr) { CHECK(self->IsExceptionPending()); self->ClearException(); @@ -611,7 +582,7 @@ static void CompileMethodQuick( if ((access_flags & kAccNative) != 0) { // Are we extracting only and have support for generic JNI down calls? if (!driver->GetCompilerOptions().IsJniCompilationEnabled() && - InstructionSetHasGenericJniStub(driver->GetInstructionSet())) { + InstructionSetHasGenericJniStub(driver->GetCompilerOptions().GetInstructionSet())) { // Leaving this empty will trigger the generic JNI version } else { // Query any JNI optimization annotations such as @FastNative or @CriticalNative. @@ -638,7 +609,6 @@ static void CompileMethodQuick( (verified_method->GetEncounteredVerificationFailures() & (verifier::VERIFY_ERROR_FORCE_INTERPRETER | verifier::VERIFY_ERROR_LOCKING)) == 0 && // Is eligable for compilation by methods-to-compile filter. - driver->IsMethodToCompile(method_ref) && driver->ShouldCompileBasedOnProfile(method_ref); if (compile) { @@ -677,46 +647,24 @@ static void CompileMethodQuick( quick_fn); } -void CompilerDriver::CompileOne(Thread* self, ArtMethod* method, TimingLogger* timings) { - DCHECK(!Runtime::Current()->IsStarted()); - jobject jclass_loader; - const DexFile* dex_file; - uint16_t class_def_idx; - uint32_t method_idx = method->GetDexMethodIndex(); - uint32_t access_flags = method->GetAccessFlags(); - InvokeType invoke_type = method->GetInvokeType(); - StackHandleScope<2> hs(self); - Handle dex_cache(hs.NewHandle(method->GetDexCache())); - Handle class_loader( - hs.NewHandle(method->GetDeclaringClass()->GetClassLoader())); - { - ScopedObjectAccessUnchecked soa(self); - ScopedLocalRef local_class_loader( - soa.Env(), soa.AddLocalReference(class_loader.Get())); - jclass_loader = soa.Env()->NewGlobalRef(local_class_loader.get()); - // Find the dex_file - dex_file = method->GetDexFile(); - class_def_idx = method->GetClassDefIndex(); - } - const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset()); - - // Go to native so that we don't block GC during compilation. - ScopedThreadSuspension sts(self, kNative); - - std::vector dex_files; - dex_files.push_back(dex_file); - - InitializeThreadPools(); - - PreCompile(jclass_loader, dex_files, timings); - +// Compile a single Method. (For testing only.) +void CompilerDriver::CompileOne(Thread* self, + jobject class_loader, + const DexFile& dex_file, + uint16_t class_def_idx, + uint32_t method_idx, + uint32_t access_flags, + InvokeType invoke_type, + const DexFile::CodeItem* code_item, + Handle dex_cache, + Handle h_class_loader) { // Can we run DEX-to-DEX compiler on this class ? optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level = GetDexToDexCompilationLevel(self, *this, - jclass_loader, - *dex_file, - dex_file->GetClassDef(class_def_idx)); + class_loader, + dex_file, + dex_file.GetClassDef(class_def_idx)); CompileMethodQuick(self, this, @@ -725,8 +673,8 @@ void CompilerDriver::CompileOne(Thread* self, ArtMethod* method, TimingLogger* t invoke_type, class_def_idx, method_idx, - class_loader, - *dex_file, + h_class_loader, + dex_file, dex_to_dex_compilation_level, true, dex_cache); @@ -741,17 +689,13 @@ void CompilerDriver::CompileOne(Thread* self, ArtMethod* method, TimingLogger* t invoke_type, class_def_idx, method_idx, - class_loader, - *dex_file, + h_class_loader, + dex_file, dex_to_dex_compilation_level, true, dex_cache); dex_to_dex_compiler_.ClearState(); } - - FreeThreadPools(); - - self->GetJniEnv()->DeleteGlobalRef(jclass_loader); } void CompilerDriver::Resolve(jobject class_loader, @@ -776,30 +720,84 @@ void CompilerDriver::Resolve(jobject class_loader, } } -// Resolve const-strings in the code. Done to have deterministic allocation behavior. Right now -// this is single-threaded for simplicity. -// TODO: Collect the relevant string indices in parallel, then allocate them sequentially in a -// stable order. +static void ResolveConstStrings(CompilerDriver* driver, + const std::vector& dex_files, + TimingLogger* timings) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + MutableHandle dex_cache(hs.NewHandle(nullptr)); -static void ResolveConstStrings(Handle dex_cache, - const DexFile& dex_file, - const DexFile::CodeItem* code_item) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (code_item == nullptr) { - // Abstract or native method. - return; + for (const DexFile* dex_file : dex_files) { + dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file)); + TimingLogger::ScopedTiming t("Resolve const-string Strings", timings); + + for (ClassAccessor accessor : dex_file->GetClasses()) { + if (!driver->IsClassToCompile(accessor.GetDescriptor())) { + // Compilation is skipped, do not resolve const-string in code of this class. + // FIXME: Make sure that inlining honors this. b/26687569 + continue; + } + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + // Resolve const-strings in the code. Done to have deterministic allocation behavior. Right + // now this is single-threaded for simplicity. + // TODO: Collect the relevant string indices in parallel, then allocate them sequentially + // in a stable order. + for (const DexInstructionPcPair& inst : method.GetInstructions()) { + switch (inst->Opcode()) { + case Instruction::CONST_STRING: + case Instruction::CONST_STRING_JUMBO: { + dex::StringIndex string_index((inst->Opcode() == Instruction::CONST_STRING) + ? inst->VRegB_21c() + : inst->VRegB_31c()); + ObjPtr string = class_linker->ResolveString(string_index, dex_cache); + CHECK(string != nullptr) << "Could not allocate a string when forcing determinism"; + break; + } + + default: + break; + } + } + } + } } +} - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); - for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) { +// Initialize type check bit strings for check-cast and instance-of in the code. Done to have +// deterministic allocation behavior. Right now this is single-threaded for simplicity. +// TODO: Collect the relevant type indices in parallel, then process them sequentially in a +// stable order. + +static void InitializeTypeCheckBitstrings(CompilerDriver* driver, + ClassLinker* class_linker, + Handle dex_cache, + const DexFile& dex_file, + const ClassAccessor::Method& method) + REQUIRES_SHARED(Locks::mutator_lock_) { + for (const DexInstructionPcPair& inst : method.GetInstructions()) { switch (inst->Opcode()) { - case Instruction::CONST_STRING: - case Instruction::CONST_STRING_JUMBO: { - dex::StringIndex string_index((inst->Opcode() == Instruction::CONST_STRING) - ? inst->VRegB_21c() - : inst->VRegB_31c()); - ObjPtr string = class_linker->ResolveString(string_index, dex_cache); - CHECK(string != nullptr) << "Could not allocate a string when forcing determinism"; + case Instruction::CHECK_CAST: + case Instruction::INSTANCE_OF: { + dex::TypeIndex type_index( + (inst->Opcode() == Instruction::CHECK_CAST) ? inst->VRegB_21c() : inst->VRegC_22c()); + const char* descriptor = dex_file.StringByTypeIdx(type_index); + // We currently do not use the bitstring type check for array or final (including + // primitive) classes. We may reconsider this in future if it's deemed to be beneficial. + // And we cannot use it for classes outside the boot image as we do not know the runtime + // value of their bitstring when compiling (it may not even get assigned at runtime). + if (descriptor[0] == 'L' && driver->GetCompilerOptions().IsImageClass(descriptor)) { + ObjPtr klass = + class_linker->LookupResolvedType(type_index, + dex_cache.Get(), + /* class_loader */ nullptr); + CHECK(klass != nullptr) << descriptor << " should have been previously resolved."; + // Now assign the bitstring if the class is not final. Keep this in sync with sharpening. + if (!klass->IsFinal()) { + MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); + SubtypeCheck>::EnsureAssigned(klass); + } + } break; } @@ -809,9 +807,9 @@ static void ResolveConstStrings(Handle dex_cache, } } -static void ResolveConstStrings(CompilerDriver* driver, - const std::vector& dex_files, - TimingLogger* timings) { +static void InitializeTypeCheckBitstrings(CompilerDriver* driver, + const std::vector& dex_files, + TimingLogger* timings) { ScopedObjectAccess soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); @@ -819,44 +817,19 @@ static void ResolveConstStrings(CompilerDriver* driver, for (const DexFile* dex_file : dex_files) { dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file)); - TimingLogger::ScopedTiming t("Resolve const-string Strings", timings); - - size_t class_def_count = dex_file->NumClassDefs(); - for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); + TimingLogger::ScopedTiming t("Initialize type check bitstrings", timings); - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - // empty class, probably a marker interface - continue; - } - - ClassDataItemIterator it(*dex_file, class_data); - it.SkipAllFields(); - - bool compilation_enabled = driver->IsClassToCompile( - dex_file->StringByTypeIdx(class_def.class_idx_)); - if (!compilation_enabled) { - // Compilation is skipped, do not resolve const-string in code of this class. - // TODO: Make sure that inlining honors this. + for (ClassAccessor accessor : dex_file->GetClasses()) { + if (!driver->IsClassToCompile(accessor.GetDescriptor())) { + // Compilation is skipped, do not look for type checks in code of this class. + // FIXME: Make sure that inlining honors this. b/26687569 continue; } // Direct and virtual methods. - int64_t previous_method_idx = -1; - while (it.HasNextMethod()) { - uint32_t method_idx = it.GetMemberIndex(); - if (method_idx == previous_method_idx) { - // smali can create dex files with two encoded_methods sharing the same method_idx - // http://code.google.com/p/smali/issues/detail?id=119 - it.Next(); - continue; - } - previous_method_idx = method_idx; - ResolveConstStrings(dex_cache, *dex_file, it.GetMethodCodeItem()); - it.Next(); + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + InitializeTypeCheckBitstrings(driver, class_linker, dex_cache, *dex_file, method); } - DCHECK(!it.HasNext()); } } } @@ -876,10 +849,8 @@ static void EnsureVerifiedOrVerifyAtRuntime(jobject jclass_loader, ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); for (const DexFile* dex_file : dex_files) { - for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); - const char* descriptor = dex_file->GetClassDescriptor(class_def); - cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader)); + for (ClassAccessor accessor : dex_file->GetClasses()) { + cls.Assign(class_linker->FindClass(soa.Self(), accessor.GetDescriptor(), class_loader)); if (cls == nullptr) { soa.Self()->ClearException(); } else if (&cls->GetDexFile() == dex_file) { @@ -896,6 +867,20 @@ void CompilerDriver::PreCompile(jobject class_loader, TimingLogger* timings) { CheckThreadPools(); + VLOG(compiler) << "Before precompile " << GetMemoryUsageString(false); + + compiled_classes_.AddDexFiles(GetCompilerOptions().GetDexFilesForOatFile()); + dex_to_dex_compiler_.SetDexFiles(GetCompilerOptions().GetDexFilesForOatFile()); + + // Precompile: + // 1) Load image classes. + // 2) Resolve all classes. + // 3) For deterministic boot image, resolve strings for const-string instructions. + // 4) Attempt to verify all classes. + // 5) Attempt to initialize image classes, and trivially initialized classes. + // 6) Update the set of image classes. + // 7) For deterministic boot image, initialize bitstrings for type checking. + LoadImageClasses(timings); VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(false); @@ -955,32 +940,21 @@ void CompilerDriver::PreCompile(jobject class_loader, UpdateImageClasses(timings); VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false); -} -bool CompilerDriver::IsImageClass(const char* descriptor) const { - if (image_classes_ != nullptr) { - // If we have a set of image classes, use those. - return image_classes_->find(descriptor) != image_classes_->end(); + if (kBitstringSubtypeCheckEnabled && + GetCompilerOptions().IsForceDeterminism() && GetCompilerOptions().IsBootImage()) { + // Initialize type check bit string used by check-cast and instanceof. + // Do this now to have a deterministic image. + // Note: This is done after UpdateImageClasses() at it relies on the image classes to be final. + InitializeTypeCheckBitstrings(this, dex_files, timings); } - // No set of image classes, assume we include all the classes. - // NOTE: Currently only reachable from InitImageMethodVisitor for the app image case. - return !GetCompilerOptions().IsBootImage(); } bool CompilerDriver::IsClassToCompile(const char* descriptor) const { if (classes_to_compile_ == nullptr) { return true; } - return classes_to_compile_->find(descriptor) != classes_to_compile_->end(); -} - -bool CompilerDriver::IsMethodToCompile(const MethodReference& method_ref) const { - if (methods_to_compile_ == nullptr) { - return true; - } - - std::string tmp = method_ref.PrettyMethod(); - return methods_to_compile_->find(tmp.c_str()) != methods_to_compile_->end(); + return classes_to_compile_->find(StringPiece(descriptor)) != classes_to_compile_->end(); } bool CompilerDriver::ShouldCompileBasedOnProfile(const MethodReference& method_ref) const { @@ -1069,7 +1043,7 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { class RecordImageClassesVisitor : public ClassVisitor { public: - explicit RecordImageClassesVisitor(std::unordered_set* image_classes) + explicit RecordImageClassesVisitor(HashSet* image_classes) : image_classes_(image_classes) {} bool operator()(ObjPtr klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { @@ -1079,7 +1053,7 @@ class RecordImageClassesVisitor : public ClassVisitor { } private: - std::unordered_set* const image_classes_; + HashSet* const image_classes_; }; // Make a list of descriptors for classes to include in the image @@ -1094,7 +1068,7 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) { Thread* self = Thread::Current(); ScopedObjectAccess soa(self); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - CHECK(image_classes_.get() != nullptr); + CHECK(image_classes_ != nullptr); for (auto it = image_classes_->begin(), end = image_classes_->end(); it != end;) { const std::string& descriptor(*it); StackHandleScope<1> hs(self); @@ -1102,7 +1076,7 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) { hs.NewHandle(class_linker->FindSystemClass(self, descriptor.c_str()))); if (klass == nullptr) { VLOG(compiler) << "Failed to find class " << descriptor; - image_classes_->erase(it++); + it = image_classes_->erase(it); self->ClearException(); } else { ++it; @@ -1152,15 +1126,15 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) { // We walk the roots looking for classes so that we'll pick up the // above classes plus any classes them depend on such super // classes, interfaces, and the required ClassLinker roots. - RecordImageClassesVisitor visitor(image_classes_.get()); + RecordImageClassesVisitor visitor(image_classes_); class_linker->VisitClasses(&visitor); - CHECK_NE(image_classes_->size(), 0U); + CHECK(!image_classes_->empty()); } static void MaybeAddToImageClasses(Thread* self, ObjPtr klass, - std::unordered_set* image_classes) + HashSet* image_classes) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK_EQ(self, Thread::Current()); StackHandleScope<1> hs(self); @@ -1168,11 +1142,10 @@ static void MaybeAddToImageClasses(Thread* self, const PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); while (!klass->IsObjectClass()) { const char* descriptor = klass->GetDescriptor(&temp); - std::pair::iterator, bool> result = - image_classes->insert(descriptor); - if (!result.second) { // Previously inserted. - break; + if (image_classes->find(StringPiece(descriptor)) != image_classes->end()) { + break; // Previously inserted. } + image_classes->insert(descriptor); VLOG(compiler) << "Adding " << descriptor << " to image classes"; for (size_t i = 0, num_interfaces = klass->NumDirectInterfaces(); i != num_interfaces; ++i) { ObjPtr interface = mirror::Class::GetDirectInterface(self, klass, i); @@ -1194,7 +1167,7 @@ static void MaybeAddToImageClasses(Thread* self, class ClinitImageUpdate { public: static ClinitImageUpdate* Create(VariableSizedHandleScope& hs, - std::unordered_set* image_class_descriptors, + HashSet* image_class_descriptors, Thread* self, ClassLinker* linker) { std::unique_ptr res(new ClinitImageUpdate(hs, @@ -1251,7 +1224,7 @@ class ClinitImageUpdate { bool operator()(ObjPtr klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { std::string temp; - const char* name = klass->GetDescriptor(&temp); + StringPiece name(klass->GetDescriptor(&temp)); if (data_->image_class_descriptors_->find(name) != data_->image_class_descriptors_->end()) { data_->image_classes_.push_back(hs_.NewHandle(klass)); } else { @@ -1270,7 +1243,7 @@ class ClinitImageUpdate { }; ClinitImageUpdate(VariableSizedHandleScope& hs, - std::unordered_set* image_class_descriptors, + HashSet* image_class_descriptors, Thread* self, ClassLinker* linker) REQUIRES_SHARED(Locks::mutator_lock_) : hs_(hs), @@ -1317,7 +1290,7 @@ class ClinitImageUpdate { VariableSizedHandleScope& hs_; mutable std::vector> to_insert_; mutable std::unordered_set marked_objects_; - std::unordered_set* const image_class_descriptors_; + HashSet* const image_class_descriptors_; std::vector> image_classes_; Thread* const self_; const char* old_cause_; @@ -1337,7 +1310,7 @@ void CompilerDriver::UpdateImageClasses(TimingLogger* timings) { VariableSizedHandleScope hs(Thread::Current()); std::string error_msg; std::unique_ptr update(ClinitImageUpdate::Create(hs, - image_classes_.get(), + image_classes_, Thread::Current(), runtime->GetClassLinker())); @@ -1346,77 +1319,6 @@ void CompilerDriver::UpdateImageClasses(TimingLogger* timings) { } } -bool CompilerDriver::CanAssumeClassIsLoaded(mirror::Class* klass) { - Runtime* runtime = Runtime::Current(); - if (!runtime->IsAotCompiler()) { - DCHECK(runtime->UseJitCompilation()); - // Having the klass reference here implies that the klass is already loaded. - return true; - } - if (!GetCompilerOptions().IsBootImage()) { - // Assume loaded only if klass is in the boot image. App classes cannot be assumed - // loaded because we don't even know what class loader will be used to load them. - bool class_in_image = runtime->GetHeap()->FindSpaceFromObject(klass, false)->IsImageSpace(); - return class_in_image; - } - std::string temp; - const char* descriptor = klass->GetDescriptor(&temp); - return IsImageClass(descriptor); -} - -bool CompilerDriver::CanAccessTypeWithoutChecks(ObjPtr referrer_class, - ObjPtr resolved_class) { - if (resolved_class == nullptr) { - stats_->TypeNeedsAccessCheck(); - return false; // Unknown class needs access checks. - } - bool is_accessible = resolved_class->IsPublic(); // Public classes are always accessible. - if (!is_accessible) { - if (referrer_class == nullptr) { - stats_->TypeNeedsAccessCheck(); - return false; // Incomplete referrer knowledge needs access check. - } - // Perform access check, will return true if access is ok or false if we're going to have to - // check this at runtime (for example for class loaders). - is_accessible = referrer_class->CanAccess(resolved_class); - } - if (is_accessible) { - stats_->TypeDoesntNeedAccessCheck(); - } else { - stats_->TypeNeedsAccessCheck(); - } - return is_accessible; -} - -bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(ObjPtr referrer_class, - ObjPtr resolved_class, - bool* finalizable) { - if (resolved_class == nullptr) { - stats_->TypeNeedsAccessCheck(); - // Be conservative. - *finalizable = true; - return false; // Unknown class needs access checks. - } - *finalizable = resolved_class->IsFinalizable(); - bool is_accessible = resolved_class->IsPublic(); // Public classes are always accessible. - if (!is_accessible) { - if (referrer_class == nullptr) { - stats_->TypeNeedsAccessCheck(); - return false; // Incomplete referrer knowledge needs access check. - } - // Perform access and instantiable checks, will return true if access is ok or false if we're - // going to have to check this at runtime (for example for class loaders). - is_accessible = referrer_class->CanAccess(resolved_class); - } - bool result = is_accessible && resolved_class->IsInstantiable(); - if (result) { - stats_->TypeDoesntNeedAccessCheck(); - } else { - stats_->TypeNeedsAccessCheck(); - } - return result; -} - void CompilerDriver::ProcessedInstanceField(bool resolved) { if (!resolved) { stats_->UnresolvedInstanceField(); @@ -1555,7 +1457,7 @@ class ParallelCompilationManager { self->AssertNoPendingException(); CHECK_GT(work_units, 0U); - index_.StoreRelaxed(begin); + index_.store(begin, std::memory_order_relaxed); for (size_t i = 0; i < work_units; ++i) { thread_pool_->AddTask(self, new ForAllClosureLambda(this, end, fn)); } @@ -1573,7 +1475,7 @@ class ParallelCompilationManager { } size_t NextIndex() { - return index_.FetchAndAddSequentiallyConsistent(1); + return index_.fetch_add(1, std::memory_order_seq_cst); } private: @@ -1663,20 +1565,12 @@ static void CheckAndClearResolveException(Thread* self) bool CompilerDriver::RequiresConstructorBarrier(const DexFile& dex_file, uint16_t class_def_idx) const { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx); - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { - // Empty class such as a marker interface. - return false; - } - ClassDataItemIterator it(dex_file, class_data); - it.SkipStaticFields(); + ClassAccessor accessor(dex_file, class_def_idx); // We require a constructor barrier if there are final instance fields. - while (it.HasNextInstanceField()) { - if (it.MemberIsFinal()) { + for (const ClassAccessor::Field& field : accessor.GetInstanceFields()) { + if (field.IsFinal()) { return true; } - it.Next(); } return false; } @@ -1693,11 +1587,6 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { const DexFile& dex_file = *manager_->GetDexFile(); ClassLinker* class_linker = manager_->GetClassLinker(); - // If an instance field is final then we need to have a barrier on the return, static final - // fields are assigned within the lock held for class initialization. Conservatively assume - // constructor barriers are always required. - bool requires_constructor_barrier = true; - // Method and Field are the worst. We can't resolve without either // context from the code use (to disambiguate virtual vs direct // method and instance vs static field) or from class @@ -1729,56 +1618,53 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { // We want to resolve the methods and fields eagerly. resolve_fields_and_methods = true; } - // Note the class_data pointer advances through the headers, - // static fields, instance fields, direct methods, and virtual - // methods. - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { - // Empty class such as a marker interface. - requires_constructor_barrier = false; - } else { - ClassDataItemIterator it(dex_file, class_data); - while (it.HasNextStaticField()) { - if (resolve_fields_and_methods) { - ArtField* field = class_linker->ResolveField( - it.GetMemberIndex(), dex_cache, class_loader, /* is_static */ true); - if (field == nullptr) { - CheckAndClearResolveException(soa.Self()); - } + // If an instance field is final then we need to have a barrier on the return, static final + // fields are assigned within the lock held for class initialization. + bool requires_constructor_barrier = false; + + 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_) { + if (resolve_fields_and_methods) { + ArtMethod* resolved = class_linker->ResolveMethod( + method.GetIndex(), + dex_cache, + class_loader, + /* referrer */ nullptr, + method.GetInvokeType(class_def.access_flags_)); + if (resolved == nullptr) { + CheckAndClearResolveException(soa.Self()); } - it.Next(); } - // We require a constructor barrier if there are final instance fields. - requires_constructor_barrier = false; - while (it.HasNextInstanceField()) { - if (it.MemberIsFinal()) { - requires_constructor_barrier = true; - } - if (resolve_fields_and_methods) { - ArtField* field = class_linker->ResolveField( - it.GetMemberIndex(), dex_cache, class_loader, /* is_static */ false); - if (field == nullptr) { - CheckAndClearResolveException(soa.Self()); + }; + accessor.VisitFieldsAndMethods( + // static fields + [&](ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) { + if (resolve_fields_and_methods) { + ArtField* resolved = class_linker->ResolveField( + field.GetIndex(), dex_cache, class_loader, /* is_static */ true); + if (resolved == nullptr) { + CheckAndClearResolveException(soa.Self()); + } } - } - it.Next(); - } - if (resolve_fields_and_methods) { - while (it.HasNextMethod()) { - ArtMethod* method = class_linker->ResolveMethod( - it.GetMemberIndex(), - dex_cache, - class_loader, - /* referrer */ nullptr, - it.GetMethodInvokeType(class_def)); - if (method == nullptr) { - CheckAndClearResolveException(soa.Self()); + }, + // instance fields + [&](ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) { + if (field.IsFinal()) { + // We require a constructor barrier if there are final instance fields. + requires_constructor_barrier = true; } - it.Next(); - } - DCHECK(!it.HasNext()); - } - } + if (resolve_fields_and_methods) { + ArtField* resolved = class_linker->ResolveField( + field.GetIndex(), dex_cache, class_loader, /* is_static */ false); + if (resolved == nullptr) { + CheckAndClearResolveException(soa.Self()); + } + } + }, + /*direct methods*/ method_visitor, + /*virtual methods*/ method_visitor); manager_->GetCompiler()->SetRequiresConstructorBarrier(self, &dex_file, class_def_index, @@ -1865,32 +1751,13 @@ void CompilerDriver::SetVerified(jobject class_loader, } } -static void PopulateVerifiedMethods(const DexFile& dex_file, - uint32_t class_def_index, - VerificationResults* verification_results) { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { - return; - } - ClassDataItemIterator it(dex_file, class_data); - it.SkipAllFields(); - - while (it.HasNextMethod()) { - verification_results->CreateVerifiedMethodFor(MethodReference(&dex_file, it.GetMemberIndex())); - it.Next(); - } - DCHECK(!it.HasNext()); -} - -static void LoadAndUpdateStatus(const DexFile& dex_file, - const DexFile::ClassDef& class_def, +static void LoadAndUpdateStatus(const ClassAccessor& accessor, ClassStatus status, Handle class_loader, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { StackHandleScope<1> hs(self); - const char* descriptor = dex_file.GetClassDescriptor(class_def); + const char* descriptor = accessor.GetDescriptor(); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle cls(hs.NewHandle( class_linker->FindClass(self, descriptor, class_loader))); @@ -1898,7 +1765,7 @@ static void LoadAndUpdateStatus(const DexFile& dex_file, // Check that the class is resolved with the current dex file. We might get // a boot image class, or a class in a different dex file for multidex, and // we should not update the status in that case. - if (&cls->GetDexFile() == &dex_file) { + if (&cls->GetDexFile() == &accessor.GetDexFile()) { ObjectLock lock(self, cls); mirror::Class::SetStatus(cls, status, self); } @@ -1937,36 +1804,34 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, // Fetch the list of unverified classes. const std::set& unverified_classes = verifier_deps->GetUnverifiedClasses(*dex_file); - for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); - if (unverified_classes.find(class_def.class_idx_) == unverified_classes.end()) { + 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, i); - ClassStatus existing = ClassStatus::kNotReady; - DCHECK(compiled_classes_.Get(ref, &existing)) << ref.dex_file->GetLocation(); + ClassReference ref(dex_file, accessor.GetClassDefIndex()); + const ClassStatus existing = ClassStatus::kNotReady; ClassStateTable::InsertResult result = compiled_classes_.Insert(ref, existing, ClassStatus::kVerified); - CHECK_EQ(result, ClassStateTable::kInsertResultSuccess); + CHECK_EQ(result, ClassStateTable::kInsertResultSuccess) << ref.dex_file->GetLocation(); } else { // Update the class status, so later compilation stages know they don't need to verify // the class. - LoadAndUpdateStatus( - *dex_file, class_def, ClassStatus::kVerified, class_loader, soa.Self()); + LoadAndUpdateStatus(accessor, ClassStatus::kVerified, class_loader, soa.Self()); // Create `VerifiedMethod`s for each methods, the compiler expects one for // quickening or compiling. // Note that this means: // - We're only going to compile methods that did verify. // - Quickening will not do checkcast ellision. // TODO(ngeoffray): Reconsider this once we refactor compiler filters. - PopulateVerifiedMethods(*dex_file, i, verification_results_); + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + verification_results_->CreateVerifiedMethodFor(method.GetReference()); + } } } else if (!compiler_only_verifies) { // Make sure later compilation stages know they should not try to verify // this class again. - LoadAndUpdateStatus(*dex_file, - class_def, + LoadAndUpdateStatus(accessor, ClassStatus::kRetryVerificationAtRuntime, class_loader, soa.Self()); @@ -1997,7 +1862,8 @@ void CompilerDriver::Verify(jobject jclass_loader, // Create per-thread VerifierDeps to avoid contention on the main one. // We will merge them after verification. for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) { - worker->GetThread()->SetVerifierDeps(new verifier::VerifierDeps(dex_files_for_oat_file_)); + worker->GetThread()->SetVerifierDeps( + new verifier::VerifierDeps(GetCompilerOptions().GetDexFilesForOatFile())); } } @@ -2022,7 +1888,7 @@ void CompilerDriver::Verify(jobject jclass_loader, for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) { verifier::VerifierDeps* thread_deps = worker->GetThread()->GetVerifierDeps(); worker->GetThread()->SetVerifierDeps(nullptr); - verifier_deps->MergeWith(*thread_deps, dex_files_for_oat_file_); + verifier_deps->MergeWith(*thread_deps, GetCompilerOptions().GetDexFilesForOatFile()); delete thread_deps; } Thread::Current()->SetVerifierDeps(nullptr); @@ -2083,6 +1949,9 @@ class VerifyClassVisitor : public CompilationVisitor { DCHECK(failure_kind == verifier::FailureKind::kNoFailure) << failure_kind; failure_kind = verifier::FailureKind::kSoftFailure; } + } else if (&klass->GetDexFile() != &dex_file) { + // Skip a duplicate class (as the resolved class is from another, earlier dex file). + return; // Do not update state. } else if (!SkipClass(jclass_loader, dex_file, klass.Get())) { CHECK(klass->IsResolved()) << klass->PrettyClass(); failure_kind = class_linker->VerifyClass(soa.Self(), klass, log_level_); @@ -2187,8 +2056,9 @@ class SetVerifiedClassVisitor : public CompilationVisitor { mirror::Class::SetStatus(klass, ClassStatus::kVerified, soa.Self()); // Mark methods as pre-verified. If we don't do this, the interpreter will run with // access checks. - klass->SetSkipAccessChecksFlagOnAllMethods( - GetInstructionSetPointerSize(manager_->GetCompiler()->GetInstructionSet())); + InstructionSet instruction_set = + manager_->GetCompiler()->GetCompilerOptions().GetInstructionSet(); + klass->SetSkipAccessChecksFlagOnAllMethods(GetInstructionSetPointerSize(instruction_set)); klass->SetVerificationAttempted(); } // Record the final class status if necessary. @@ -2305,7 +2175,7 @@ class InitializeClassVisitor : public CompilationVisitor { (is_app_image || is_boot_image) && is_superclass_initialized && !too_many_encoded_fields && - manager_->GetCompiler()->IsImageClass(descriptor)) { + manager_->GetCompiler()->GetCompilerOptions().IsImageClass(descriptor)) { bool can_init_static_fields = false; if (is_boot_image) { // We need to initialize static fields, we only do this for image classes that aren't @@ -2704,22 +2574,22 @@ static void CompileDexFile(CompilerDriver* driver, auto compile = [&context, &compile_fn](size_t class_def_index) { ScopedTrace trace(__FUNCTION__); const DexFile& dex_file = *context.GetDexFile(); - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); ClassLinker* class_linker = context.GetClassLinker(); 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_index); // Skip compiling classes with generic verifier failures since they will still fail at runtime if (context.GetCompiler()->GetVerificationResults()->IsClassRejected(ref)) { return; } // Use a scoped object access to perform to the quick SkipClass check. - const char* descriptor = dex_file.GetClassDescriptor(class_def); ScopedObjectAccess soa(Thread::Current()); StackHandleScope<3> hs(soa.Self()); Handle class_loader( hs.NewHandle(soa.Decode(jclass_loader))); Handle klass( - hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader))); + hs.NewHandle(class_linker->FindClass(soa.Self(), accessor.GetDescriptor(), class_loader))); Handle dex_cache; if (klass == nullptr) { soa.Self()->AssertPendingException(); @@ -2727,13 +2597,15 @@ static void CompileDexFile(CompilerDriver* driver, dex_cache = hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)); } else if (SkipClass(jclass_loader, dex_file, klass.Get())) { return; + } else if (&klass->GetDexFile() != &dex_file) { + // Skip a duplicate class (as the resolved class is from another, earlier dex file). + return; // Do not update state. } else { dex_cache = hs.NewHandle(klass->GetDexCache()); } - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { - // empty class, probably a marker interface + // Avoid suspension if there are no methods to compile. + if (accessor.NumDirectMethods() + accessor.NumVirtualMethods() == 0) { return; } @@ -2746,28 +2618,24 @@ static void CompileDexFile(CompilerDriver* driver, optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level = GetDexToDexCompilationLevel(soa.Self(), *driver, jclass_loader, dex_file, class_def); - ClassDataItemIterator it(dex_file, class_data); - it.SkipAllFields(); - bool compilation_enabled = driver->IsClassToCompile( - dex_file.StringByTypeIdx(class_def.class_idx_)); + const bool compilation_enabled = driver->IsClassToCompile(accessor.GetDescriptor()); // Compile direct and virtual methods. int64_t previous_method_idx = -1; - while (it.HasNextMethod()) { - uint32_t method_idx = it.GetMemberIndex(); + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + const uint32_t method_idx = method.GetIndex(); if (method_idx == previous_method_idx) { // smali can create dex files with two encoded_methods sharing the same method_idx // http://code.google.com/p/smali/issues/detail?id=119 - it.Next(); continue; } previous_method_idx = method_idx; compile_fn(soa.Self(), driver, - it.GetMethodCodeItem(), - it.GetMethodAccessFlags(), - it.GetMethodInvokeType(class_def), + method.GetCodeItem(), + method.GetAccessFlags(), + method.GetInvokeType(class_def.access_flags_), class_def_index, method_idx, class_loader, @@ -2775,9 +2643,7 @@ static void CompileDexFile(CompilerDriver* driver, dex_to_dex_compilation_level, compilation_enabled, dex_cache); - it.Next(); } - DCHECK(!it.HasNext()); }; context.ForAllLambda(0, dex_file.NumClassDefs(), compile, thread_count); } @@ -2838,7 +2704,8 @@ void CompilerDriver::AddCompiledMethod(const MethodReference& method_ref, /*expected*/ nullptr, compiled_method); CHECK(result == MethodTable::kInsertResultSuccess); - non_relative_linker_patch_count_.FetchAndAddRelaxed(non_relative_linker_patch_count); + non_relative_linker_patch_count_.fetch_add(non_relative_linker_patch_count, + std::memory_order_relaxed); DCHECK(GetCompiledMethod(method_ref) != nullptr) << method_ref.PrettyMethod(); } @@ -2895,7 +2762,7 @@ void CompilerDriver::RecordClassStatus(const ClassReference& ref, ClassStatus st if (kIsDebugBuild) { // Check to make sure it's not a dex file for an oat file we are compiling since these // should always succeed. These do not include classes in for used libraries. - for (const DexFile* dex_file : GetDexFilesForOatFile()) { + for (const DexFile* dex_file : GetCompilerOptions().GetDexFilesForOatFile()) { CHECK_NE(ref.dex_file, dex_file) << ref.dex_file->GetLocation(); } } @@ -2949,7 +2816,7 @@ bool CompilerDriver::IsMethodVerifiedWithoutFailures(uint32_t method_idx, } size_t CompilerDriver::GetNonRelativeLinkerPatchCount() const { - return non_relative_linker_patch_count_.LoadRelaxed(); + return non_relative_linker_patch_count_.load(std::memory_order_relaxed); } void CompilerDriver::SetRequiresConstructorBarrier(Thread* self, @@ -2994,18 +2861,6 @@ std::string CompilerDriver::GetMemoryUsageString(bool extended) const { return oss.str(); } -bool CompilerDriver::MayInlineInternal(const DexFile* inlined_from, - const DexFile* inlined_into) const { - // We're not allowed to inline across dex files if we're the no-inline-from dex file. - if (inlined_from != inlined_into && - compiler_options_->GetNoInlineFromDexFile() != nullptr && - ContainsElement(*compiler_options_->GetNoInlineFromDexFile(), inlined_from)) { - return false; - } - - return true; -} - void CompilerDriver::InitializeThreadPools() { size_t parallel_count = parallel_thread_count_ > 0 ? parallel_thread_count_ - 1 : 0; parallel_thread_pool_.reset( @@ -3018,12 +2873,6 @@ void CompilerDriver::FreeThreadPools() { single_thread_pool_.reset(); } -void CompilerDriver::SetDexFilesForOatFile(const std::vector& dex_files) { - dex_files_for_oat_file_ = dex_files; - compiled_classes_.AddDexFiles(dex_files); - dex_to_dex_compiler_.SetDexFiles(dex_files); -} - void CompilerDriver::SetClasspathDexFiles(const std::vector& dex_files) { classpath_classes_.AddDexFiles(dex_files); } diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index a5462eefe2acab10d17579a5d130dea2e3ca1452..3d3583cb9bf2824c06896b4f204131d4d0a06974 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -20,7 +20,6 @@ #include #include #include -#include #include #include "android-base/strings.h" @@ -28,6 +27,7 @@ #include "arch/instruction_set.h" #include "base/array_ref.h" #include "base/bit_utils.h" +#include "base/hash_set.h" #include "base/mutex.h" #include "base/os.h" #include "base/quasi_atomic.h" @@ -97,47 +97,36 @@ class CompilerDriver { CompilerDriver(const CompilerOptions* compiler_options, VerificationResults* verification_results, Compiler::Kind compiler_kind, - InstructionSet instruction_set, - const InstructionSetFeatures* instruction_set_features, - std::unordered_set* image_classes, - std::unordered_set* compiled_classes, - std::unordered_set* compiled_methods, + HashSet* image_classes, size_t thread_count, int swap_fd, const ProfileCompilationInfo* profile_compilation_info); ~CompilerDriver(); - // Set dex files associated with the oat file being compiled. - void SetDexFilesForOatFile(const std::vector& dex_files); - // Set dex files classpath. void SetClasspathDexFiles(const std::vector& dex_files); - // Get dex files associated with the the oat file being compiled. - ArrayRef GetDexFilesForOatFile() const { - return ArrayRef(dex_files_for_oat_file_); - } - void CompileAll(jobject class_loader, const std::vector& dex_files, TimingLogger* timings) REQUIRES(!Locks::mutator_lock_); - // Compile a single Method. - void CompileOne(Thread* self, ArtMethod* method, TimingLogger* timings) - REQUIRES_SHARED(Locks::mutator_lock_); + // Compile a single Method. (For testing only.) + void CompileOne(Thread* self, + jobject class_loader, + const DexFile& dex_file, + uint16_t class_def_idx, + uint32_t method_idx, + uint32_t access_flags, + InvokeType invoke_type, + const DexFile::CodeItem* code_item, + Handle dex_cache, + Handle h_class_loader) + REQUIRES(!Locks::mutator_lock_); VerificationResults* GetVerificationResults() const; - InstructionSet GetInstructionSet() const { - return instruction_set_; - } - - const InstructionSetFeatures* GetInstructionSetFeatures() const { - return instruction_set_features_; - } - const CompilerOptions& GetCompilerOptions() const { return *compiler_options_; } @@ -146,10 +135,6 @@ class CompilerDriver { return compiler_.get(); } - const std::unordered_set* GetImageClasses() const { - return image_classes_.get(); - } - // Generate the trampolines that are invoked by unresolved direct methods. std::unique_ptr> CreateJniDlsymLookup() const; std::unique_ptr> CreateQuickGenericJniTrampoline() const; @@ -212,18 +197,6 @@ class CompilerDriver { uint16_t class_def_index) REQUIRES(!requires_constructor_barrier_lock_); - // Are runtime access checks necessary in the compiled code? - bool CanAccessTypeWithoutChecks(ObjPtr referrer_class, - ObjPtr resolved_class) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Are runtime access and instantiable checks necessary in the code? - // out_is_finalizable is set to whether the type is finalizable. - bool CanAccessInstantiableTypeWithoutChecks(ObjPtr referrer_class, - ObjPtr resolved_class, - bool* out_is_finalizable) - REQUIRES_SHARED(Locks::mutator_lock_); - // Resolve compiling method's class. Returns null on failure. ObjPtr ResolveCompilingMethodsClass(const ScopedObjectAccess& soa, Handle dex_cache, @@ -282,14 +255,6 @@ class CompilerDriver { const VerifiedMethod* GetVerifiedMethod(const DexFile* dex_file, uint32_t method_idx) const; bool IsSafeCast(const DexCompilationUnit* mUnit, uint32_t dex_pc); - bool GetSupportBootImageFixup() const { - return support_boot_image_fixup_; - } - - void SetSupportBootImageFixup(bool support_boot_image_fixup) { - support_boot_image_fixup_ = support_boot_image_fixup; - } - void SetCompilerContext(void* compiler_context) { compiler_context_ = compiler_context; } @@ -310,15 +275,9 @@ class CompilerDriver { return compiled_method_storage_.DedupeEnabled(); } - // Checks if class specified by type_idx is one of the image_classes_ - bool IsImageClass(const char* descriptor) const; - // Checks whether the provided class should be compiled, i.e., is in classes_to_compile_. bool IsClassToCompile(const char* descriptor) const; - // Checks whether the provided method should be compiled, i.e., is in method_to_compile_. - bool IsMethodToCompile(const MethodReference& method_ref) const; - // Checks whether profile guided compilation is enabled and if the method should be compiled // according to the profile file. bool ShouldCompileBasedOnProfile(const MethodReference& method_ref) const; @@ -353,17 +312,6 @@ class CompilerDriver { return &compiled_method_storage_; } - // Can we assume that the klass is loaded? - bool CanAssumeClassIsLoaded(mirror::Class* klass) - REQUIRES_SHARED(Locks::mutator_lock_); - - bool MayInline(const DexFile* inlined_from, const DexFile* inlined_into) const { - if (!kIsTargetBuild) { - return MayInlineInternal(inlined_from, inlined_into); - } - return true; - } - const ProfileCompilationInfo* GetProfileCompilationInfo() const { return profile_compilation_info_; } @@ -457,8 +405,6 @@ class CompilerDriver { const std::vector& dex_files, TimingLogger* timings); - bool MayInlineInternal(const DexFile* inlined_from, const DexFile* inlined_into) const; - void InitializeThreadPools(); void FreeThreadPools(); void CheckThreadPools(); @@ -471,9 +417,6 @@ class CompilerDriver { std::unique_ptr compiler_; Compiler::Kind compiler_kind_; - const InstructionSet instruction_set_; - const InstructionSetFeatures* const instruction_set_features_; - // All class references that require constructor barriers. If the class reference is not in the // set then the result has not yet been computed. mutable ReaderWriterMutex requires_constructor_barrier_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; @@ -496,26 +439,24 @@ class CompilerDriver { // in the .oat_patches ELF section if requested in the compiler options. Atomic non_relative_linker_patch_count_; - // If image_ is true, specifies the classes that will be included in the image. - // Note if image_classes_ is null, all classes are included in the image. - std::unique_ptr> image_classes_; + // Image classes to be updated by PreCompile(). + // TODO: Remove this member which is a non-const pointer to the CompilerOptions' data. + // Pass this explicitly to the PreCompile() which should be called directly from + // Dex2Oat rather than implicitly by CompileAll(). + HashSet* image_classes_; // Specifies the classes that will be compiled. Note that if classes_to_compile_ is null, // all classes are eligible for compilation (duplication filters etc. will still apply). // This option may be restricted to the boot image, depending on a flag in the implementation. - std::unique_ptr> classes_to_compile_; - - // Specifies the methods that will be compiled. Note that if methods_to_compile_ is null, - // all methods are eligible for compilation (compilation filters etc. will still apply). - // This option may be restricted to the boot image, depending on a flag in the implementation. - std::unique_ptr> methods_to_compile_; + std::unique_ptr> classes_to_compile_; std::atomic number_of_soft_verifier_failures_; + bool had_hard_verifier_failure_; // A thread pool that can (potentially) run tasks in parallel. - std::unique_ptr parallel_thread_pool_; size_t parallel_thread_count_; + std::unique_ptr parallel_thread_pool_; // A thread pool that guarantees running single-threaded on the main thread. std::unique_ptr single_thread_pool_; @@ -528,11 +469,6 @@ class CompilerDriver { void* compiler_context_; - bool support_boot_image_fixup_; - - // List of dex files associates with the oat file. - std::vector dex_files_for_oat_file_; - CompiledMethodStorage compiled_method_storage_; // Info for profile guided compilation. @@ -543,6 +479,7 @@ class CompilerDriver { // Compiler for dex to dex (quickening). optimizer::DexToDexCompiler dex_to_dex_compiler_; + friend class CommonCompilerTest; friend class CompileClassVisitor; friend class DexToDexDecompilerTest; friend class verifier::VerifierDepsTest; diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index 162904c0e73714285b123cfd38e76cb04dd36c5f..2eeb4399db7996d4a67a532d5fcae68a06711e02 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -30,12 +30,12 @@ #include "dex/dex_file_types.h" #include "gc/heap.h" #include "handle_scope-inl.h" -#include "jit/profile_compilation_info.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/dex_cache-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" +#include "profile/profile_compilation_info.h" #include "scoped_thread_state_change-inl.h" namespace art { @@ -46,7 +46,7 @@ class CompilerDriverTest : public CommonCompilerTest { TimingLogger timings("CompilerDriverTest::CompileAll", false, false); TimingLogger::ScopedTiming t(__FUNCTION__, &timings); dex_files_ = GetDexFiles(class_loader); - compiler_driver_->SetDexFilesForOatFile(dex_files_);; + SetDexFilesForOatFile(dex_files_); compiler_driver_->CompileAll(class_loader, dex_files_, &timings); t.NewTiming("MakeAllExecutable"); MakeAllExecutable(class_loader); @@ -88,7 +88,7 @@ class CompilerDriverTest : public CommonCompilerTest { StackHandleScope<1> hs(soa.Self()); Handle loader( hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* c = class_linker->FindClass(soa.Self(), descriptor, loader); + ObjPtr c = class_linker->FindClass(soa.Self(), descriptor, loader); CHECK(c != nullptr); const auto pointer_size = class_linker->GetImagePointerSize(); for (auto& m : c->GetMethods(pointer_size)) { @@ -115,14 +115,14 @@ TEST_F(CompilerDriverTest, DISABLED_LARGE_CompileDexLibCore) { ObjPtr dex_cache = class_linker_->FindDexCache(soa.Self(), dex); EXPECT_EQ(dex.NumStringIds(), dex_cache->NumStrings()); for (size_t i = 0; i < dex_cache->NumStrings(); i++) { - const mirror::String* string = dex_cache->GetResolvedString(dex::StringIndex(i)); + const ObjPtr string = dex_cache->GetResolvedString(dex::StringIndex(i)); EXPECT_TRUE(string != nullptr) << "string_idx=" << i; } EXPECT_EQ(dex.NumTypeIds(), dex_cache->NumResolvedTypes()); for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) { - mirror::Class* type = dex_cache->GetResolvedType(dex::TypeIndex(i)); - EXPECT_TRUE(type != nullptr) << "type_idx=" << i - << " " << dex.GetTypeDescriptor(dex.GetTypeId(dex::TypeIndex(i))); + const ObjPtr type = dex_cache->GetResolvedType(dex::TypeIndex(i)); + EXPECT_TRUE(type != nullptr) + << "type_idx=" << i << " " << dex.GetTypeDescriptor(dex.GetTypeId(dex::TypeIndex(i))); } EXPECT_TRUE(dex_cache->StaticMethodSize() == dex_cache->NumResolvedMethods() || dex.NumMethodIds() == dex_cache->NumResolvedMethods()); @@ -184,59 +184,6 @@ TEST_F(CompilerDriverTest, AbstractMethodErrorStub) { } } -class CompilerDriverMethodsTest : public CompilerDriverTest { - protected: - std::unordered_set* GetCompiledMethods() OVERRIDE { - return new std::unordered_set({ - "byte StaticLeafMethods.identity(byte)", - "int StaticLeafMethods.sum(int, int, int)", - "double StaticLeafMethods.sum(double, double, double, double)" - }); - } -}; - -TEST_F(CompilerDriverMethodsTest, Selection) { - Thread* self = Thread::Current(); - jobject class_loader; - { - ScopedObjectAccess soa(self); - class_loader = LoadDex("StaticLeafMethods"); - } - ASSERT_NE(class_loader, nullptr); - - // Need to enable dex-file writability. Methods rejected to be compiled will run through the - // dex-to-dex compiler. - for (const DexFile* dex_file : GetDexFiles(class_loader)) { - ASSERT_TRUE(dex_file->EnableWrite()); - } - - CompileAll(class_loader); - - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - ScopedObjectAccess soa(self); - StackHandleScope<1> hs(self); - Handle h_loader( - hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* klass = class_linker->FindClass(self, "LStaticLeafMethods;", h_loader); - ASSERT_NE(klass, nullptr); - - std::unique_ptr> expected(GetCompiledMethods()); - - const auto pointer_size = class_linker->GetImagePointerSize(); - for (auto& m : klass->GetDirectMethods(pointer_size)) { - std::string name = m.PrettyMethod(true); - const void* code = m.GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); - ASSERT_NE(code, nullptr); - if (expected->find(name) != expected->end()) { - expected->erase(name); - EXPECT_FALSE(class_linker->IsQuickToInterpreterBridge(code)); - } else { - EXPECT_TRUE(class_linker->IsQuickToInterpreterBridge(code)); - } - } - EXPECT_TRUE(expected->empty()); -} - class CompilerDriverProfileTest : public CompilerDriverTest { protected: ProfileCompilationInfo* GetProfileCompilationInfo() OVERRIDE { @@ -281,7 +228,7 @@ class CompilerDriverProfileTest : public CompilerDriverTest { StackHandleScope<1> hs(self); Handle h_loader( hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* klass = class_linker->FindClass(self, clazz.c_str(), h_loader); + ObjPtr klass = class_linker->FindClass(self, clazz.c_str(), h_loader); ASSERT_NE(klass, nullptr); const auto pointer_size = class_linker->GetImagePointerSize(); @@ -342,7 +289,7 @@ class CompilerDriverVerifyTest : public CompilerDriverTest { StackHandleScope<1> hs(self); Handle h_loader( hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* klass = class_linker->FindClass(self, clazz.c_str(), h_loader); + ObjPtr klass = class_linker->FindClass(self, clazz.c_str(), h_loader); ASSERT_NE(klass, nullptr); EXPECT_TRUE(klass->IsVerified()); @@ -384,7 +331,7 @@ TEST_F(CompilerDriverVerifyTest, RetryVerifcationStatusCheckVerified) { ASSERT_GT(dex_files.size(), 0u); dex_file = dex_files.front(); } - compiler_driver_->SetDexFilesForOatFile(dex_files); + SetDexFilesForOatFile(dex_files); callbacks_->SetDoesClassUnloading(true, compiler_driver_.get()); ClassReference ref(dex_file, 0u); // Test that the status is read from the compiler driver as expected. diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc index 2d82d79c4ac1515c6640d6b1e1455159f9008f01..62d547de44768843aa3e0abf3bf3a14569217c7e 100644 --- a/compiler/driver/compiler_options.cc +++ b/compiler/driver/compiler_options.cc @@ -20,6 +20,8 @@ #include "android-base/stringprintf.h" +#include "arch/instruction_set.h" +#include "arch/instruction_set_features.h" #include "base/runtime_debug.h" #include "base/variant_map.h" #include "cmdline_parser.h" @@ -37,11 +39,14 @@ CompilerOptions::CompilerOptions() tiny_method_threshold_(kDefaultTinyMethodThreshold), num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold), inline_max_code_units_(kUnsetInlineMaxCodeUnits), - no_inline_from_(nullptr), + instruction_set_(kRuntimeISA == InstructionSet::kArm ? InstructionSet::kThumb2 : kRuntimeISA), + instruction_set_features_(nullptr), + no_inline_from_(), + dex_files_for_oat_file_(), + image_classes_(), boot_image_(false), core_image_(false), app_image_(false), - top_k_profile_threshold_(kDefaultTopKProfileThreshold), debuggable_(false), generate_debug_info_(kDefaultGenerateDebugInfo), generate_mini_debug_info_(kDefaultGenerateMiniDebugInfo), @@ -51,7 +56,9 @@ CompilerOptions::CompilerOptions() implicit_suspend_checks_(false), compile_pic_(false), dump_timings_(false), + dump_pass_timings_(false), dump_stats_(false), + top_k_profile_threshold_(kDefaultTopKProfileThreshold), verbose_methods_(), abort_on_hard_verifier_failure_(false), abort_on_soft_verifier_failure_(false), @@ -66,8 +73,8 @@ CompilerOptions::CompilerOptions() } CompilerOptions::~CompilerOptions() { - // The destructor looks empty but it destroys a PassManagerOptions object. We keep it here - // because we don't want to include the PassManagerOptions definition from the header file. + // Everything done by member destructors. + // The definitions of classes forward-declared in the header have now been #included. } namespace { @@ -128,4 +135,11 @@ bool CompilerOptions::ParseCompilerOptions(const std::vector& optio #pragma GCC diagnostic pop +bool CompilerOptions::IsImageClass(const char* descriptor) const { + // Historical note: We used to hold the set indirectly and there was a distinction between an + // empty set and a null, null meaning to include all classes. However, the distiction has been + // removed; if we don't have a profile, we treat it as an empty set of classes. b/77340429 + return image_classes_.find(StringPiece(descriptor)) != image_classes_.end(); +} + } // namespace art diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index 05d8805e8197d83dd12d8b5121d553c1b9583bc1..601c9140dde5c30e19abe51e1238906123fd0365 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -17,23 +17,31 @@ #ifndef ART_COMPILER_DRIVER_COMPILER_OPTIONS_H_ #define ART_COMPILER_DRIVER_COMPILER_OPTIONS_H_ +#include #include #include #include +#include "base/globals.h" +#include "base/hash_set.h" #include "base/macros.h" #include "base/utils.h" #include "compiler_filter.h" -#include "globals.h" #include "optimizing/register_allocator.h" namespace art { +namespace jit { +class JitCompiler; +} // namespace jit + namespace verifier { class VerifierDepsTest; } // namespace verifier class DexFile; +enum class InstructionSet; +class InstructionSetFeatures; class CompilerOptions FINAL { public: @@ -230,10 +238,29 @@ class CompilerOptions FINAL { return abort_on_soft_verifier_failure_; } - const std::vector* GetNoInlineFromDexFile() const { + InstructionSet GetInstructionSet() const { + return instruction_set_; + } + + const InstructionSetFeatures* GetInstructionSetFeatures() const { + return instruction_set_features_.get(); + } + + + const std::vector& GetNoInlineFromDexFile() const { return no_inline_from_; } + const std::vector& GetDexFilesForOatFile() const { + return dex_files_for_oat_file_; + } + + const HashSet& GetImageClasses() const { + return image_classes_; + } + + bool IsImageClass(const char* descriptor) const; + bool ParseCompilerOptions(const std::vector& options, bool ignore_unrecognized, std::string* error_msg); @@ -270,6 +297,10 @@ class CompilerOptions FINAL { return dump_timings_; } + bool GetDumpPassTimings() const { + return dump_pass_timings_; + } + bool GetDumpStats() const { return dump_stats_; } @@ -297,16 +328,24 @@ class CompilerOptions FINAL { size_t num_dex_methods_threshold_; size_t inline_max_code_units_; - // Dex files from which we should not inline code. + InstructionSet instruction_set_; + std::unique_ptr instruction_set_features_; + + // Dex files from which we should not inline code. Does not own the dex files. // This is usually a very short list (i.e. a single dex file), so we // prefer vector<> over a lookup-oriented container, such as set<>. - const std::vector* no_inline_from_; + std::vector no_inline_from_; + + // List of dex files associated with the oat file, empty for JIT. + std::vector dex_files_for_oat_file_; + + // Image classes, specifies the classes that will be included in the image if creating an image. + // Must not be empty for real boot image, only for tests pretending to compile boot image. + HashSet image_classes_; bool boot_image_; bool core_image_; bool app_image_; - // When using a profile file only the top K% of the profiled samples will be compiled. - double top_k_profile_threshold_; bool debuggable_; bool generate_debug_info_; bool generate_mini_debug_info_; @@ -316,8 +355,12 @@ class CompilerOptions FINAL { bool implicit_suspend_checks_; bool compile_pic_; bool dump_timings_; + bool dump_pass_timings_; bool dump_stats_; + // When using a profile file only the top K% of the profiled samples will be compiled. + double top_k_profile_threshold_; + // Vector of methods to have verbose output enabled for. std::vector verbose_methods_; @@ -357,6 +400,7 @@ class CompilerOptions FINAL { friend class Dex2Oat; friend class DexToDexDecompilerTest; friend class CommonCompilerTest; + friend class jit::JitCompiler; friend class verifier::VerifierDepsTest; template diff --git a/compiler/driver/compiler_options_map-inl.h b/compiler/driver/compiler_options_map-inl.h index 3b18db09fc7049cfd65d6fa3e4abedda54626047..32fc887b8e6144d4f4ae5693c987b1e55c1066bf 100644 --- a/compiler/driver/compiler_options_map-inl.h +++ b/compiler/driver/compiler_options_map-inl.h @@ -85,6 +85,10 @@ inline bool ReadCompilerOptions(Base& map, CompilerOptions* options, std::string options->dump_timings_ = true; } + if (map.Exists(Base::DumpPassTimings)) { + options->dump_pass_timings_ = true; + } + if (map.Exists(Base::DumpStats)) { options->dump_stats_ = true; } @@ -146,6 +150,9 @@ inline void AddCompilerOptionsArgumentParserOptions(Builder& b) { .Define({"--dump-timings"}) .IntoKey(Map::DumpTimings) + .Define({"--dump-pass-timings"}) + .IntoKey(Map::DumpPassTimings) + .Define({"--dump-stats"}) .IntoKey(Map::DumpStats) diff --git a/compiler/driver/compiler_options_map.def b/compiler/driver/compiler_options_map.def index acddae729912ee34ea1e0b9484aaf841158c62b4..529d43fc722d204892947b994c3002a7be31f022 100644 --- a/compiler/driver/compiler_options_map.def +++ b/compiler/driver/compiler_options_map.def @@ -60,6 +60,7 @@ COMPILER_OPTIONS_KEY (ParseStringList<','>, VerboseMethods) COMPILER_OPTIONS_KEY (bool, DeduplicateCode, true) COMPILER_OPTIONS_KEY (Unit, CountHotnessInCompiledCode) COMPILER_OPTIONS_KEY (Unit, DumpTimings) +COMPILER_OPTIONS_KEY (Unit, DumpPassTimings) COMPILER_OPTIONS_KEY (Unit, DumpStats) #undef COMPILER_OPTIONS_KEY diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc index f582341b180531add1e32833884d8606f4cd41fc..b56a991e74c69b4e8b2b859adca4f0372fee9030 100644 --- a/compiler/exception_test.cc +++ b/compiler/exception_test.cc @@ -15,11 +15,13 @@ */ #include +#include #include "base/arena_allocator.h" #include "base/callee_save_type.h" #include "base/enums.h" #include "base/leb128.h" +#include "base/malloc_arena_pool.h" #include "class_linker.h" #include "common_runtime_test.h" #include "dex/code_item_accessors-inl.h" @@ -33,6 +35,7 @@ #include "mirror/object_array-inl.h" #include "mirror/stack_trace_element.h" #include "oat_quick_method_header.h" +#include "obj_ptr-inl.h" #include "optimizing/stack_map_stream.h" #include "runtime-inl.h" #include "scoped_thread_state_change-inl.h" @@ -67,47 +70,36 @@ class ExceptionTest : public CommonRuntimeTest { fake_code_.push_back(0x70 | i); } - ArenaPool pool; + const uint32_t native_pc_offset = 4u; + CHECK_ALIGNED_PARAM(native_pc_offset, GetInstructionSetInstructionAlignment(kRuntimeISA)); + + MallocArenaPool pool; ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stack_maps(&allocator, kRuntimeISA); - stack_maps.BeginStackMapEntry(kDexPc, - /* native_pc_offset */ 3u, - /* register_mask */ 0u, - /* sp_mask */ nullptr, - /* num_dex_registers */ 0u, - /* inlining_depth */ 0u); + stack_maps.BeginMethod(4 * sizeof(void*), 0u, 0u, 0u); + stack_maps.BeginStackMapEntry(kDexPc, native_pc_offset); stack_maps.EndStackMapEntry(); - size_t stack_maps_size = stack_maps.PrepareForFillIn(); - size_t stack_maps_offset = stack_maps_size + sizeof(OatQuickMethodHeader); + stack_maps.EndMethod(); + const size_t stack_maps_size = stack_maps.PrepareForFillIn(); + const size_t header_size = sizeof(OatQuickMethodHeader); + const size_t code_alignment = GetInstructionSetAlignment(kRuntimeISA); + const size_t code_offset = RoundUp(stack_maps_size + header_size, code_alignment); - fake_header_code_and_maps_.resize(stack_maps_offset + fake_code_.size()); + fake_header_code_and_maps_.resize(code_offset + fake_code_.size()); MemoryRegion stack_maps_region(&fake_header_code_and_maps_[0], stack_maps_size); stack_maps.FillInCodeInfo(stack_maps_region); - OatQuickMethodHeader method_header(stack_maps_offset, 0u, 4 * sizeof(void*), 0u, 0u, code_size); - memcpy(&fake_header_code_and_maps_[stack_maps_size], &method_header, sizeof(method_header)); + OatQuickMethodHeader method_header(code_offset, 0u, 4 * sizeof(void*), 0u, 0u, code_size); + static_assert(std::is_trivially_copyable::value, "Cannot use memcpy"); + memcpy(&fake_header_code_and_maps_[code_offset - header_size], &method_header, header_size); std::copy(fake_code_.begin(), fake_code_.end(), - fake_header_code_and_maps_.begin() + stack_maps_offset); - - // Align the code. - const size_t alignment = GetInstructionSetAlignment(kRuntimeISA); - fake_header_code_and_maps_.reserve(fake_header_code_and_maps_.size() + alignment); - const void* unaligned_code_ptr = - fake_header_code_and_maps_.data() + (fake_header_code_and_maps_.size() - code_size); - size_t offset = dchecked_integral_cast(reinterpret_cast(unaligned_code_ptr)); - size_t padding = RoundUp(offset, alignment) - offset; - // Make sure no resizing takes place. - CHECK_GE(fake_header_code_and_maps_.capacity(), fake_header_code_and_maps_.size() + padding); - fake_header_code_and_maps_.insert(fake_header_code_and_maps_.begin(), padding, 0); - const void* code_ptr = reinterpret_cast(unaligned_code_ptr) + padding; - CHECK_EQ(code_ptr, - static_cast(fake_header_code_and_maps_.data() + - (fake_header_code_and_maps_.size() - code_size))); + fake_header_code_and_maps_.begin() + code_offset); + const void* code_ptr = fake_header_code_and_maps_.data() + code_offset; if (kRuntimeISA == InstructionSet::kArm) { // Check that the Thumb2 adjustment will be a NOP, see EntryPointToCodePointer(). - CHECK_ALIGNED(stack_maps_offset, 2); + CHECK_ALIGNED(code_ptr, 2); } method_f_ = my_klass_->FindClassMethod("f", "()I", kRuntimePointerSize); @@ -130,7 +122,7 @@ class ExceptionTest : public CommonRuntimeTest { ArtMethod* method_g_; private: - mirror::Class* my_klass_; + ObjPtr my_klass_; }; TEST_F(ExceptionTest, FindCatchHandler) { diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index ac5c6fb01f8c5c5fe6511b5b603339a855f8d15c..a881c5ec98dbffbe2320cd744d6426f7def427e7 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -33,6 +33,7 @@ #include "jit/debugger_interface.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" +#include "jit/jit_logger.h" #include "oat_file-inl.h" #include "oat_quick_method_header.h" #include "object_lock.h" @@ -50,7 +51,7 @@ extern "C" void* jit_load(bool* generate_debug_info) { VLOG(jit) << "loading jit compiler"; auto* const jit_compiler = JitCompiler::Create(); CHECK(jit_compiler != nullptr); - *generate_debug_info = jit_compiler->GetCompilerOptions()->GetGenerateDebugInfo(); + *generate_debug_info = jit_compiler->GetCompilerOptions().GetGenerateDebugInfo(); VLOG(jit) << "Done loading jit compiler"; return jit_compiler; } @@ -72,10 +73,11 @@ extern "C" void jit_types_loaded(void* handle, mirror::Class** types, size_t cou REQUIRES_SHARED(Locks::mutator_lock_) { auto* jit_compiler = reinterpret_cast(handle); DCHECK(jit_compiler != nullptr); - if (jit_compiler->GetCompilerOptions()->GetGenerateDebugInfo()) { + const CompilerOptions& compiler_options = jit_compiler->GetCompilerOptions(); + if (compiler_options.GetGenerateDebugInfo()) { const ArrayRef types_array(types, count); std::vector elf_file = debug::WriteDebugElfFileForClasses( - kRuntimeISA, jit_compiler->GetCompilerDriver()->GetInstructionSetFeatures(), types_array); + kRuntimeISA, compiler_options.GetInstructionSetFeatures(), types_array); MutexLock mu(Thread::Current(), *Locks::native_debug_interface_lock_); // We never free debug info for types, so we don't need to provide a handle // (which would have been otherwise used as identifier to remove it later). @@ -103,53 +105,56 @@ JitCompiler::JitCompiler() { // Set debuggability based on the runtime value. compiler_options_->SetDebuggable(Runtime::Current()->IsJavaDebuggable()); - const InstructionSet instruction_set = kRuntimeISA; + const InstructionSet instruction_set = compiler_options_->GetInstructionSet(); + if (kRuntimeISA == InstructionSet::kArm) { + DCHECK_EQ(instruction_set, InstructionSet::kThumb2); + } else { + DCHECK_EQ(instruction_set, kRuntimeISA); + } + std::unique_ptr instruction_set_features; for (const StringPiece option : Runtime::Current()->GetCompilerOptions()) { VLOG(compiler) << "JIT compiler option " << option; std::string error_msg; if (option.starts_with("--instruction-set-variant=")) { StringPiece str = option.substr(strlen("--instruction-set-variant=")).data(); VLOG(compiler) << "JIT instruction set variant " << str; - instruction_set_features_ = InstructionSetFeatures::FromVariant( + instruction_set_features = InstructionSetFeatures::FromVariant( instruction_set, str.as_string(), &error_msg); - if (instruction_set_features_ == nullptr) { + if (instruction_set_features == nullptr) { LOG(WARNING) << "Error parsing " << option << " message=" << error_msg; } } else if (option.starts_with("--instruction-set-features=")) { StringPiece str = option.substr(strlen("--instruction-set-features=")).data(); VLOG(compiler) << "JIT instruction set features " << str; - if (instruction_set_features_ == nullptr) { - instruction_set_features_ = InstructionSetFeatures::FromVariant( + if (instruction_set_features == nullptr) { + instruction_set_features = InstructionSetFeatures::FromVariant( instruction_set, "default", &error_msg); - if (instruction_set_features_ == nullptr) { + if (instruction_set_features == nullptr) { LOG(WARNING) << "Error parsing " << option << " message=" << error_msg; } } - instruction_set_features_ = - instruction_set_features_->AddFeaturesFromString(str.as_string(), &error_msg); - if (instruction_set_features_ == nullptr) { + instruction_set_features = + instruction_set_features->AddFeaturesFromString(str.as_string(), &error_msg); + if (instruction_set_features == nullptr) { LOG(WARNING) << "Error parsing " << option << " message=" << error_msg; } } } - if (instruction_set_features_ == nullptr) { - instruction_set_features_ = InstructionSetFeatures::FromCppDefines(); + if (instruction_set_features == nullptr) { + instruction_set_features = InstructionSetFeatures::FromCppDefines(); } + compiler_options_->instruction_set_features_ = std::move(instruction_set_features); + compiler_driver_.reset(new CompilerDriver( compiler_options_.get(), /* verification_results */ nullptr, Compiler::kOptimizing, - instruction_set, - instruction_set_features_.get(), /* image_classes */ nullptr, - /* compiled_classes */ nullptr, - /* compiled_methods */ nullptr, /* thread_count */ 1, /* swap_fd */ -1, /* profile_compilation_info */ nullptr)); // Disable dedupe so we can remove compiled methods. compiler_driver_->SetDedupeEnabled(false); - compiler_driver_->SetSupportBootImageFixup(false); size_t thread_count = compiler_driver_->GetThreadCount(); if (compiler_options_->GetGenerateDebugInfo()) { diff --git a/compiler/jit/jit_compiler.h b/compiler/jit/jit_compiler.h index 31dc9e2fe5a2c6f8773c472aaeb15afc8571432d..5840fece2ecde2ec78eda4b76c1f3f8c7f1209c3 100644 --- a/compiler/jit/jit_compiler.h +++ b/compiler/jit/jit_compiler.h @@ -18,18 +18,19 @@ #define ART_COMPILER_JIT_JIT_COMPILER_H_ #include "base/mutex.h" -#include "compiled_method.h" -#include "driver/compiler_driver.h" -#include "driver/compiler_options.h" -#include "jit_logger.h" namespace art { class ArtMethod; -class InstructionSetFeatures; +class CompiledMethod; +class CompilerDriver; +class CompilerOptions; +class Thread; namespace jit { +class JitLogger; + class JitCompiler { public: static JitCompiler* Create(); @@ -39,8 +40,8 @@ class JitCompiler { bool CompileMethod(Thread* self, ArtMethod* method, bool osr) REQUIRES_SHARED(Locks::mutator_lock_); - CompilerOptions* GetCompilerOptions() const { - return compiler_options_.get(); + const CompilerOptions& GetCompilerOptions() const { + return *compiler_options_.get(); } CompilerDriver* GetCompilerDriver() const { return compiler_driver_.get(); @@ -49,7 +50,6 @@ class JitCompiler { private: std::unique_ptr compiler_options_; std::unique_ptr compiler_driver_; - std::unique_ptr instruction_set_features_; std::unique_ptr jit_logger_; JitCompiler(); diff --git a/compiler/jni/jni_cfi_test.cc b/compiler/jni/jni_cfi_test.cc index 236b5c0c2e360eb77a401bf5de4a61841c714383..920a3a8da63d046306dbfdea686a19d5dec237c2 100644 --- a/compiler/jni/jni_cfi_test.cc +++ b/compiler/jni/jni_cfi_test.cc @@ -20,6 +20,7 @@ #include "arch/instruction_set.h" #include "base/arena_allocator.h" #include "base/enums.h" +#include "base/malloc_arena_pool.h" #include "cfi_test.h" #include "gtest/gtest.h" #include "jni/quick/calling_convention.h" @@ -61,7 +62,7 @@ class JNICFITest : public CFITest { const bool is_synchronized = false; const char* shorty = "IIFII"; - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); std::unique_ptr jni_conv( @@ -94,7 +95,11 @@ class JNICFITest : public CFITest { const std::vector& actual_cfi = *(jni_asm->cfi().data()); if (kGenerateExpected) { - GenerateExpected(stdout, isa, isa_str, actual_asm, actual_cfi); + GenerateExpected(stdout, + isa, + isa_str, + ArrayRef(actual_asm), + ArrayRef(actual_cfi)); } else { EXPECT_EQ(expected_asm, actual_asm); EXPECT_EQ(expected_cfi, actual_cfi); diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index 451a90996511c55933f32b3987ac4b033224a9a7..3cb4a652ad513a9d1e740fba4b0bcdedd5f42353 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -21,15 +21,15 @@ #include "art_method-inl.h" #include "base/bit_utils.h" +#include "base/mem_map.h" #include "class_linker.h" #include "common_compiler_test.h" #include "compiler.h" #include "dex/dex_file.h" #include "gtest/gtest.h" #include "indirect_reference_table.h" -#include "java_vm_ext.h" -#include "jni_internal.h" -#include "mem_map.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" @@ -245,7 +245,7 @@ class JniCompilerTest : public CommonCompilerTest { Handle loader( hs.NewHandle(soa.Decode(class_loader))); // Compile the native method before starting the runtime - mirror::Class* c = class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader); + ObjPtr c = class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader); const auto pointer_size = class_linker_->GetImagePointerSize(); ArtMethod* method = c->FindClassMethod(method_name, method_sig, pointer_size); ASSERT_TRUE(method != nullptr) << method_name << " " << method_sig; diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index d001cfe4fc5d207deb444c3c30508ad90adf5a27..62e8e0264f28b7fe0540e340fe30ed4af559b48d 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -27,16 +27,16 @@ #include "base/enums.h" #include "base/logging.h" // For VLOG. #include "base/macros.h" +#include "base/malloc_arena_pool.h" +#include "base/memory_region.h" #include "base/utils.h" #include "calling_convention.h" #include "class_linker.h" #include "debug/dwarf/debug_frame_opcode_writer.h" #include "dex/dex_file-inl.h" -#include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "entrypoints/quick/quick_entrypoints.h" -#include "jni_env_ext.h" -#include "memory_region.h" +#include "jni/jni_env_ext.h" #include "thread.h" #include "utils/arm/managed_register_arm.h" #include "utils/arm64/managed_register_arm64.h" @@ -114,7 +114,7 @@ static ThreadOffset GetJniEntrypointThreadOffset(JniEntrypoint whi // convention. // template -static JniCompiledMethod ArtJniCompileMethodInternal(CompilerDriver* driver, +static JniCompiledMethod ArtJniCompileMethodInternal(const CompilerOptions& compiler_options, uint32_t access_flags, uint32_t method_idx, const DexFile& dex_file) { @@ -123,8 +123,9 @@ static JniCompiledMethod ArtJniCompileMethodInternal(CompilerDriver* driver, const bool is_static = (access_flags & kAccStatic) != 0; const bool is_synchronized = (access_flags & kAccSynchronized) != 0; const char* shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx)); - InstructionSet instruction_set = driver->GetInstructionSet(); - const InstructionSetFeatures* instruction_set_features = driver->GetInstructionSetFeatures(); + InstructionSet instruction_set = compiler_options.GetInstructionSet(); + const InstructionSetFeatures* instruction_set_features = + compiler_options.GetInstructionSetFeatures(); // i.e. if the method was annotated with @FastNative const bool is_fast_native = (access_flags & kAccFastNative) != 0u; @@ -174,7 +175,7 @@ static JniCompiledMethod ArtJniCompileMethodInternal(CompilerDriver* driver, } } - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); // Calling conventions used to iterate over parameters to method @@ -215,7 +216,6 @@ static JniCompiledMethod ArtJniCompileMethodInternal(CompilerDriver* driver, // Assembler that holds generated instructions std::unique_ptr> jni_asm = GetMacroAssembler(&allocator, instruction_set, instruction_set_features); - const CompilerOptions& compiler_options = driver->GetCompilerOptions(); jni_asm->cfi().SetEnabled(compiler_options.GenerateAnyDebugInfo()); jni_asm->SetEmitRunTimeChecksInDebugMode(compiler_options.EmitRunTimeChecksInDebugMode()); @@ -770,16 +770,16 @@ static void SetNativeParameter(JNIMacroAssembler* jni_asm, } } -JniCompiledMethod ArtQuickJniCompileMethod(CompilerDriver* compiler, +JniCompiledMethod ArtQuickJniCompileMethod(const CompilerOptions& compiler_options, uint32_t access_flags, uint32_t method_idx, const DexFile& dex_file) { - if (Is64BitInstructionSet(compiler->GetInstructionSet())) { + if (Is64BitInstructionSet(compiler_options.GetInstructionSet())) { return ArtJniCompileMethodInternal( - compiler, access_flags, method_idx, dex_file); + compiler_options, access_flags, method_idx, dex_file); } else { return ArtJniCompileMethodInternal( - compiler, access_flags, method_idx, dex_file); + compiler_options, access_flags, method_idx, dex_file); } } diff --git a/compiler/jni/quick/jni_compiler.h b/compiler/jni/quick/jni_compiler.h index 11419947a0ed652fcea2e98523577dcc404f2f2a..313fcd361e4249eaba1c2d581ab272cbda2c109d 100644 --- a/compiler/jni/quick/jni_compiler.h +++ b/compiler/jni/quick/jni_compiler.h @@ -25,7 +25,7 @@ namespace art { class ArtMethod; -class CompilerDriver; +class CompilerOptions; class DexFile; class JniCompiledMethod { @@ -62,7 +62,7 @@ class JniCompiledMethod { std::vector cfi_; }; -JniCompiledMethod ArtQuickJniCompileMethod(CompilerDriver* compiler, +JniCompiledMethod ArtQuickJniCompileMethod(const CompilerOptions& compiler_options, uint32_t access_flags, uint32_t method_idx, const DexFile& dex_file); diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc deleted file mode 100644 index 78755176e43fed209627a9528cdb8edb4a2c151d..0000000000000000000000000000000000000000 --- a/compiler/linker/arm/relative_patcher_thumb2.cc +++ /dev/null @@ -1,484 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "linker/arm/relative_patcher_thumb2.h" - -#include - -#include "arch/arm/asm_support_arm.h" -#include "art_method.h" -#include "base/bit_utils.h" -#include "compiled_method.h" -#include "entrypoints/quick/quick_entrypoints_enum.h" -#include "linker/linker_patch.h" -#include "lock_word.h" -#include "mirror/array-inl.h" -#include "mirror/object.h" -#include "read_barrier.h" -#include "utils/arm/assembler_arm_vixl.h" - -namespace art { -namespace linker { - -// PC displacement from patch location; Thumb2 PC is always at instruction address + 4. -static constexpr int32_t kPcDisplacement = 4; - -// Maximum positive and negative displacement for method call measured from the patch location. -// (Signed 25 bit displacement with the last bit 0 has range [-2^24, 2^24-2] measured from -// the Thumb2 PC pointing right after the BL, i.e. 4 bytes later than the patch location.) -constexpr uint32_t kMaxMethodCallPositiveDisplacement = (1u << 24) - 2 + kPcDisplacement; -constexpr uint32_t kMaxMethodCallNegativeDisplacement = (1u << 24) - kPcDisplacement; - -// Maximum positive and negative displacement for a conditional branch measured from the patch -// location. (Signed 21 bit displacement with the last bit 0 has range [-2^20, 2^20-2] measured -// from the Thumb2 PC pointing right after the B.cond, i.e. 4 bytes later than the patch location.) -constexpr uint32_t kMaxBcondPositiveDisplacement = (1u << 20) - 2u + kPcDisplacement; -constexpr uint32_t kMaxBcondNegativeDisplacement = (1u << 20) - kPcDisplacement; - -Thumb2RelativePatcher::Thumb2RelativePatcher(RelativePatcherTargetProvider* provider) - : ArmBaseRelativePatcher(provider, InstructionSet::kThumb2) { -} - -void Thumb2RelativePatcher::PatchCall(std::vector* code, - uint32_t literal_offset, - uint32_t patch_offset, - uint32_t target_offset) { - DCHECK_LE(literal_offset + 4u, code->size()); - DCHECK_EQ(literal_offset & 1u, 0u); - DCHECK_EQ(patch_offset & 1u, 0u); - DCHECK_EQ(target_offset & 1u, 1u); // Thumb2 mode bit. - uint32_t displacement = CalculateMethodCallDisplacement(patch_offset, target_offset & ~1u); - displacement -= kPcDisplacement; // The base PC is at the end of the 4-byte patch. - DCHECK_EQ(displacement & 1u, 0u); - DCHECK((displacement >> 24) == 0u || (displacement >> 24) == 255u); // 25-bit signed. - uint32_t signbit = (displacement >> 31) & 0x1; - uint32_t i1 = (displacement >> 23) & 0x1; - uint32_t i2 = (displacement >> 22) & 0x1; - uint32_t imm10 = (displacement >> 12) & 0x03ff; - uint32_t imm11 = (displacement >> 1) & 0x07ff; - uint32_t j1 = i1 ^ (signbit ^ 1); - uint32_t j2 = i2 ^ (signbit ^ 1); - uint32_t value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm10 << 16) | imm11; - value |= 0xf000d000; // BL - - // Check that we're just overwriting an existing BL. - DCHECK_EQ(GetInsn32(code, literal_offset) & 0xf800d000, 0xf000d000); - // Write the new BL. - SetInsn32(code, literal_offset, value); -} - -void Thumb2RelativePatcher::PatchPcRelativeReference(std::vector* code, - const LinkerPatch& patch, - uint32_t patch_offset, - uint32_t target_offset) { - uint32_t literal_offset = patch.LiteralOffset(); - uint32_t pc_literal_offset = patch.PcInsnOffset(); - uint32_t pc_base = patch_offset + (pc_literal_offset - literal_offset) + 4u /* PC adjustment */; - uint32_t diff = target_offset - pc_base; - - uint32_t insn = GetInsn32(code, literal_offset); - DCHECK_EQ(insn & 0xff7ff0ffu, 0xf2400000u); // MOVW/MOVT, unpatched (imm16 == 0). - uint32_t diff16 = ((insn & 0x00800000u) != 0u) ? (diff >> 16) : (diff & 0xffffu); - uint32_t imm4 = (diff16 >> 12) & 0xfu; - uint32_t imm = (diff16 >> 11) & 0x1u; - uint32_t imm3 = (diff16 >> 8) & 0x7u; - uint32_t imm8 = diff16 & 0xffu; - insn = (insn & 0xfbf08f00u) | (imm << 26) | (imm4 << 16) | (imm3 << 12) | imm8; - SetInsn32(code, literal_offset, insn); -} - -void Thumb2RelativePatcher::PatchBakerReadBarrierBranch(std::vector* code, - const LinkerPatch& patch, - uint32_t patch_offset) { - DCHECK_ALIGNED(patch_offset, 2u); - uint32_t literal_offset = patch.LiteralOffset(); - DCHECK_ALIGNED(literal_offset, 2u); - DCHECK_LT(literal_offset, code->size()); - uint32_t insn = GetInsn32(code, literal_offset); - DCHECK_EQ(insn, 0xf0408000); // BNE +0 (unpatched) - ThunkKey key = GetBakerThunkKey(patch); - if (kIsDebugBuild) { - const uint32_t encoded_data = key.GetCustomValue1(); - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - // Check that the next instruction matches the expected LDR. - switch (kind) { - case BakerReadBarrierKind::kField: { - BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); - if (width == BakerReadBarrierWidth::kWide) { - DCHECK_GE(code->size() - literal_offset, 8u); - uint32_t next_insn = GetInsn32(code, literal_offset + 4u); - // LDR (immediate), encoding T3, with correct base_reg. - CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register. - const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(next_insn & 0xffff0000u, 0xf8d00000u | (base_reg << 16)); - } else { - DCHECK_GE(code->size() - literal_offset, 6u); - uint32_t next_insn = GetInsn16(code, literal_offset + 4u); - // LDR (immediate), encoding T1, with correct base_reg. - CheckValidReg(next_insn & 0x7u); // Check destination register. - const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(next_insn & 0xf838u, 0x6800u | (base_reg << 3)); - } - break; - } - case BakerReadBarrierKind::kArray: { - DCHECK_GE(code->size() - literal_offset, 8u); - uint32_t next_insn = GetInsn32(code, literal_offset + 4u); - // LDR (register) with correct base_reg, S=1 and option=011 (LDR Wt, [Xn, Xm, LSL #2]). - CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register. - const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(next_insn & 0xffff0ff0u, 0xf8500020u | (base_reg << 16)); - CheckValidReg(next_insn & 0xf); // Check index register - break; - } - case BakerReadBarrierKind::kGcRoot: { - BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); - if (width == BakerReadBarrierWidth::kWide) { - DCHECK_GE(literal_offset, 4u); - uint32_t prev_insn = GetInsn32(code, literal_offset - 4u); - // LDR (immediate), encoding T3, with correct root_reg. - const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(prev_insn & 0xfff0f000u, 0xf8d00000u | (root_reg << 12)); - } else { - DCHECK_GE(literal_offset, 2u); - uint32_t prev_insn = GetInsn16(code, literal_offset - 2u); - // LDR (immediate), encoding T1, with correct root_reg. - const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(prev_insn & 0xf807u, 0x6800u | root_reg); - } - break; - } - default: - LOG(FATAL) << "Unexpected type: " << static_cast(key.GetType()); - UNREACHABLE(); - } - } - uint32_t target_offset = GetThunkTargetOffset(key, patch_offset); - DCHECK_ALIGNED(target_offset, 4u); - uint32_t disp = target_offset - (patch_offset + kPcDisplacement); - DCHECK((disp >> 20) == 0u || (disp >> 20) == 0xfffu); // 21-bit signed. - insn |= ((disp << (26 - 20)) & 0x04000000u) | // Shift bit 20 to 26, "S". - ((disp >> (19 - 11)) & 0x00000800u) | // Shift bit 19 to 13, "J1". - ((disp >> (18 - 13)) & 0x00002000u) | // Shift bit 18 to 11, "J2". - ((disp << (16 - 12)) & 0x003f0000u) | // Shift bits 12-17 to 16-25, "imm6". - ((disp >> (1 - 0)) & 0x000007ffu); // Shift bits 1-12 to 0-11, "imm11". - SetInsn32(code, literal_offset, insn); -} - -#define __ assembler.GetVIXLAssembler()-> - -static void EmitGrayCheckAndFastPath(arm::ArmVIXLAssembler& assembler, - vixl::aarch32::Register base_reg, - vixl::aarch32::MemOperand& lock_word, - vixl::aarch32::Label* slow_path, - int32_t raw_ldr_offset) { - using namespace vixl::aarch32; // NOLINT(build/namespaces) - // Load the lock word containing the rb_state. - __ Ldr(ip, lock_word); - // Given the numeric representation, it's enough to check the low bit of the rb_state. - static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0"); - static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1"); - __ Tst(ip, Operand(LockWord::kReadBarrierStateMaskShifted)); - __ B(ne, slow_path, /* is_far_target */ false); - __ Add(lr, lr, raw_ldr_offset); - // Introduce a dependency on the lock_word including rb_state, - // to prevent load-load reordering, and without using - // a memory barrier (which would be more expensive). - __ Add(base_reg, base_reg, Operand(ip, LSR, 32)); - __ Bx(lr); // And return back to the function. - // Note: The fake dependency is unnecessary for the slow path. -} - -// Load the read barrier introspection entrypoint in register `entrypoint` -static void LoadReadBarrierMarkIntrospectionEntrypoint(arm::ArmVIXLAssembler& assembler, - vixl::aarch32::Register entrypoint) { - using vixl::aarch32::MemOperand; - using vixl::aarch32::ip; - // Thread Register. - const vixl::aarch32::Register tr = vixl::aarch32::r9; - - // The register where the read barrier introspection entrypoint is loaded - // is fixed: `Thumb2RelativePatcher::kBakerCcEntrypointRegister` (R4). - DCHECK_EQ(entrypoint.GetCode(), Thumb2RelativePatcher::kBakerCcEntrypointRegister); - // entrypoint = Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection. - DCHECK_EQ(ip.GetCode(), 12u); - const int32_t entry_point_offset = - Thread::ReadBarrierMarkEntryPointsOffset(ip.GetCode()); - __ Ldr(entrypoint, MemOperand(tr, entry_point_offset)); -} - -void Thumb2RelativePatcher::CompileBakerReadBarrierThunk(arm::ArmVIXLAssembler& assembler, - uint32_t encoded_data) { - using namespace vixl::aarch32; // NOLINT(build/namespaces) - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - switch (kind) { - case BakerReadBarrierKind::kField: { - // Check if the holder is gray and, if not, add fake dependency to the base register - // and return to the LDR instruction to load the reference. Otherwise, use introspection - // to load the reference and call the entrypoint (in kBakerCcEntrypointRegister) - // that performs further checks on the reference and marks it if needed. - Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(base_reg.GetCode()); - Register holder_reg(BakerReadBarrierSecondRegField::Decode(encoded_data)); - CheckValidReg(holder_reg.GetCode()); - BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip); - // If base_reg differs from holder_reg, the offset was too large and we must have - // emitted an explicit null check before the load. Otherwise, we need to null-check - // the holder as we do not necessarily do that check before going to the thunk. - vixl::aarch32::Label throw_npe; - if (holder_reg.Is(base_reg)) { - __ CompareAndBranchIfZero(holder_reg, &throw_npe, /* is_far_target */ false); - } - vixl::aarch32::Label slow_path; - MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value()); - const int32_t raw_ldr_offset = (width == BakerReadBarrierWidth::kWide) - ? BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET - : BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET; - EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset); - __ Bind(&slow_path); - const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 + - raw_ldr_offset; - Register ep_reg(kBakerCcEntrypointRegister); - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); - if (width == BakerReadBarrierWidth::kWide) { - MemOperand ldr_half_address(lr, ldr_offset + 2); - __ Ldrh(ip, ldr_half_address); // Load the LDR immediate half-word with "Rt | imm12". - __ Ubfx(ip, ip, 0, 12); // Extract the offset imm12. - __ Ldr(ip, MemOperand(base_reg, ip)); // Load the reference. - } else { - MemOperand ldr_address(lr, ldr_offset); - __ Ldrh(ip, ldr_address); // Load the LDR immediate, encoding T1. - __ Add(ep_reg, // Adjust the entrypoint address to the entrypoint - ep_reg, // for narrow LDR. - Operand(BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET)); - __ Ubfx(ip, ip, 6, 5); // Extract the imm5, i.e. offset / 4. - __ Ldr(ip, MemOperand(base_reg, ip, LSL, 2)); // Load the reference. - } - // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference. - __ Bx(ep_reg); // Jump to the entrypoint. - if (holder_reg.Is(base_reg)) { - // Add null check slow path. The stack map is at the address pointed to by LR. - __ Bind(&throw_npe); - int32_t offset = GetThreadOffset(kQuickThrowNullPointer).Int32Value(); - __ Ldr(ip, MemOperand(/* Thread* */ vixl::aarch32::r9, offset)); - __ Bx(ip); - } - break; - } - case BakerReadBarrierKind::kArray: { - Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(base_reg.GetCode()); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip); - vixl::aarch32::Label slow_path; - int32_t data_offset = - mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value(); - MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset); - DCHECK_LT(lock_word.GetOffsetImmediate(), 0); - const int32_t raw_ldr_offset = BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET; - EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset); - __ Bind(&slow_path); - const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 + - raw_ldr_offset; - MemOperand ldr_address(lr, ldr_offset + 2); - __ Ldrb(ip, ldr_address); // Load the LDR (register) byte with "00 | imm2 | Rm", - // i.e. Rm+32 because the scale in imm2 is 2. - Register ep_reg(kBakerCcEntrypointRegister); - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); - __ Bfi(ep_reg, ip, 3, 6); // Insert ip to the entrypoint address to create - // a switch case target based on the index register. - __ Mov(ip, base_reg); // Move the base register to ip0. - __ Bx(ep_reg); // Jump to the entrypoint's array switch case. - break; - } - case BakerReadBarrierKind::kGcRoot: { - // Check if the reference needs to be marked and if so (i.e. not null, not marked yet - // and it does not have a forwarding address), call the correct introspection entrypoint; - // otherwise return the reference (or the extracted forwarding address). - // There is no gray bit check for GC roots. - Register root_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(root_reg.GetCode()); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip); - vixl::aarch32::Label return_label, not_marked, forwarding_address; - __ CompareAndBranchIfZero(root_reg, &return_label, /* is_far_target */ false); - MemOperand lock_word(root_reg, mirror::Object::MonitorOffset().Int32Value()); - __ Ldr(ip, lock_word); - __ Tst(ip, LockWord::kMarkBitStateMaskShifted); - __ B(eq, ¬_marked); - __ Bind(&return_label); - __ Bx(lr); - __ Bind(¬_marked); - static_assert(LockWord::kStateShift == 30 && LockWord::kStateForwardingAddress == 3, - "To use 'CMP ip, #modified-immediate; BHS', we need the lock word state in " - " the highest bits and the 'forwarding address' state to have all bits set"); - __ Cmp(ip, Operand(0xc0000000)); - __ B(hs, &forwarding_address); - Register ep_reg(kBakerCcEntrypointRegister); - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); - // Adjust the art_quick_read_barrier_mark_introspection address in kBakerCcEntrypointRegister - // to art_quick_read_barrier_mark_introspection_gc_roots. - int32_t entrypoint_offset = (width == BakerReadBarrierWidth::kWide) - ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET - : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET; - __ Add(ep_reg, ep_reg, Operand(entrypoint_offset)); - __ Mov(ip, root_reg); - __ Bx(ep_reg); - __ Bind(&forwarding_address); - __ Lsl(root_reg, ip, LockWord::kForwardingAddressShift); - __ Bx(lr); - break; - } - default: - LOG(FATAL) << "Unexpected kind: " << static_cast(kind); - UNREACHABLE(); - } -} - -std::vector Thumb2RelativePatcher::CompileThunk(const ThunkKey& key) { - ArenaPool pool; - ArenaAllocator allocator(&pool); - arm::ArmVIXLAssembler assembler(&allocator); - - switch (key.GetType()) { - case ThunkType::kMethodCall: - // The thunk just uses the entry point in the ArtMethod. This works even for calls - // to the generic JNI and interpreter trampolines. - assembler.LoadFromOffset( - arm::kLoadWord, - vixl::aarch32::pc, - vixl::aarch32::r0, - ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value()); - __ Bkpt(0); - break; - case ThunkType::kBakerReadBarrier: - CompileBakerReadBarrierThunk(assembler, key.GetCustomValue1()); - break; - } - - assembler.FinalizeCode(); - std::vector thunk_code(assembler.CodeSize()); - MemoryRegion code(thunk_code.data(), thunk_code.size()); - assembler.FinalizeInstructions(code); - return thunk_code; -} - -std::string Thumb2RelativePatcher::GetThunkDebugName(const ThunkKey& key) { - switch (key.GetType()) { - case ThunkType::kMethodCall: - return "MethodCallThunk"; - - case ThunkType::kBakerReadBarrier: { - uint32_t encoded_data = key.GetCustomValue1(); - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - std::ostringstream oss; - oss << "BakerReadBarrierThunk"; - switch (kind) { - case BakerReadBarrierKind::kField: - oss << "Field"; - if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) { - oss << "Wide"; - } - oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data) - << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data); - break; - case BakerReadBarrierKind::kArray: - oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide); - break; - case BakerReadBarrierKind::kGcRoot: - oss << "GcRoot"; - if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) { - oss << "Wide"; - } - oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - break; - } - return oss.str(); - } - } -} - -#undef __ - -uint32_t Thumb2RelativePatcher::MaxPositiveDisplacement(const ThunkKey& key) { - switch (key.GetType()) { - case ThunkType::kMethodCall: - return kMaxMethodCallPositiveDisplacement; - case ThunkType::kBakerReadBarrier: - return kMaxBcondPositiveDisplacement; - } -} - -uint32_t Thumb2RelativePatcher::MaxNegativeDisplacement(const ThunkKey& key) { - switch (key.GetType()) { - case ThunkType::kMethodCall: - return kMaxMethodCallNegativeDisplacement; - case ThunkType::kBakerReadBarrier: - return kMaxBcondNegativeDisplacement; - } -} - -void Thumb2RelativePatcher::SetInsn32(std::vector* code, uint32_t offset, uint32_t value) { - DCHECK_LE(offset + 4u, code->size()); - DCHECK_ALIGNED(offset, 2u); - uint8_t* addr = &(*code)[offset]; - addr[0] = (value >> 16) & 0xff; - addr[1] = (value >> 24) & 0xff; - addr[2] = (value >> 0) & 0xff; - addr[3] = (value >> 8) & 0xff; -} - -uint32_t Thumb2RelativePatcher::GetInsn32(ArrayRef code, uint32_t offset) { - DCHECK_LE(offset + 4u, code.size()); - DCHECK_ALIGNED(offset, 2u); - const uint8_t* addr = &code[offset]; - return - (static_cast(addr[0]) << 16) + - (static_cast(addr[1]) << 24) + - (static_cast(addr[2]) << 0)+ - (static_cast(addr[3]) << 8); -} - -template -uint32_t Thumb2RelativePatcher::GetInsn32(Vector* code, uint32_t offset) { - static_assert(std::is_same::value, "Invalid value type"); - return GetInsn32(ArrayRef(*code), offset); -} - -uint32_t Thumb2RelativePatcher::GetInsn16(ArrayRef code, uint32_t offset) { - DCHECK_LE(offset + 2u, code.size()); - DCHECK_ALIGNED(offset, 2u); - const uint8_t* addr = &code[offset]; - return (static_cast(addr[0]) << 0) + (static_cast(addr[1]) << 8); -} - -template -uint32_t Thumb2RelativePatcher::GetInsn16(Vector* code, uint32_t offset) { - static_assert(std::is_same::value, "Invalid value type"); - return GetInsn16(ArrayRef(*code), offset); -} - -} // namespace linker -} // namespace art diff --git a/compiler/linker/arm/relative_patcher_thumb2.h b/compiler/linker/arm/relative_patcher_thumb2.h deleted file mode 100644 index 68386c00f4a0dd5090c15e261560d581caeb03cc..0000000000000000000000000000000000000000 --- a/compiler/linker/arm/relative_patcher_thumb2.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_THUMB2_H_ -#define ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_THUMB2_H_ - -#include "arch/arm/registers_arm.h" -#include "base/array_ref.h" -#include "base/bit_field.h" -#include "base/bit_utils.h" -#include "linker/arm/relative_patcher_arm_base.h" - -namespace art { - -namespace arm { -class ArmVIXLAssembler; -} // namespace arm - -namespace linker { - -class Thumb2RelativePatcher FINAL : public ArmBaseRelativePatcher { - public: - static constexpr uint32_t kBakerCcEntrypointRegister = 4u; - - static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, - uint32_t holder_reg, - bool narrow) { - CheckValidReg(base_reg); - CheckValidReg(holder_reg); - DCHECK(!narrow || base_reg < 8u) << base_reg; - BakerReadBarrierWidth width = - narrow ? BakerReadBarrierWidth::kNarrow : BakerReadBarrierWidth::kWide; - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kField) | - BakerReadBarrierFirstRegField::Encode(base_reg) | - BakerReadBarrierSecondRegField::Encode(holder_reg) | - BakerReadBarrierWidthField::Encode(width); - } - - static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { - CheckValidReg(base_reg); - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kArray) | - BakerReadBarrierFirstRegField::Encode(base_reg) | - BakerReadBarrierSecondRegField::Encode(kInvalidEncodedReg) | - BakerReadBarrierWidthField::Encode(BakerReadBarrierWidth::kWide); - } - - static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg, bool narrow) { - CheckValidReg(root_reg); - DCHECK(!narrow || root_reg < 8u) << root_reg; - BakerReadBarrierWidth width = - narrow ? BakerReadBarrierWidth::kNarrow : BakerReadBarrierWidth::kWide; - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kGcRoot) | - BakerReadBarrierFirstRegField::Encode(root_reg) | - BakerReadBarrierSecondRegField::Encode(kInvalidEncodedReg) | - BakerReadBarrierWidthField::Encode(width); - } - - explicit Thumb2RelativePatcher(RelativePatcherTargetProvider* provider); - - void PatchCall(std::vector* code, - uint32_t literal_offset, - uint32_t patch_offset, - uint32_t target_offset) OVERRIDE; - void PatchPcRelativeReference(std::vector* code, - const LinkerPatch& patch, - uint32_t patch_offset, - uint32_t target_offset) OVERRIDE; - void PatchBakerReadBarrierBranch(std::vector* code, - const LinkerPatch& patch, - uint32_t patch_offset) OVERRIDE; - - protected: - std::vector CompileThunk(const ThunkKey& key) OVERRIDE; - std::string GetThunkDebugName(const ThunkKey& key) OVERRIDE; - uint32_t MaxPositiveDisplacement(const ThunkKey& key) OVERRIDE; - uint32_t MaxNegativeDisplacement(const ThunkKey& key) OVERRIDE; - - private: - static constexpr uint32_t kInvalidEncodedReg = /* pc is invalid */ 15u; - - enum class BakerReadBarrierKind : uint8_t { - kField, // Field get or array get with constant offset (i.e. constant index). - kArray, // Array get with index in register. - kGcRoot, // GC root load. - kLast = kGcRoot - }; - - enum class BakerReadBarrierWidth : uint8_t { - kWide, // 32-bit LDR (and 32-bit NEG if heap poisoning is enabled). - kNarrow, // 16-bit LDR (and 16-bit NEG if heap poisoning is enabled). - kLast = kNarrow - }; - - static constexpr size_t kBitsForBakerReadBarrierKind = - MinimumBitsToStore(static_cast(BakerReadBarrierKind::kLast)); - static constexpr size_t kBitsForRegister = 4u; - using BakerReadBarrierKindField = - BitField; - using BakerReadBarrierFirstRegField = - BitField; - using BakerReadBarrierSecondRegField = - BitField; - static constexpr size_t kBitsForBakerReadBarrierWidth = - MinimumBitsToStore(static_cast(BakerReadBarrierWidth::kLast)); - using BakerReadBarrierWidthField = BitField; - - static void CheckValidReg(uint32_t reg) { - DCHECK(reg < 12u && reg != kBakerCcEntrypointRegister) << reg; - } - - void CompileBakerReadBarrierThunk(arm::ArmVIXLAssembler& assembler, uint32_t encoded_data); - - void SetInsn32(std::vector* code, uint32_t offset, uint32_t value); - static uint32_t GetInsn32(ArrayRef code, uint32_t offset); - - template - static uint32_t GetInsn32(Vector* code, uint32_t offset); - - static uint32_t GetInsn16(ArrayRef code, uint32_t offset); - - template - static uint32_t GetInsn16(Vector* code, uint32_t offset); - - friend class Thumb2RelativePatcherTest; - - DISALLOW_COPY_AND_ASSIGN(Thumb2RelativePatcher); -}; - -} // namespace linker -} // namespace art - -#endif // ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_THUMB2_H_ diff --git a/compiler/linker/buffered_output_stream.h b/compiler/linker/buffered_output_stream.h index 66994e82a1c90ccd86c63bd56cf9e736577e5bd4..512409cb2f5409ba225f329f20abc58da884d386 100644 --- a/compiler/linker/buffered_output_stream.h +++ b/compiler/linker/buffered_output_stream.h @@ -21,7 +21,7 @@ #include "output_stream.h" -#include "globals.h" +#include "base/globals.h" namespace art { namespace linker { diff --git a/compiler/linker/elf_builder.h b/compiler/linker/elf_builder.h index a5f60992cae5e18765fb8ea5c33c005d4c3ab77f..3da7a43762742460b60abfd7519649d10c28ab5f 100644 --- a/compiler/linker/elf_builder.h +++ b/compiler/linker/elf_builder.h @@ -529,6 +529,8 @@ class ElfBuilder FINAL { stream_(output), rodata_(this, ".rodata", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), text_(this, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, nullptr, 0, kPageSize, 0), + data_bimg_rel_ro_( + this, ".data.bimg.rel.ro", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), bss_(this, ".bss", SHT_NOBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), dex_(this, ".dex", SHT_NOBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), dynstr_(this, ".dynstr", SHF_ALLOC, kPageSize), @@ -552,6 +554,7 @@ class ElfBuilder FINAL { loaded_size_(0u), virtual_address_(0) { text_.phdr_flags_ = PF_R | PF_X; + data_bimg_rel_ro_.phdr_flags_ = PF_R | PF_W; // Shall be made read-only at run time. bss_.phdr_flags_ = PF_R | PF_W; dex_.phdr_flags_ = PF_R; dynamic_.phdr_flags_ = PF_R | PF_W; @@ -566,6 +569,7 @@ class ElfBuilder FINAL { BuildIdSection* GetBuildId() { return &build_id_; } Section* GetRoData() { return &rodata_; } Section* GetText() { return &text_; } + Section* GetDataBimgRelRo() { return &data_bimg_rel_ro_; } Section* GetBss() { return &bss_; } Section* GetDex() { return &dex_; } StringSection* GetStrTab() { return &strtab_; } @@ -694,6 +698,7 @@ class ElfBuilder FINAL { void PrepareDynamicSection(const std::string& elf_file_path, Elf_Word rodata_size, Elf_Word text_size, + Elf_Word data_bimg_rel_ro_size, Elf_Word bss_size, Elf_Word bss_methods_offset, Elf_Word bss_roots_offset, @@ -707,6 +712,9 @@ class ElfBuilder FINAL { // Allocate all pre-dynamic sections. rodata_.AllocateVirtualMemory(rodata_size); text_.AllocateVirtualMemory(text_size); + if (data_bimg_rel_ro_size != 0) { + data_bimg_rel_ro_.AllocateVirtualMemory(data_bimg_rel_ro_size); + } if (bss_size != 0) { bss_.AllocateVirtualMemory(bss_size); } @@ -735,6 +743,24 @@ class ElfBuilder FINAL { Elf_Word oatlastword_address = rodata_.GetAddress() + rodata_size - 4; dynsym_.Add(oatlastword, &rodata_, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT); } + if (data_bimg_rel_ro_size != 0u) { + Elf_Word oatdatabimgrelro = dynstr_.Add("oatdatabimgrelro"); + dynsym_.Add(oatdatabimgrelro, + &data_bimg_rel_ro_, + data_bimg_rel_ro_.GetAddress(), + data_bimg_rel_ro_size, + STB_GLOBAL, + STT_OBJECT); + Elf_Word oatdatabimgrelrolastword = dynstr_.Add("oatdatabimgrelrolastword"); + Elf_Word oatdatabimgrelrolastword_address = + data_bimg_rel_ro_.GetAddress() + data_bimg_rel_ro_size - 4; + dynsym_.Add(oatdatabimgrelrolastword, + &data_bimg_rel_ro_, + oatdatabimgrelrolastword_address, + 4, + STB_GLOBAL, + STT_OBJECT); + } DCHECK_LE(bss_roots_offset, bss_size); if (bss_size != 0u) { Elf_Word oatbss = dynstr_.Add("oatbss"); @@ -1010,6 +1036,7 @@ class ElfBuilder FINAL { Section rodata_; Section text_; + Section data_bimg_rel_ro_; Section bss_; Section dex_; CachedStringSection dynstr_; diff --git a/compiler/linker/linker_patch.h b/compiler/linker/linker_patch.h index 77d689d4dbb06ae525c294bd2c3225c3247f3ec1..b7beb7bdb407fc1d81041d942979f3e296c7ad3a 100644 --- a/compiler/linker/linker_patch.h +++ b/compiler/linker/linker_patch.h @@ -40,20 +40,40 @@ class LinkerPatch { // which is ridiculous given we have only a handful of values here. If we // choose to squeeze the Type into fewer than 8 bits, we'll have to declare // patch_type_ as an uintN_t and do explicit static_cast<>s. + // + // Note: Actual patching is instruction_set-dependent. enum class Type : uint8_t { - kMethodRelative, // NOTE: Actual patching is instruction_set-dependent. - kMethodBssEntry, // NOTE: Actual patching is instruction_set-dependent. - kCall, - kCallRelative, // NOTE: Actual patching is instruction_set-dependent. - kTypeRelative, // NOTE: Actual patching is instruction_set-dependent. - kTypeClassTable, // NOTE: Actual patching is instruction_set-dependent. - kTypeBssEntry, // NOTE: Actual patching is instruction_set-dependent. - kStringRelative, // NOTE: Actual patching is instruction_set-dependent. - kStringInternTable, // NOTE: Actual patching is instruction_set-dependent. - kStringBssEntry, // NOTE: Actual patching is instruction_set-dependent. - kBakerReadBarrierBranch, // NOTE: Actual patching is instruction_set-dependent. + kIntrinsicReference, // Boot image reference for an intrinsic, see IntrinsicObjects. + kDataBimgRelRo, + kMethodRelative, + kMethodBssEntry, + kCall, // TODO: Remove. (Deprecated, non-PIC.) + kCallRelative, + kTypeRelative, + kTypeBssEntry, + kStringRelative, + kStringBssEntry, + kBakerReadBarrierBranch, }; + static LinkerPatch IntrinsicReferencePatch(size_t literal_offset, + uint32_t pc_insn_offset, + uint32_t intrinsic_data) { + LinkerPatch patch(literal_offset, Type::kIntrinsicReference, /* target_dex_file */ nullptr); + patch.intrinsic_data_ = intrinsic_data; + patch.pc_insn_offset_ = pc_insn_offset; + return patch; + } + + static LinkerPatch DataBimgRelRoPatch(size_t literal_offset, + uint32_t pc_insn_offset, + uint32_t boot_image_offset) { + LinkerPatch patch(literal_offset, Type::kDataBimgRelRo, /* target_dex_file */ nullptr); + patch.boot_image_offset_ = boot_image_offset; + patch.pc_insn_offset_ = pc_insn_offset; + return patch; + } + static LinkerPatch RelativeMethodPatch(size_t literal_offset, const DexFile* target_dex_file, uint32_t pc_insn_offset, @@ -100,16 +120,6 @@ class LinkerPatch { return patch; } - static LinkerPatch TypeClassTablePatch(size_t literal_offset, - const DexFile* target_dex_file, - uint32_t pc_insn_offset, - uint32_t target_type_idx) { - LinkerPatch patch(literal_offset, Type::kTypeClassTable, target_dex_file); - patch.type_idx_ = target_type_idx; - patch.pc_insn_offset_ = pc_insn_offset; - return patch; - } - static LinkerPatch TypeBssEntryPatch(size_t literal_offset, const DexFile* target_dex_file, uint32_t pc_insn_offset, @@ -130,16 +140,6 @@ class LinkerPatch { return patch; } - static LinkerPatch StringInternTablePatch(size_t literal_offset, - const DexFile* target_dex_file, - uint32_t pc_insn_offset, - uint32_t target_string_idx) { - LinkerPatch patch(literal_offset, Type::kStringInternTable, target_dex_file); - patch.string_idx_ = target_string_idx; - patch.pc_insn_offset_ = pc_insn_offset; - return patch; - } - static LinkerPatch StringBssEntryPatch(size_t literal_offset, const DexFile* target_dex_file, uint32_t pc_insn_offset, @@ -153,7 +153,7 @@ class LinkerPatch { static LinkerPatch BakerReadBarrierBranchPatch(size_t literal_offset, uint32_t custom_value1 = 0u, uint32_t custom_value2 = 0u) { - LinkerPatch patch(literal_offset, Type::kBakerReadBarrierBranch, nullptr); + LinkerPatch patch(literal_offset, Type::kBakerReadBarrierBranch, /* target_dex_file */ nullptr); patch.baker_custom_value1_ = custom_value1; patch.baker_custom_value2_ = custom_value2; return patch; @@ -172,14 +172,14 @@ class LinkerPatch { bool IsPcRelative() const { switch (GetType()) { + case Type::kIntrinsicReference: + case Type::kDataBimgRelRo: case Type::kMethodRelative: case Type::kMethodBssEntry: case Type::kCallRelative: case Type::kTypeRelative: - case Type::kTypeClassTable: case Type::kTypeBssEntry: case Type::kStringRelative: - case Type::kStringInternTable: case Type::kStringBssEntry: case Type::kBakerReadBarrierBranch: return true; @@ -188,6 +188,16 @@ class LinkerPatch { } } + uint32_t IntrinsicData() const { + DCHECK(patch_type_ == Type::kIntrinsicReference); + return intrinsic_data_; + } + + uint32_t BootImageOffset() const { + DCHECK(patch_type_ == Type::kDataBimgRelRo); + return boot_image_offset_; + } + MethodReference TargetMethod() const { DCHECK(patch_type_ == Type::kMethodRelative || patch_type_ == Type::kMethodBssEntry || @@ -198,40 +208,36 @@ class LinkerPatch { const DexFile* TargetTypeDexFile() const { DCHECK(patch_type_ == Type::kTypeRelative || - patch_type_ == Type::kTypeClassTable || patch_type_ == Type::kTypeBssEntry); return target_dex_file_; } dex::TypeIndex TargetTypeIndex() const { DCHECK(patch_type_ == Type::kTypeRelative || - patch_type_ == Type::kTypeClassTable || patch_type_ == Type::kTypeBssEntry); return dex::TypeIndex(type_idx_); } const DexFile* TargetStringDexFile() const { DCHECK(patch_type_ == Type::kStringRelative || - patch_type_ == Type::kStringInternTable || patch_type_ == Type::kStringBssEntry); return target_dex_file_; } dex::StringIndex TargetStringIndex() const { DCHECK(patch_type_ == Type::kStringRelative || - patch_type_ == Type::kStringInternTable || patch_type_ == Type::kStringBssEntry); return dex::StringIndex(string_idx_); } uint32_t PcInsnOffset() const { - DCHECK(patch_type_ == Type::kMethodRelative || + DCHECK(patch_type_ == Type::kIntrinsicReference || + patch_type_ == Type::kDataBimgRelRo || + patch_type_ == Type::kMethodRelative || patch_type_ == Type::kMethodBssEntry || patch_type_ == Type::kTypeRelative || - patch_type_ == Type::kTypeClassTable || patch_type_ == Type::kTypeBssEntry || patch_type_ == Type::kStringRelative || - patch_type_ == Type::kStringInternTable || patch_type_ == Type::kStringBssEntry); return pc_insn_offset_; } @@ -263,14 +269,17 @@ class LinkerPatch { uint32_t literal_offset_ : 24; // Method code size up to 16MiB. Type patch_type_ : 8; union { - uint32_t cmp1_; // Used for relational operators. - uint32_t method_idx_; // Method index for Call/Method patches. - uint32_t type_idx_; // Type index for Type patches. - uint32_t string_idx_; // String index for String patches. + uint32_t cmp1_; // Used for relational operators. + uint32_t boot_image_offset_; // Data to write to the .data.bimg.rel.ro entry. + uint32_t method_idx_; // Method index for Call/Method patches. + uint32_t type_idx_; // Type index for Type patches. + uint32_t string_idx_; // String index for String patches. + uint32_t intrinsic_data_; // Data for IntrinsicObjects. uint32_t baker_custom_value1_; static_assert(sizeof(method_idx_) == sizeof(cmp1_), "needed by relational operators"); static_assert(sizeof(type_idx_) == sizeof(cmp1_), "needed by relational operators"); static_assert(sizeof(string_idx_) == sizeof(cmp1_), "needed by relational operators"); + static_assert(sizeof(intrinsic_data_) == sizeof(cmp1_), "needed by relational operators"); static_assert(sizeof(baker_custom_value1_) == sizeof(cmp1_), "needed by relational operators"); }; union { diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc index d893cc88c448aa5c5f34477f6a019b9a9c61c665..dfefa524bf56ba0260110d6873504e057b75de34 100644 --- a/compiler/optimizing/bounds_check_elimination.cc +++ b/compiler/optimizing/bounds_check_elimination.cc @@ -1938,9 +1938,9 @@ class BCEVisitor : public HGraphVisitor { DISALLOW_COPY_AND_ASSIGN(BCEVisitor); }; -void BoundsCheckElimination::Run() { +bool BoundsCheckElimination::Run() { if (!graph_->HasBoundsChecks()) { - return; + return false; } // Reverse post order guarantees a node's dominators are visited first. @@ -1968,6 +1968,8 @@ void BoundsCheckElimination::Run() { // Perform cleanup. visitor.Finish(); + + return true; } } // namespace art diff --git a/compiler/optimizing/bounds_check_elimination.h b/compiler/optimizing/bounds_check_elimination.h index 79c67a8c7ade8dde7775f5ed44a9f71466030a5e..92ab7984c80bcb115b6cf3ce02b57807f7433d31 100644 --- a/compiler/optimizing/bounds_check_elimination.h +++ b/compiler/optimizing/bounds_check_elimination.h @@ -34,7 +34,7 @@ class BoundsCheckElimination : public HOptimization { side_effects_(side_effects), induction_analysis_(induction_analysis) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kBoundsCheckEliminationPassName = "BCE"; diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc index 1523478613bb6d72ee0f1bca966c04ee7b48f542..7c29df877a817366c238e95fef2aee4cbe1e78c4 100644 --- a/compiler/optimizing/bounds_check_elimination_test.cc +++ b/compiler/optimizing/bounds_check_elimination_test.cc @@ -43,7 +43,7 @@ class BoundsCheckEliminationTest : public OptimizingUnitTest { void RunBCE() { graph_->BuildDominatorTree(); - InstructionSimplifier(graph_, /* codegen */ nullptr, /* driver */ nullptr).Run(); + InstructionSimplifier(graph_, /* codegen */ nullptr).Run(); SideEffectsAnalysis side_effects(graph_); side_effects.Run(); diff --git a/compiler/optimizing/cha_guard_optimization.cc b/compiler/optimizing/cha_guard_optimization.cc index 3addaeecd995df56a651d78357a3650e748a854f..bdc395b52dd6a60448c99ec3a266ea9d61f7d2af 100644 --- a/compiler/optimizing/cha_guard_optimization.cc +++ b/compiler/optimizing/cha_guard_optimization.cc @@ -241,14 +241,15 @@ void CHAGuardVisitor::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) { GetGraph()->IncrementNumberOfCHAGuards(); } -void CHAGuardOptimization::Run() { +bool CHAGuardOptimization::Run() { if (graph_->GetNumberOfCHAGuards() == 0) { - return; + return false; } CHAGuardVisitor visitor(graph_); for (HBasicBlock* block : graph_->GetReversePostOrder()) { visitor.VisitBasicBlock(block); } + return true; } } // namespace art diff --git a/compiler/optimizing/cha_guard_optimization.h b/compiler/optimizing/cha_guard_optimization.h index f14e07bd6ccb0273ade8833f52404e7d4c93c848..d2c5a344b72439534b965080db7e95786c6426ea 100644 --- a/compiler/optimizing/cha_guard_optimization.h +++ b/compiler/optimizing/cha_guard_optimization.h @@ -30,7 +30,7 @@ class CHAGuardOptimization : public HOptimization { const char* name = kCHAGuardOptimizationPassName) : HOptimization(graph, name) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kCHAGuardOptimizationPassName = "cha_guard_optimization"; diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 6abda9b30262a2584bb45ffebdd93b2b99431fb2..0ebf4bec0afb5548f1fe50e893c562e393d4211b 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -51,6 +51,8 @@ #include "dex/verified_method.h" #include "driver/compiler_driver.h" #include "graph_visualizer.h" +#include "image.h" +#include "gc/space/image_space.h" #include "intern_table.h" #include "intrinsics.h" #include "mirror/array-inl.h" @@ -61,15 +63,13 @@ #include "parallel_move_resolver.h" #include "scoped_thread_state_change-inl.h" #include "ssa_liveness_analysis.h" +#include "stack_map.h" #include "stack_map_stream.h" #include "thread-current-inl.h" #include "utils/assembler.h" namespace art { -// If true, we record the static and direct invokes in the invoke infos. -static constexpr bool kEnableDexLayoutOptimizations = false; - // Return whether a location is consistent with a type. static bool CheckType(DataType::Type type, Location location) { if (location.IsFpuRegister() @@ -390,6 +390,11 @@ void CodeGenerator::Compile(CodeAllocator* allocator) { HGraphVisitor* instruction_visitor = GetInstructionVisitor(); DCHECK_EQ(current_block_index_, 0u); + GetStackMapStream()->BeginMethod(HasEmptyFrame() ? 0 : frame_size_, + core_spill_mask_, + fpu_spill_mask_, + GetGraph()->GetNumberOfVRegs()); + size_t frame_start = GetAssembler()->CodeSize(); GenerateFrameEntry(); DCHECK_EQ(GetAssembler()->cfi().GetCurrentCFAOffset(), static_cast(frame_size_)); @@ -432,6 +437,8 @@ void CodeGenerator::Compile(CodeAllocator* allocator) { // Finalize instructions in assember; Finalize(allocator); + + GetStackMapStream()->EndMethod(); } void CodeGenerator::Finalize(CodeAllocator* allocator) { @@ -447,6 +454,18 @@ void CodeGenerator::EmitLinkerPatches( // No linker patches by default. } +bool CodeGenerator::NeedsThunkCode(const linker::LinkerPatch& patch ATTRIBUTE_UNUSED) const { + // Code generators that create patches requiring thunk compilation should override this function. + return false; +} + +void CodeGenerator::EmitThunkCode(const linker::LinkerPatch& patch ATTRIBUTE_UNUSED, + /*out*/ ArenaVector* code ATTRIBUTE_UNUSED, + /*out*/ std::string* debug_name ATTRIBUTE_UNUSED) { + // Code generators that create patches requiring thunk compilation should override this function. + LOG(FATAL) << "Unexpected call to EmitThunkCode()."; +} + void CodeGenerator::InitializeCodeGeneration(size_t number_of_spill_slots, size_t maximum_safepoint_spill_size, size_t number_of_out_slots, @@ -501,7 +520,7 @@ void CodeGenerator::CreateCommonInvokeLocationSummary( locations->AddTemp(visitor->GetMethodLocation()); break; } - } else { + } else if (!invoke->IsInvokePolymorphic()) { locations->AddTemp(visitor->GetMethodLocation()); } } @@ -529,6 +548,7 @@ void CodeGenerator::GenerateInvokeStaticOrDirectRuntimeCall( case kVirtual: case kInterface: case kPolymorphic: + case kCustom: LOG(FATAL) << "Unexpected invoke type: " << invoke->GetInvokeType(); UNREACHABLE(); } @@ -557,6 +577,7 @@ void CodeGenerator::GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invok entrypoint = kQuickInvokeInterfaceTrampolineWithAccessCheck; break; case kPolymorphic: + case kCustom: LOG(FATAL) << "Unexpected invoke type: " << invoke->GetInvokeType(); UNREACHABLE(); } @@ -564,11 +585,19 @@ void CodeGenerator::GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invok } void CodeGenerator::GenerateInvokePolymorphicCall(HInvokePolymorphic* invoke) { - MoveConstant(invoke->GetLocations()->GetTemp(0), static_cast(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, @@ -722,6 +751,87 @@ void CodeGenerator::GenerateLoadClassRuntimeCall(HLoadClass* cls) { } } +void CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary( + HLoadMethodHandle* method_handle, + Location runtime_proto_index_location, + Location runtime_return_location) { + DCHECK_EQ(method_handle->InputCount(), 1u); + LocationSummary* locations = + new (method_handle->GetBlock()->GetGraph()->GetAllocator()) LocationSummary( + method_handle, LocationSummary::kCallOnMainOnly); + locations->SetInAt(0, Location::NoLocation()); + locations->AddTemp(runtime_proto_index_location); + locations->SetOut(runtime_return_location); +} + +void CodeGenerator::GenerateLoadMethodHandleRuntimeCall(HLoadMethodHandle* method_handle) { + LocationSummary* locations = method_handle->GetLocations(); + MoveConstant(locations->GetTemp(0), method_handle->GetMethodHandleIndex()); + CheckEntrypointTypes(); + InvokeRuntime(kQuickResolveMethodHandle, method_handle, method_handle->GetDexPc()); +} + +void CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary( + HLoadMethodType* method_type, + Location runtime_proto_index_location, + Location runtime_return_location) { + DCHECK_EQ(method_type->InputCount(), 1u); + LocationSummary* locations = + new (method_type->GetBlock()->GetGraph()->GetAllocator()) LocationSummary( + method_type, LocationSummary::kCallOnMainOnly); + locations->SetInAt(0, Location::NoLocation()); + locations->AddTemp(runtime_proto_index_location); + locations->SetOut(runtime_return_location); +} + +void CodeGenerator::GenerateLoadMethodTypeRuntimeCall(HLoadMethodType* method_type) { + LocationSummary* locations = method_type->GetLocations(); + MoveConstant(locations->GetTemp(0), method_type->GetProtoIndex().index_); + CheckEntrypointTypes(); + InvokeRuntime(kQuickResolveMethodType, method_type, method_type->GetDexPc()); +} + +static uint32_t GetBootImageOffsetImpl(const void* object, ImageHeader::ImageSections section) { + Runtime* runtime = Runtime::Current(); + DCHECK(runtime->IsAotCompiler()); + const std::vector& boot_image_spaces = + runtime->GetHeap()->GetBootImageSpaces(); + // Check that the `object` is in the expected section of one of the boot image files. + DCHECK(std::any_of(boot_image_spaces.begin(), + boot_image_spaces.end(), + [object, section](gc::space::ImageSpace* space) { + uintptr_t begin = reinterpret_cast(space->Begin()); + uintptr_t offset = reinterpret_cast(object) - begin; + return space->GetImageHeader().GetImageSection(section).Contains(offset); + })); + uintptr_t begin = reinterpret_cast(boot_image_spaces.front()->Begin()); + uintptr_t offset = reinterpret_cast(object) - begin; + return dchecked_integral_cast(offset); +} + +// NO_THREAD_SAFETY_ANALYSIS: Avoid taking the mutator lock, boot image classes are non-moveable. +uint32_t CodeGenerator::GetBootImageOffset(HLoadClass* load_class) NO_THREAD_SAFETY_ANALYSIS { + DCHECK_EQ(load_class->GetLoadKind(), HLoadClass::LoadKind::kBootImageRelRo); + ObjPtr klass = load_class->GetClass().Get(); + DCHECK(klass != nullptr); + return GetBootImageOffsetImpl(klass.Ptr(), ImageHeader::kSectionObjects); +} + +// NO_THREAD_SAFETY_ANALYSIS: Avoid taking the mutator lock, boot image strings are non-moveable. +uint32_t CodeGenerator::GetBootImageOffset(HLoadString* load_string) NO_THREAD_SAFETY_ANALYSIS { + DCHECK_EQ(load_string->GetLoadKind(), HLoadString::LoadKind::kBootImageRelRo); + ObjPtr string = load_string->GetString().Get(); + DCHECK(string != nullptr); + return GetBootImageOffsetImpl(string.Ptr(), ImageHeader::kSectionObjects); +} + +uint32_t CodeGenerator::GetBootImageOffset(HInvokeStaticOrDirect* invoke) { + DCHECK_EQ(invoke->GetMethodLoadKind(), HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo); + ArtMethod* method = invoke->GetResolvedMethod(); + DCHECK(method != nullptr); + return GetBootImageOffsetImpl(method, ImageHeader::kSectionArtMethods); +} + void CodeGenerator::BlockIfInRegister(Location location, bool is_out) const { // The DCHECKS below check that a register is not specified twice in // the summary. The out location can overlap with an input, so we need @@ -771,53 +881,45 @@ void CodeGenerator::AllocateLocations(HInstruction* instruction) { } std::unique_ptr CodeGenerator::Create(HGraph* graph, - InstructionSet instruction_set, - const InstructionSetFeatures& isa_features, const CompilerOptions& compiler_options, OptimizingCompilerStats* stats) { ArenaAllocator* allocator = graph->GetAllocator(); - switch (instruction_set) { + switch (compiler_options.GetInstructionSet()) { #ifdef ART_ENABLE_CODEGEN_arm case InstructionSet::kArm: case InstructionSet::kThumb2: { return std::unique_ptr( - new (allocator) arm::CodeGeneratorARMVIXL( - graph, *isa_features.AsArmInstructionSetFeatures(), compiler_options, stats)); + new (allocator) arm::CodeGeneratorARMVIXL(graph, compiler_options, stats)); } #endif #ifdef ART_ENABLE_CODEGEN_arm64 case InstructionSet::kArm64: { return std::unique_ptr( - new (allocator) arm64::CodeGeneratorARM64( - graph, *isa_features.AsArm64InstructionSetFeatures(), compiler_options, stats)); + new (allocator) arm64::CodeGeneratorARM64(graph, compiler_options, stats)); } #endif #ifdef ART_ENABLE_CODEGEN_mips case InstructionSet::kMips: { return std::unique_ptr( - new (allocator) mips::CodeGeneratorMIPS( - graph, *isa_features.AsMipsInstructionSetFeatures(), compiler_options, stats)); + new (allocator) mips::CodeGeneratorMIPS(graph, compiler_options, stats)); } #endif #ifdef ART_ENABLE_CODEGEN_mips64 case InstructionSet::kMips64: { return std::unique_ptr( - new (allocator) mips64::CodeGeneratorMIPS64( - graph, *isa_features.AsMips64InstructionSetFeatures(), compiler_options, stats)); + new (allocator) mips64::CodeGeneratorMIPS64(graph, compiler_options, stats)); } #endif #ifdef ART_ENABLE_CODEGEN_x86 case InstructionSet::kX86: { return std::unique_ptr( - new (allocator) x86::CodeGeneratorX86( - graph, *isa_features.AsX86InstructionSetFeatures(), compiler_options, stats)); + new (allocator) x86::CodeGeneratorX86(graph, compiler_options, stats)); } #endif #ifdef ART_ENABLE_CODEGEN_x86_64 case InstructionSet::kX86_64: { return std::unique_ptr( - new (allocator) x86_64::CodeGeneratorX86_64( - graph, *isa_features.AsX86_64InstructionSetFeatures(), compiler_options, stats)); + new (allocator) x86_64::CodeGeneratorX86_64(graph, compiler_options, stats)); } #endif default: @@ -880,11 +982,10 @@ static void CheckCovers(uint32_t dex_pc, const CodeInfo& code_info, const ArenaVector& loop_headers, ArenaVector* covered) { - CodeInfoEncoding encoding = code_info.ExtractEncoding(); for (size_t i = 0; i < loop_headers.size(); ++i) { if (loop_headers[i]->GetDexPc() == dex_pc) { if (graph.IsCompilingOsr()) { - DCHECK(code_info.GetOsrStackMapForDexPc(dex_pc, encoding).IsValid()); + DCHECK(code_info.GetOsrStackMapForDexPc(dex_pc).IsValid()); } ++(*covered)[i]; } @@ -950,7 +1051,8 @@ void CodeGenerator::BuildStackMaps(MemoryRegion stack_map_region, void CodeGenerator::RecordPcInfo(HInstruction* instruction, uint32_t dex_pc, - SlowPathCode* slow_path) { + SlowPathCode* slow_path, + bool native_debug_info) { if (instruction != nullptr) { // The code generated for some type conversions // may call the runtime, thus normally requiring a subsequent @@ -981,7 +1083,7 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, if (instruction == nullptr) { // For stack overflow checks and native-debug-info entries without dex register // mapping (i.e. start of basic block or start of slow path). - stack_map_stream->BeginStackMapEntry(dex_pc, native_pc, 0, 0, 0, 0); + stack_map_stream->BeginStackMapEntry(dex_pc, native_pc); stack_map_stream->EndStackMapEntry(); return; } @@ -1015,37 +1117,27 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, outer_dex_pc = outer_environment->GetDexPc(); outer_environment_size = outer_environment->Size(); } + + HLoopInformation* info = instruction->GetBlock()->GetLoopInformation(); + bool osr = + instruction->IsSuspendCheck() && + (info != nullptr) && + graph_->IsCompilingOsr() && + (inlining_depth == 0); + StackMap::Kind kind = native_debug_info + ? StackMap::Kind::Debug + : (osr ? StackMap::Kind::OSR : StackMap::Kind::Default); stack_map_stream->BeginStackMapEntry(outer_dex_pc, native_pc, register_mask, locations->GetStackMask(), - outer_environment_size, - inlining_depth); + kind); EmitEnvironment(environment, slow_path); - // Record invoke info, the common case for the trampoline is super and static invokes. Only - // record these to reduce oat file size. - if (kEnableDexLayoutOptimizations) { - if (instruction->IsInvokeStaticOrDirect()) { - HInvoke* const invoke = instruction->AsInvokeStaticOrDirect(); - DCHECK(environment != nullptr); - stack_map_stream->AddInvoke(invoke->GetInvokeType(), invoke->GetDexMethodIndex()); - } - } stack_map_stream->EndStackMapEntry(); - HLoopInformation* info = instruction->GetBlock()->GetLoopInformation(); - if (instruction->IsSuspendCheck() && - (info != nullptr) && - graph_->IsCompilingOsr() && - (inlining_depth == 0)) { + if (osr) { DCHECK_EQ(info->GetSuspendCheck(), instruction); - // We duplicate the stack map as a marker that this stack map can be an OSR entry. - // Duplicating it avoids having the runtime recognize and skip an OSR stack map. DCHECK(info->IsIrreducible()); - stack_map_stream->BeginStackMapEntry( - dex_pc, native_pc, register_mask, locations->GetStackMask(), outer_environment_size, 0); - EmitEnvironment(instruction->GetEnvironment(), slow_path); - stack_map_stream->EndStackMapEntry(); if (kIsDebugBuild) { for (size_t i = 0, environment_size = environment->Size(); i < environment_size; ++i) { HInstruction* in_environment = environment->GetInstructionAt(i); @@ -1062,14 +1154,6 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, } } } - } else if (kIsDebugBuild) { - // Ensure stack maps are unique, by checking that the native pc in the stack map - // last emitted is different than the native pc of the stack map just emitted. - size_t number_of_stack_maps = stack_map_stream->GetNumberOfStackMaps(); - if (number_of_stack_maps > 1) { - DCHECK_NE(stack_map_stream->GetStackMap(number_of_stack_maps - 1).native_pc_code_offset, - stack_map_stream->GetStackMap(number_of_stack_maps - 2).native_pc_code_offset); - } } } @@ -1080,8 +1164,7 @@ bool CodeGenerator::HasStackMapAtCurrentPc() { if (count == 0) { return false; } - CodeOffset native_pc_offset = stack_map_stream->GetStackMap(count - 1).native_pc_code_offset; - return (native_pc_offset.Uint32Value(GetInstructionSet()) == pc); + return stack_map_stream->GetStackMapNativePcOffset(count - 1) == pc; } void CodeGenerator::MaybeRecordNativeDebugInfo(HInstruction* instruction, @@ -1092,12 +1175,11 @@ void CodeGenerator::MaybeRecordNativeDebugInfo(HInstruction* instruction, // Ensure that we do not collide with the stack map of the previous instruction. GenerateNop(); } - RecordPcInfo(instruction, dex_pc, slow_path); + RecordPcInfo(instruction, dex_pc, slow_path, /* native_debug_info */ true); } } void CodeGenerator::RecordCatchBlockInfo() { - ArenaAllocator* allocator = graph_->GetAllocator(); StackMapStream* stack_map_stream = GetStackMapStream(); for (HBasicBlock* block : *block_order_) { @@ -1107,30 +1189,23 @@ void CodeGenerator::RecordCatchBlockInfo() { uint32_t dex_pc = block->GetDexPc(); uint32_t num_vregs = graph_->GetNumberOfVRegs(); - uint32_t inlining_depth = 0; // Inlining of catch blocks is not supported at the moment. uint32_t native_pc = GetAddressOf(block); - uint32_t register_mask = 0; // Not used. - - // The stack mask is not used, so we leave it empty. - ArenaBitVector* stack_mask = - ArenaBitVector::Create(allocator, 0, /* expandable */ true, kArenaAllocCodeGenerator); stack_map_stream->BeginStackMapEntry(dex_pc, native_pc, - register_mask, - stack_mask, - num_vregs, - inlining_depth); + /* register_mask */ 0, + /* stack_mask */ nullptr, + StackMap::Kind::Catch); HInstruction* current_phi = block->GetFirstPhi(); for (size_t vreg = 0; vreg < num_vregs; ++vreg) { - while (current_phi != nullptr && current_phi->AsPhi()->GetRegNumber() < vreg) { - HInstruction* next_phi = current_phi->GetNext(); - DCHECK(next_phi == nullptr || - current_phi->AsPhi()->GetRegNumber() <= next_phi->AsPhi()->GetRegNumber()) - << "Phis need to be sorted by vreg number to keep this a linear-time loop."; - current_phi = next_phi; - } + while (current_phi != nullptr && current_phi->AsPhi()->GetRegNumber() < vreg) { + HInstruction* next_phi = current_phi->GetNext(); + DCHECK(next_phi == nullptr || + current_phi->AsPhi()->GetRegNumber() <= next_phi->AsPhi()->GetRegNumber()) + << "Phis need to be sorted by vreg number to keep this a linear-time loop."; + current_phi = next_phi; + } if (current_phi == nullptr || current_phi->AsPhi()->GetRegNumber() != vreg) { stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kNone, 0); @@ -1190,50 +1265,45 @@ void CodeGenerator::EmitEnvironment(HEnvironment* environment, SlowPathCode* slo continue; } + using Kind = DexRegisterLocation::Kind; Location location = environment->GetLocationAt(i); switch (location.GetKind()) { case Location::kConstant: { DCHECK_EQ(current, location.GetConstant()); if (current->IsLongConstant()) { int64_t value = current->AsLongConstant()->GetValue(); - stack_map_stream->AddDexRegisterEntry( - DexRegisterLocation::Kind::kConstant, Low32Bits(value)); - stack_map_stream->AddDexRegisterEntry( - DexRegisterLocation::Kind::kConstant, High32Bits(value)); + stack_map_stream->AddDexRegisterEntry(Kind::kConstant, Low32Bits(value)); + stack_map_stream->AddDexRegisterEntry(Kind::kConstant, High32Bits(value)); ++i; DCHECK_LT(i, environment_size); } else if (current->IsDoubleConstant()) { int64_t value = bit_cast(current->AsDoubleConstant()->GetValue()); - stack_map_stream->AddDexRegisterEntry( - DexRegisterLocation::Kind::kConstant, Low32Bits(value)); - stack_map_stream->AddDexRegisterEntry( - DexRegisterLocation::Kind::kConstant, High32Bits(value)); + stack_map_stream->AddDexRegisterEntry(Kind::kConstant, Low32Bits(value)); + stack_map_stream->AddDexRegisterEntry(Kind::kConstant, High32Bits(value)); ++i; DCHECK_LT(i, environment_size); } else if (current->IsIntConstant()) { int32_t value = current->AsIntConstant()->GetValue(); - stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kConstant, value); + stack_map_stream->AddDexRegisterEntry(Kind::kConstant, value); } else if (current->IsNullConstant()) { - stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kConstant, 0); + stack_map_stream->AddDexRegisterEntry(Kind::kConstant, 0); } else { DCHECK(current->IsFloatConstant()) << current->DebugName(); int32_t value = bit_cast(current->AsFloatConstant()->GetValue()); - stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kConstant, value); + stack_map_stream->AddDexRegisterEntry(Kind::kConstant, value); } break; } case Location::kStackSlot: { - stack_map_stream->AddDexRegisterEntry( - DexRegisterLocation::Kind::kInStack, location.GetStackIndex()); + stack_map_stream->AddDexRegisterEntry(Kind::kInStack, location.GetStackIndex()); break; } case Location::kDoubleStackSlot: { + stack_map_stream->AddDexRegisterEntry(Kind::kInStack, location.GetStackIndex()); stack_map_stream->AddDexRegisterEntry( - DexRegisterLocation::Kind::kInStack, location.GetStackIndex()); - stack_map_stream->AddDexRegisterEntry( - DexRegisterLocation::Kind::kInStack, location.GetHighStackIndex(kVRegSize)); + Kind::kInStack, location.GetHighStackIndex(kVRegSize)); ++i; DCHECK_LT(i, environment_size); break; @@ -1243,17 +1313,16 @@ void CodeGenerator::EmitEnvironment(HEnvironment* environment, SlowPathCode* slo int id = location.reg(); if (slow_path != nullptr && slow_path->IsCoreRegisterSaved(id)) { uint32_t offset = slow_path->GetStackOffsetOfCoreRegister(id); - stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack, offset); + stack_map_stream->AddDexRegisterEntry(Kind::kInStack, offset); if (current->GetType() == DataType::Type::kInt64) { - stack_map_stream->AddDexRegisterEntry( - DexRegisterLocation::Kind::kInStack, offset + kVRegSize); + stack_map_stream->AddDexRegisterEntry(Kind::kInStack, offset + kVRegSize); ++i; DCHECK_LT(i, environment_size); } } else { - stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kInRegister, id); + stack_map_stream->AddDexRegisterEntry(Kind::kInRegister, id); if (current->GetType() == DataType::Type::kInt64) { - stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kInRegisterHigh, id); + stack_map_stream->AddDexRegisterEntry(Kind::kInRegisterHigh, id); ++i; DCHECK_LT(i, environment_size); } @@ -1265,18 +1334,16 @@ void CodeGenerator::EmitEnvironment(HEnvironment* environment, SlowPathCode* slo int id = location.reg(); if (slow_path != nullptr && slow_path->IsFpuRegisterSaved(id)) { uint32_t offset = slow_path->GetStackOffsetOfFpuRegister(id); - stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack, offset); + stack_map_stream->AddDexRegisterEntry(Kind::kInStack, offset); if (current->GetType() == DataType::Type::kFloat64) { - stack_map_stream->AddDexRegisterEntry( - DexRegisterLocation::Kind::kInStack, offset + kVRegSize); + stack_map_stream->AddDexRegisterEntry(Kind::kInStack, offset + kVRegSize); ++i; DCHECK_LT(i, environment_size); } } else { - stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kInFpuRegister, id); + stack_map_stream->AddDexRegisterEntry(Kind::kInFpuRegister, id); if (current->GetType() == DataType::Type::kFloat64) { - stack_map_stream->AddDexRegisterEntry( - DexRegisterLocation::Kind::kInFpuRegisterHigh, id); + stack_map_stream->AddDexRegisterEntry(Kind::kInFpuRegisterHigh, id); ++i; DCHECK_LT(i, environment_size); } @@ -1289,16 +1356,16 @@ void CodeGenerator::EmitEnvironment(HEnvironment* environment, SlowPathCode* slo int high = location.high(); if (slow_path != nullptr && slow_path->IsFpuRegisterSaved(low)) { uint32_t offset = slow_path->GetStackOffsetOfFpuRegister(low); - stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack, offset); + stack_map_stream->AddDexRegisterEntry(Kind::kInStack, offset); } else { - stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kInFpuRegister, low); + stack_map_stream->AddDexRegisterEntry(Kind::kInFpuRegister, low); } if (slow_path != nullptr && slow_path->IsFpuRegisterSaved(high)) { uint32_t offset = slow_path->GetStackOffsetOfFpuRegister(high); - stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack, offset); + stack_map_stream->AddDexRegisterEntry(Kind::kInStack, offset); ++i; } else { - stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kInFpuRegister, high); + stack_map_stream->AddDexRegisterEntry(Kind::kInFpuRegister, high); ++i; } DCHECK_LT(i, environment_size); @@ -1310,15 +1377,15 @@ void CodeGenerator::EmitEnvironment(HEnvironment* environment, SlowPathCode* slo int high = location.high(); if (slow_path != nullptr && slow_path->IsCoreRegisterSaved(low)) { uint32_t offset = slow_path->GetStackOffsetOfCoreRegister(low); - stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack, offset); + stack_map_stream->AddDexRegisterEntry(Kind::kInStack, offset); } else { - stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kInRegister, low); + stack_map_stream->AddDexRegisterEntry(Kind::kInRegister, low); } if (slow_path != nullptr && slow_path->IsCoreRegisterSaved(high)) { uint32_t offset = slow_path->GetStackOffsetOfCoreRegister(high); - stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack, offset); + stack_map_stream->AddDexRegisterEntry(Kind::kInStack, offset); } else { - stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kInRegister, high); + stack_map_stream->AddDexRegisterEntry(Kind::kInRegister, high); } ++i; DCHECK_LT(i, environment_size); @@ -1326,7 +1393,7 @@ void CodeGenerator::EmitEnvironment(HEnvironment* environment, SlowPathCode* slo } case Location::kInvalid: { - stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kNone, 0); + stack_map_stream->AddDexRegisterEntry(Kind::kNone, 0); break; } diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index f784a1a857351eb5d23cb2d6a95e3a16cdae8a03..59f858ea52d9300065d7da859e91b5dfa3a52284 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -21,20 +21,20 @@ #include "arch/instruction_set_features.h" #include "base/arena_containers.h" #include "base/arena_object.h" +#include "base/array_ref.h" #include "base/bit_field.h" #include "base/bit_utils.h" #include "base/enums.h" +#include "base/globals.h" +#include "base/memory_region.h" #include "dex/string_reference.h" #include "dex/type_reference.h" -#include "globals.h" #include "graph_visualizer.h" #include "locations.h" -#include "memory_region.h" #include "nodes.h" #include "optimizing_compiler_stats.h" #include "read_barrier_option.h" #include "stack.h" -#include "stack_map.h" #include "utils/label.h" namespace art { @@ -74,6 +74,7 @@ class CodeAllocator { virtual ~CodeAllocator() {} virtual uint8_t* Allocate(size_t size) = 0; + virtual ArrayRef GetMemory() const = 0; private: DISALLOW_COPY_AND_ASSIGN(CodeAllocator); @@ -187,8 +188,6 @@ class CodeGenerator : public DeletableArenaObject { // Compiles the graph to executable instructions. void Compile(CodeAllocator* allocator); static std::unique_ptr Create(HGraph* graph, - InstructionSet instruction_set, - const InstructionSetFeatures& isa_features, const CompilerOptions& compiler_options, OptimizingCompilerStats* stats = nullptr); virtual ~CodeGenerator(); @@ -210,6 +209,10 @@ class CodeGenerator : public DeletableArenaObject { virtual void Initialize() = 0; virtual void Finalize(CodeAllocator* allocator); virtual void EmitLinkerPatches(ArenaVector* linker_patches); + virtual bool NeedsThunkCode(const linker::LinkerPatch& patch) const; + virtual void EmitThunkCode(const linker::LinkerPatch& patch, + /*out*/ ArenaVector* code, + /*out*/ std::string* debug_name); virtual void GenerateFrameEntry() = 0; virtual void GenerateFrameExit() = 0; virtual void Bind(HBasicBlock* block) = 0; @@ -318,7 +321,10 @@ class CodeGenerator : public DeletableArenaObject { } // Record native to dex mapping for a suspend point. Required by runtime. - void RecordPcInfo(HInstruction* instruction, uint32_t dex_pc, SlowPathCode* slow_path = nullptr); + void RecordPcInfo(HInstruction* instruction, + uint32_t dex_pc, + SlowPathCode* slow_path = nullptr, + bool native_debug_info = false); // Check whether we have already recorded mapping at this PC. bool HasStackMapAtCurrentPc(); // Record extra stack maps if we support native debugging. @@ -438,6 +444,8 @@ class CodeGenerator : public DeletableArenaObject { case TypeCheckKind::kArrayCheck: case TypeCheckKind::kUnresolvedCheck: return false; + case TypeCheckKind::kBitstringCheck: + return true; } LOG(FATAL) << "Unreachable"; UNREACHABLE(); @@ -535,10 +543,13 @@ class CodeGenerator : public DeletableArenaObject { 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, @@ -556,6 +567,20 @@ class CodeGenerator : public DeletableArenaObject { Location runtime_return_location); void GenerateLoadClassRuntimeCall(HLoadClass* cls); + static void CreateLoadMethodHandleRuntimeCallLocationSummary(HLoadMethodHandle* method_handle, + Location runtime_handle_index_location, + Location runtime_return_location); + void GenerateLoadMethodHandleRuntimeCall(HLoadMethodHandle* method_handle); + + static void CreateLoadMethodTypeRuntimeCallLocationSummary(HLoadMethodType* method_type, + Location runtime_type_index_location, + Location runtime_return_location); + void GenerateLoadMethodTypeRuntimeCall(HLoadMethodType* method_type); + + uint32_t GetBootImageOffset(HLoadClass* load_class); + uint32_t GetBootImageOffset(HLoadString* load_string); + uint32_t GetBootImageOffset(HInvokeStaticOrDirect* invoke); + static void CreateSystemArrayCopyLocationSummary(HInvoke* invoke); void SetDisassemblyInformation(DisassemblyInformation* info) { disasm_info_ = info; } diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 60f8f98757d985e3419d49ab9abf231b8a64b1fa..02c995a8337ae64b29a28997650dc1666605df88 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -27,10 +27,10 @@ #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "gc/accounting/card_table.h" +#include "gc/space/image_space.h" #include "heap_poisoning.h" #include "intrinsics.h" #include "intrinsics_arm64.h" -#include "linker/arm64/relative_patcher_arm64.h" #include "linker/linker_patch.h" #include "lock_word.h" #include "mirror/array-inl.h" @@ -69,7 +69,7 @@ using helpers::InputCPURegisterOrZeroRegAt; using helpers::InputFPRegisterAt; using helpers::InputOperandAt; using helpers::InputRegisterAt; -using helpers::Int64ConstantFrom; +using helpers::Int64FromLocation; using helpers::IsConstantZeroBitPattern; using helpers::LocationFrom; using helpers::OperandFromMemOperand; @@ -78,6 +78,7 @@ using helpers::OutputFPRegister; using helpers::OutputRegister; using helpers::QRegisterFrom; using helpers::RegisterFrom; +using helpers::SRegisterFrom; using helpers::StackOperandFrom; using helpers::VIXLRegCodeFromART; using helpers::WRegisterFrom; @@ -1373,7 +1374,6 @@ Location InvokeDexCallingConventionVisitorARM64::GetMethodLocation() const { } CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph, - const Arm64InstructionSetFeatures& isa_features, const CompilerOptions& compiler_options, OptimizingCompilerStats* stats) : CodeGenerator(graph, @@ -1390,7 +1390,6 @@ CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph, instruction_visitor_(graph, this), move_resolver_(graph->GetAllocator(), this), assembler_(graph->GetAllocator()), - isa_features_(isa_features), uint32_literals_(std::less(), graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), uint64_literals_(std::less(), @@ -1401,6 +1400,7 @@ CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph, type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), + boot_image_intrinsic_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), baker_read_barrier_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), jit_string_patches_(StringReferenceValueComparator(), graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), @@ -1424,6 +1424,62 @@ void CodeGeneratorARM64::Finalize(CodeAllocator* allocator) { __ FinalizeCode(); CodeGenerator::Finalize(allocator); + + // Verify Baker read barrier linker patches. + if (kIsDebugBuild) { + ArrayRef code = allocator->GetMemory(); + for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) { + DCHECK(info.label.IsBound()); + uint32_t literal_offset = info.label.GetLocation(); + DCHECK_ALIGNED(literal_offset, 4u); + + auto GetInsn = [&code](uint32_t offset) { + DCHECK_ALIGNED(offset, 4u); + return + (static_cast(code[offset + 0]) << 0) + + (static_cast(code[offset + 1]) << 8) + + (static_cast(code[offset + 2]) << 16)+ + (static_cast(code[offset + 3]) << 24); + }; + + const uint32_t encoded_data = info.custom_data; + BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); + // Check that the next instruction matches the expected LDR. + switch (kind) { + case BakerReadBarrierKind::kField: { + DCHECK_GE(code.size() - literal_offset, 8u); + uint32_t next_insn = GetInsn(literal_offset + 4u); + // LDR (immediate) with correct base_reg. + CheckValidReg(next_insn & 0x1fu); // Check destination register. + const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(next_insn & 0xffc003e0u, 0xb9400000u | (base_reg << 5)); + break; + } + case BakerReadBarrierKind::kArray: { + DCHECK_GE(code.size() - literal_offset, 8u); + uint32_t next_insn = GetInsn(literal_offset + 4u); + // LDR (register) with the correct base_reg, size=10 (32-bit), option=011 (extend = LSL), + // and S=1 (shift amount = 2 for 32-bit version), i.e. LDR Wt, [Xn, Xm, LSL #2]. + CheckValidReg(next_insn & 0x1fu); // Check destination register. + const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(next_insn & 0xffe0ffe0u, 0xb8607800u | (base_reg << 5)); + CheckValidReg((next_insn >> 16) & 0x1f); // Check index register + break; + } + case BakerReadBarrierKind::kGcRoot: { + DCHECK_GE(literal_offset, 4u); + uint32_t prev_insn = GetInsn(literal_offset - 4u); + // LDR (immediate) with correct root_reg. + const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(prev_insn & 0xffc0001fu, 0xb9400000u | root_reg); + break; + } + default: + LOG(FATAL) << "Unexpected kind: " << static_cast(kind); + UNREACHABLE(); + } + } + } } void ParallelMoveResolverARM64::PrepareForEmitNativeCode() { @@ -1672,6 +1728,10 @@ void CodeGeneratorARM64::DumpFloatingPointRegister(std::ostream& stream, int reg stream << DRegister(reg); } +const Arm64InstructionSetFeatures& CodeGeneratorARM64::GetInstructionSetFeatures() const { + return *GetCompilerOptions().GetInstructionSetFeatures()->AsArm64InstructionSetFeatures(); +} + void CodeGeneratorARM64::MoveConstant(CPURegister destination, HConstant* constant) { if (constant->IsIntConstant()) { __ Mov(Register(destination), constant->AsIntConstant()->GetValue()); @@ -2128,6 +2188,26 @@ void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCod __ Bind(slow_path->GetExitLabel()); } +void InstructionCodeGeneratorARM64::GenerateBitstringTypeCheckCompare( + HTypeCheckInstruction* check, vixl::aarch64::Register temp) { + uint32_t path_to_root = check->GetBitstringPathToRoot(); + uint32_t mask = check->GetBitstringMask(); + DCHECK(IsPowerOfTwo(mask + 1)); + size_t mask_bits = WhichPowerOf2(mask + 1); + + if (mask_bits == 16u) { + // Load only the bitstring part of the status word. + __ Ldrh(temp, HeapOperand(temp, mirror::Class::StatusOffset())); + } else { + // /* uint32_t */ temp = temp->status_ + __ Ldr(temp, HeapOperand(temp, mirror::Class::StatusOffset())); + // Extract the bitstring bits. + __ Ubfx(temp, temp, 0, mask_bits); + } + // Compare the bitstring bits to `path_to_root`. + __ Cmp(temp, path_to_root); +} + void CodeGeneratorARM64::GenerateMemoryBarrier(MemBarrierKind kind) { BarrierType type = BarrierAll; @@ -2383,6 +2463,9 @@ void InstructionCodeGeneratorARM64::HandleBinaryOp(HBinaryOperation* instr) { // all & reg_bits - 1. __ Ror(dst, lhs, RegisterFrom(instr->GetLocations()->InAt(1), type)); } + } else if (instr->IsMin() || instr->IsMax()) { + __ Cmp(lhs, rhs); + __ Csel(dst, lhs, rhs, instr->IsMin() ? lt : gt); } else { DCHECK(instr->IsXor()); __ Eor(dst, lhs, rhs); @@ -2398,6 +2481,10 @@ void InstructionCodeGeneratorARM64::HandleBinaryOp(HBinaryOperation* instr) { __ Fadd(dst, lhs, rhs); } else if (instr->IsSub()) { __ Fsub(dst, lhs, rhs); + } else if (instr->IsMin()) { + __ Fmin(dst, lhs, rhs); + } else if (instr->IsMax()) { + __ Fmax(dst, lhs, rhs); } else { LOG(FATAL) << "Unexpected floating-point binary operation"; } @@ -2618,7 +2705,7 @@ void LocationsBuilderARM64::VisitIntermediateAddressIndex(HIntermediateAddressIn void InstructionCodeGeneratorARM64::VisitIntermediateAddressIndex( HIntermediateAddressIndex* instruction) { Register index_reg = InputRegisterAt(instruction, 0); - uint32_t shift = Int64ConstantFrom(instruction->GetLocations()->InAt(2)); + uint32_t shift = Int64FromLocation(instruction->GetLocations()->InAt(2)); uint32_t offset = instruction->GetOffset()->AsIntConstant()->GetValue(); if (shift == 0) { @@ -2748,7 +2835,7 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0))); if (index.IsConstant()) { // Array load with a constant index can be treated as a field load. - offset += Int64ConstantFrom(index) << DataType::SizeShift(type); + offset += Int64FromLocation(index) << DataType::SizeShift(type); Location maybe_temp = (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location::NoLocation(); codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction, @@ -2793,14 +2880,14 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { "Expecting 0=compressed, 1=uncompressed"); __ Tbnz(length.W(), 0, &uncompressed_load); __ Ldrb(Register(OutputCPURegister(instruction)), - HeapOperand(obj, offset + Int64ConstantFrom(index))); + HeapOperand(obj, offset + Int64FromLocation(index))); __ B(&done); __ Bind(&uncompressed_load); __ Ldrh(Register(OutputCPURegister(instruction)), - HeapOperand(obj, offset + (Int64ConstantFrom(index) << 1))); + HeapOperand(obj, offset + (Int64FromLocation(index) << 1))); __ Bind(&done); } else { - offset += Int64ConstantFrom(index) << DataType::SizeShift(type); + offset += Int64FromLocation(index) << DataType::SizeShift(type); source = HeapOperand(obj, offset); } } else { @@ -2913,7 +3000,7 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) { if (!needs_write_barrier) { DCHECK(!may_need_runtime_call_for_type_check); if (index.IsConstant()) { - offset += Int64ConstantFrom(index) << DataType::SizeShift(value_type); + offset += Int64FromLocation(index) << DataType::SizeShift(value_type); destination = HeapOperand(array, offset); } else { UseScratchRegisterScope temps(masm); @@ -2951,7 +3038,7 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) { UseScratchRegisterScope temps(masm); Register temp = temps.AcquireSameSizeAs(array); if (index.IsConstant()) { - offset += Int64ConstantFrom(index) << DataType::SizeShift(value_type); + offset += Int64FromLocation(index) << DataType::SizeShift(value_type); destination = HeapOperand(array, offset); } else { destination = HeapOperand(temp, @@ -3260,61 +3347,30 @@ FOR_EACH_CONDITION_INSTRUCTION(DEFINE_CONDITION_VISITORS) #undef DEFINE_CONDITION_VISITORS #undef FOR_EACH_CONDITION_INSTRUCTION -void InstructionCodeGeneratorARM64::DivRemOneOrMinusOne(HBinaryOperation* instruction) { - DCHECK(instruction->IsDiv() || instruction->IsRem()); - - LocationSummary* locations = instruction->GetLocations(); - Location second = locations->InAt(1); - DCHECK(second.IsConstant()); +void InstructionCodeGeneratorARM64::GenerateIntDivForPower2Denom(HDiv* instruction) { + int64_t imm = Int64FromLocation(instruction->GetLocations()->InAt(1)); + uint64_t abs_imm = static_cast(AbsOrMin(imm)); + DCHECK(IsPowerOfTwo(abs_imm)) << abs_imm; Register out = OutputRegister(instruction); Register dividend = InputRegisterAt(instruction, 0); - int64_t imm = Int64FromConstant(second.GetConstant()); - DCHECK(imm == 1 || imm == -1); - if (instruction->IsRem()) { - __ Mov(out, 0); + if (abs_imm == 2) { + int bits = DataType::Size(instruction->GetResultType()) * kBitsPerByte; + __ Add(out, dividend, Operand(dividend, LSR, bits - 1)); } else { - if (imm == 1) { - __ Mov(out, dividend); - } else { - __ Neg(out, dividend); - } - } -} - -void InstructionCodeGeneratorARM64::DivRemByPowerOfTwo(HBinaryOperation* instruction) { - DCHECK(instruction->IsDiv() || instruction->IsRem()); - - LocationSummary* locations = instruction->GetLocations(); - Location second = locations->InAt(1); - DCHECK(second.IsConstant()); - - Register out = OutputRegister(instruction); - Register dividend = InputRegisterAt(instruction, 0); - int64_t imm = Int64FromConstant(second.GetConstant()); - uint64_t abs_imm = static_cast(AbsOrMin(imm)); - int ctz_imm = CTZ(abs_imm); - - UseScratchRegisterScope temps(GetVIXLAssembler()); - Register temp = temps.AcquireSameSizeAs(out); - - if (instruction->IsDiv()) { + UseScratchRegisterScope temps(GetVIXLAssembler()); + Register temp = temps.AcquireSameSizeAs(out); __ Add(temp, dividend, abs_imm - 1); __ Cmp(dividend, 0); __ Csel(out, temp, dividend, lt); - if (imm > 0) { - __ Asr(out, out, ctz_imm); - } else { - __ Neg(out, Operand(out, ASR, ctz_imm)); - } + } + + int ctz_imm = CTZ(abs_imm); + if (imm > 0) { + __ Asr(out, out, ctz_imm); } else { - int bits = instruction->GetResultType() == DataType::Type::kInt32 ? 32 : 64; - __ Asr(temp, dividend, bits - 1); - __ Lsr(temp, temp, bits - ctz_imm); - __ Add(out, dividend, temp); - __ And(out, out, abs_imm - 1); - __ Sub(out, out, temp); + __ Neg(out, Operand(out, ASR, ctz_imm)); } } @@ -3370,39 +3426,34 @@ void InstructionCodeGeneratorARM64::GenerateDivRemWithAnyConstant(HBinaryOperati } } -void InstructionCodeGeneratorARM64::GenerateDivRemIntegral(HBinaryOperation* instruction) { - DCHECK(instruction->IsDiv() || instruction->IsRem()); - DataType::Type type = instruction->GetResultType(); - DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64); +void InstructionCodeGeneratorARM64::GenerateIntDivForConstDenom(HDiv *instruction) { + int64_t imm = Int64FromLocation(instruction->GetLocations()->InAt(1)); - LocationSummary* locations = instruction->GetLocations(); - Register out = OutputRegister(instruction); - Location second = locations->InAt(1); + if (imm == 0) { + // Do not generate anything. DivZeroCheck would prevent any code to be executed. + return; + } - if (second.IsConstant()) { - int64_t imm = Int64FromConstant(second.GetConstant()); + if (IsPowerOfTwo(AbsOrMin(imm))) { + GenerateIntDivForPower2Denom(instruction); + } else { + // Cases imm == -1 or imm == 1 are handled by InstructionSimplifier. + DCHECK(imm < -2 || imm > 2) << imm; + GenerateDivRemWithAnyConstant(instruction); + } +} - if (imm == 0) { - // Do not generate anything. DivZeroCheck would prevent any code to be executed. - } else if (imm == 1 || imm == -1) { - DivRemOneOrMinusOne(instruction); - } else if (IsPowerOfTwo(AbsOrMin(imm))) { - DivRemByPowerOfTwo(instruction); - } else { - DCHECK(imm <= -2 || imm >= 2); - GenerateDivRemWithAnyConstant(instruction); - } +void InstructionCodeGeneratorARM64::GenerateIntDiv(HDiv *instruction) { + DCHECK(DataType::IsIntOrLongType(instruction->GetResultType())) + << instruction->GetResultType(); + + if (instruction->GetLocations()->InAt(1).IsConstant()) { + GenerateIntDivForConstDenom(instruction); } else { + Register out = OutputRegister(instruction); Register dividend = InputRegisterAt(instruction, 0); Register divisor = InputRegisterAt(instruction, 1); - if (instruction->IsDiv()) { - __ Sdiv(out, dividend, divisor); - } else { - UseScratchRegisterScope temps(GetVIXLAssembler()); - Register temp = temps.AcquireSameSizeAs(out); - __ Sdiv(temp, dividend, divisor); - __ Msub(out, temp, divisor, dividend); - } + __ Sdiv(out, dividend, divisor); } } @@ -3434,7 +3485,7 @@ void InstructionCodeGeneratorARM64::VisitDiv(HDiv* div) { switch (type) { case DataType::Type::kInt32: case DataType::Type::kInt64: - GenerateDivRemIntegral(div); + GenerateIntDiv(div); break; case DataType::Type::kFloat32: @@ -3466,7 +3517,7 @@ void InstructionCodeGeneratorARM64::VisitDivZeroCheck(HDivZeroCheck* instruction } if (value.IsConstant()) { - int64_t divisor = Int64ConstantFrom(value); + int64_t divisor = Int64FromLocation(value); if (divisor == 0) { __ B(slow_path->GetEntryLabel()); } else { @@ -3865,6 +3916,8 @@ void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; + case TypeCheckKind::kBitstringCheck: + break; } LocationSummary* locations = @@ -3873,7 +3926,13 @@ void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } // The "out" register is used as a temporary, so it overlaps with the inputs. // Note that TypeCheckSlowPathARM64 uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -3886,7 +3945,9 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = InputRegisterAt(instruction, 0); - Register cls = InputRegisterAt(instruction, 1); + Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) + ? Register() + : InputRegisterAt(instruction, 1); Location out_loc = locations->Out(); Register out = OutputRegister(instruction); const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -4072,6 +4133,23 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { } break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, out); + __ Cset(out, eq); + if (zero.IsLinked()) { + __ B(&done); + } + break; + } } if (zero.IsLinked()) { @@ -4094,7 +4172,13 @@ void LocationsBuilderARM64::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } // Add temps for read barriers and other uses. One is used by TypeCheckSlowPathARM64. locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } @@ -4104,7 +4188,9 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = InputRegisterAt(instruction, 0); - Register cls = InputRegisterAt(instruction, 1); + Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) + ? Register() + : InputRegisterAt(instruction, 1); const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); DCHECK_GE(num_temps, 1u); DCHECK_LE(num_temps, 3u); @@ -4285,6 +4371,20 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { __ B(ne, &start_loop); break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + maybe_temp2_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, temp); + __ B(ne, type_check_slow_path->GetEntryLabel()); + break; + } } __ Bind(&done); @@ -4459,12 +4559,23 @@ void CodeGeneratorARM64::GenerateStaticOrDirectCall( // Load method address from literal pool. __ Ldr(XRegisterFrom(temp), DeduplicateUint64Literal(invoke->GetMethodAddress())); break; + case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: { + // Add ADRP with its PC-relative .data.bimg.rel.ro patch. + uint32_t boot_image_offset = GetBootImageOffset(invoke); + vixl::aarch64::Label* adrp_label = NewBootImageRelRoPatch(boot_image_offset); + EmitAdrpPlaceholder(adrp_label, XRegisterFrom(temp)); + // Add LDR with its PC-relative .data.bimg.rel.ro patch. + vixl::aarch64::Label* ldr_label = NewBootImageRelRoPatch(boot_image_offset, adrp_label); + // Note: Boot image is in the low 4GiB and the entry is 32-bit, so emit a 32-bit load. + EmitLdrOffsetPlaceholder(ldr_label, WRegisterFrom(temp), XRegisterFrom(temp)); + break; + } case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: { - // Add ADRP with its PC-relative DexCache access patch. + // Add ADRP with its PC-relative .bss entry patch. MethodReference target_method(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()); vixl::aarch64::Label* adrp_label = NewMethodBssEntryPatch(target_method); EmitAdrpPlaceholder(adrp_label, XRegisterFrom(temp)); - // Add LDR with its PC-relative DexCache access patch. + // Add LDR with its PC-relative .bss entry patch. vixl::aarch64::Label* ldr_label = NewMethodBssEntryPatch(target_method, adrp_label); EmitLdrOffsetPlaceholder(ldr_label, XRegisterFrom(temp), XRegisterFrom(temp)); @@ -4559,6 +4670,29 @@ void InstructionCodeGeneratorARM64::VisitInvokePolymorphic(HInvokePolymorphic* i 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::NewBootImageIntrinsicPatch( + uint32_t intrinsic_data, + vixl::aarch64::Label* adrp_label) { + return NewPcRelativePatch( + /* dex_file */ nullptr, intrinsic_data, adrp_label, &boot_image_intrinsic_patches_); +} + +vixl::aarch64::Label* CodeGeneratorARM64::NewBootImageRelRoPatch( + uint32_t boot_image_offset, + vixl::aarch64::Label* adrp_label) { + return NewPcRelativePatch( + /* dex_file */ nullptr, boot_image_offset, adrp_label, &boot_image_method_patches_); +} + vixl::aarch64::Label* CodeGeneratorARM64::NewBootImageMethodPatch( MethodReference target_method, vixl::aarch64::Label* adrp_label) { @@ -4669,6 +4803,55 @@ void CodeGeneratorARM64::EmitLdrOffsetPlaceholder(vixl::aarch64::Label* fixup_la __ ldr(out, MemOperand(base, /* offset placeholder */ 0)); } +void CodeGeneratorARM64::LoadBootImageAddress(vixl::aarch64::Register reg, + uint32_t boot_image_reference) { + if (GetCompilerOptions().IsBootImage()) { + // Add ADRP with its PC-relative type patch. + vixl::aarch64::Label* adrp_label = NewBootImageIntrinsicPatch(boot_image_reference); + EmitAdrpPlaceholder(adrp_label, reg.X()); + // Add ADD with its PC-relative type patch. + vixl::aarch64::Label* add_label = NewBootImageIntrinsicPatch(boot_image_reference, adrp_label); + EmitAddPlaceholder(add_label, reg.X(), reg.X()); + } else if (GetCompilerOptions().GetCompilePic()) { + DCHECK(Runtime::Current()->IsAotCompiler()); + // Add ADRP with its PC-relative .data.bimg.rel.ro patch. + vixl::aarch64::Label* adrp_label = NewBootImageRelRoPatch(boot_image_reference); + EmitAdrpPlaceholder(adrp_label, reg.X()); + // Add LDR with its PC-relative .data.bimg.rel.ro patch. + vixl::aarch64::Label* ldr_label = NewBootImageRelRoPatch(boot_image_reference, adrp_label); + EmitLdrOffsetPlaceholder(ldr_label, reg.W(), reg.X()); + } else { + gc::Heap* heap = Runtime::Current()->GetHeap(); + DCHECK(!heap->GetBootImageSpaces().empty()); + const uint8_t* address = heap->GetBootImageSpaces()[0]->Begin() + boot_image_reference; + __ Ldr(reg.W(), DeduplicateBootImageAddressLiteral(reinterpret_cast(address))); + } +} + +void CodeGeneratorARM64::AllocateInstanceForIntrinsic(HInvokeStaticOrDirect* invoke, + uint32_t boot_image_offset) { + DCHECK(invoke->IsStatic()); + InvokeRuntimeCallingConvention calling_convention; + Register argument = calling_convention.GetRegisterAt(0); + if (GetCompilerOptions().IsBootImage()) { + DCHECK_EQ(boot_image_offset, IntrinsicVisitor::IntegerValueOfInfo::kInvalidReference); + // Load the class the same way as for HLoadClass::LoadKind::kBootImageLinkTimePcRelative. + MethodReference target_method = invoke->GetTargetMethod(); + dex::TypeIndex type_idx = target_method.dex_file->GetMethodId(target_method.index).class_idx_; + // Add ADRP with its PC-relative type patch. + vixl::aarch64::Label* adrp_label = NewBootImageTypePatch(*target_method.dex_file, type_idx); + EmitAdrpPlaceholder(adrp_label, argument.X()); + // Add ADD with its PC-relative type patch. + vixl::aarch64::Label* add_label = + NewBootImageTypePatch(*target_method.dex_file, type_idx, adrp_label); + EmitAddPlaceholder(add_label, argument.X(), argument.X()); + } else { + LoadBootImageAddress(argument, boot_image_offset); + } + InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc()); + CheckEntrypointTypes(); +} + template inline void CodeGeneratorARM64::EmitPcRelativeLinkerPatches( const ArenaDeque& infos, @@ -4681,6 +4864,15 @@ inline void CodeGeneratorARM64::EmitPcRelativeLinkerPatches( } } +template +linker::LinkerPatch NoDexFileAdapter(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t boot_image_offset) { + DCHECK(target_dex_file == nullptr); // Unused for these patches, should be null. + return Factory(literal_offset, pc_insn_offset, boot_image_offset); +} + void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector* linker_patches) { DCHECK(linker_patches->empty()); size_t size = @@ -4690,6 +4882,7 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector* lin type_bss_entry_patches_.size() + boot_image_string_patches_.size() + string_bss_entry_patches_.size() + + boot_image_intrinsic_patches_.size() + baker_read_barrier_patches_.size(); linker_patches->reserve(size); if (GetCompilerOptions().IsBootImage()) { @@ -4699,12 +4892,14 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector* lin boot_image_type_patches_, linker_patches); EmitPcRelativeLinkerPatches( boot_image_string_patches_, linker_patches); + EmitPcRelativeLinkerPatches>( + boot_image_intrinsic_patches_, linker_patches); } else { - DCHECK(boot_image_method_patches_.empty()); - EmitPcRelativeLinkerPatches( - boot_image_type_patches_, linker_patches); - EmitPcRelativeLinkerPatches( - boot_image_string_patches_, linker_patches); + EmitPcRelativeLinkerPatches>( + boot_image_method_patches_, linker_patches); + DCHECK(boot_image_type_patches_.empty()); + DCHECK(boot_image_string_patches_.empty()); + DCHECK(boot_image_intrinsic_patches_.empty()); } EmitPcRelativeLinkerPatches( method_bss_entry_patches_, linker_patches); @@ -4719,6 +4914,44 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector* lin DCHECK_EQ(size, linker_patches->size()); } +bool CodeGeneratorARM64::NeedsThunkCode(const linker::LinkerPatch& patch) const { + return patch.GetType() == linker::LinkerPatch::Type::kBakerReadBarrierBranch || + patch.GetType() == linker::LinkerPatch::Type::kCallRelative; +} + +void CodeGeneratorARM64::EmitThunkCode(const linker::LinkerPatch& patch, + /*out*/ ArenaVector* code, + /*out*/ std::string* debug_name) { + Arm64Assembler assembler(GetGraph()->GetAllocator()); + switch (patch.GetType()) { + case linker::LinkerPatch::Type::kCallRelative: { + // The thunk just uses the entry point in the ArtMethod. This works even for calls + // to the generic JNI and interpreter trampolines. + Offset offset(ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kArm64PointerSize).Int32Value()); + assembler.JumpTo(ManagedRegister(arm64::X0), offset, ManagedRegister(arm64::IP0)); + if (GetCompilerOptions().GenerateAnyDebugInfo()) { + *debug_name = "MethodCallThunk"; + } + break; + } + case linker::LinkerPatch::Type::kBakerReadBarrierBranch: { + DCHECK_EQ(patch.GetBakerCustomValue2(), 0u); + CompileBakerReadBarrierThunk(assembler, patch.GetBakerCustomValue1(), debug_name); + break; + } + default: + LOG(FATAL) << "Unexpected patch type " << patch.GetType(); + UNREACHABLE(); + } + + // Ensure we emit the literal pool if any. + assembler.FinalizeCode(); + code->resize(assembler.CodeSize()); + MemoryRegion code_region(code->data(), code->size()); + assembler.FinalizeInstructions(code_region); +} + vixl::aarch64::Literal* CodeGeneratorARM64::DeduplicateUint32Literal(uint32_t value) { return uint32_literals_.GetOrCreate( value, @@ -4779,7 +5012,7 @@ HLoadClass::LoadKind CodeGeneratorARM64::GetSupportedLoadClassKind( case HLoadClass::LoadKind::kReferrersClass: break; case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: - case HLoadClass::LoadKind::kBootImageClassTable: + case HLoadClass::LoadKind::kBootImageRelRo: case HLoadClass::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -4859,12 +5092,12 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA DCHECK(!cls->MustGenerateClinitCheck()); // /* GcRoot */ out = current_method->declaring_class_ Register current_method = InputRegisterAt(cls, 0); - GenerateGcRootFieldLoad(cls, - out_loc, - current_method, - ArtMethod::DeclaringClassOffset().Int32Value(), - /* fixup_label */ nullptr, - read_barrier_option); + codegen_->GenerateGcRootFieldLoad(cls, + out_loc, + current_method, + ArtMethod::DeclaringClassOffset().Int32Value(), + /* fixup_label */ nullptr, + read_barrier_option); break; } case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: { @@ -4888,23 +5121,16 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(address)); break; } - case HLoadClass::LoadKind::kBootImageClassTable: { + case HLoadClass::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); - // Add ADRP with its PC-relative type patch. - const DexFile& dex_file = cls->GetDexFile(); - dex::TypeIndex type_index = cls->GetTypeIndex(); - vixl::aarch64::Label* adrp_label = codegen_->NewBootImageTypePatch(dex_file, type_index); + uint32_t boot_image_offset = codegen_->GetBootImageOffset(cls); + // Add ADRP with its PC-relative .data.bimg.rel.ro patch. + vixl::aarch64::Label* adrp_label = codegen_->NewBootImageRelRoPatch(boot_image_offset); codegen_->EmitAdrpPlaceholder(adrp_label, out.X()); - // Add LDR with its PC-relative type patch. + // Add LDR with its PC-relative .data.bimg.rel.ro patch. vixl::aarch64::Label* ldr_label = - codegen_->NewBootImageTypePatch(dex_file, type_index, adrp_label); + codegen_->NewBootImageRelRoPatch(boot_image_offset, adrp_label); codegen_->EmitLdrOffsetPlaceholder(ldr_label, out.W(), out.X()); - // Extract the reference from the slot data, i.e. clear the hash bits. - int32_t masked_hash = ClassTable::TableSlot::MaskHash( - ComputeModifiedUtf8Hash(dex_file.StringByTypeIdx(type_index))); - if (masked_hash != 0) { - __ Sub(out.W(), out.W(), Operand(masked_hash)); - } break; } case HLoadClass::LoadKind::kBssEntry: { @@ -4914,16 +5140,16 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA vixl::aarch64::Register temp = XRegisterFrom(out_loc); vixl::aarch64::Label* adrp_label = codegen_->NewBssEntryTypePatch(dex_file, type_index); codegen_->EmitAdrpPlaceholder(adrp_label, temp); - // Add LDR with its PC-relative Class patch. + // Add LDR with its PC-relative Class .bss entry patch. vixl::aarch64::Label* ldr_label = codegen_->NewBssEntryTypePatch(dex_file, type_index, adrp_label); // /* GcRoot */ out = *(base_address + offset) /* PC-relative */ - GenerateGcRootFieldLoad(cls, - out_loc, - temp, - /* offset placeholder */ 0u, - ldr_label, - read_barrier_option); + codegen_->GenerateGcRootFieldLoad(cls, + out_loc, + temp, + /* offset placeholder */ 0u, + ldr_label, + read_barrier_option); generate_null_check = true; break; } @@ -4931,12 +5157,12 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(), cls->GetTypeIndex(), cls->GetClass())); - GenerateGcRootFieldLoad(cls, - out_loc, - out.X(), - /* offset */ 0, - /* fixup_label */ nullptr, - read_barrier_option); + codegen_->GenerateGcRootFieldLoad(cls, + out_loc, + out.X(), + /* offset */ 0, + /* fixup_label */ nullptr, + read_barrier_option); break; } case HLoadClass::LoadKind::kRuntimeCall: @@ -4963,6 +5189,26 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA } } +void LocationsBuilderARM64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + InvokeRuntimeCallingConvention calling_convention; + Location location = LocationFrom(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorARM64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + +void LocationsBuilderARM64::VisitLoadMethodType(HLoadMethodType* load) { + InvokeRuntimeCallingConvention calling_convention; + Location location = LocationFrom(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorARM64::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + static MemOperand GetExceptionTlsAddress() { return MemOperand(tr, Thread::ExceptionOffset().Int32Value()); } @@ -4989,7 +5235,7 @@ HLoadString::LoadKind CodeGeneratorARM64::GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) { switch (desired_string_load_kind) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: - case HLoadString::LoadKind::kBootImageInternTable: + case HLoadString::LoadKind::kBootImageRelRo: case HLoadString::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -5055,16 +5301,15 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(address)); return; } - case HLoadString::LoadKind::kBootImageInternTable: { + case HLoadString::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); - // Add ADRP with its PC-relative String patch. - const DexFile& dex_file = load->GetDexFile(); - const dex::StringIndex string_index = load->GetStringIndex(); - vixl::aarch64::Label* adrp_label = codegen_->NewBootImageStringPatch(dex_file, string_index); + // Add ADRP with its PC-relative .data.bimg.rel.ro patch. + uint32_t boot_image_offset = codegen_->GetBootImageOffset(load); + vixl::aarch64::Label* adrp_label = codegen_->NewBootImageRelRoPatch(boot_image_offset); codegen_->EmitAdrpPlaceholder(adrp_label, out.X()); - // Add LDR with its PC-relative String patch. + // Add LDR with its PC-relative .data.bimg.rel.ro patch. vixl::aarch64::Label* ldr_label = - codegen_->NewBootImageStringPatch(dex_file, string_index, adrp_label); + codegen_->NewBootImageRelRoPatch(boot_image_offset, adrp_label); codegen_->EmitLdrOffsetPlaceholder(ldr_label, out.W(), out.X()); return; } @@ -5076,16 +5321,16 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD Register temp = XRegisterFrom(out_loc); vixl::aarch64::Label* adrp_label = codegen_->NewStringBssEntryPatch(dex_file, string_index); codegen_->EmitAdrpPlaceholder(adrp_label, temp); - // Add LDR with its .bss entry String patch. + // Add LDR with its PC-relative String .bss entry patch. vixl::aarch64::Label* ldr_label = codegen_->NewStringBssEntryPatch(dex_file, string_index, adrp_label); // /* GcRoot */ out = *(base_address + offset) /* PC-relative */ - GenerateGcRootFieldLoad(load, - out_loc, - temp, - /* offset placeholder */ 0u, - ldr_label, - kCompilerReadBarrierOption); + codegen_->GenerateGcRootFieldLoad(load, + out_loc, + temp, + /* offset placeholder */ 0u, + ldr_label, + kCompilerReadBarrierOption); SlowPathCodeARM64* slow_path = new (codegen_->GetScopedAllocator()) LoadStringSlowPathARM64(load); codegen_->AddSlowPath(slow_path); @@ -5098,12 +5343,12 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD __ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(), load->GetStringIndex(), load->GetString())); - GenerateGcRootFieldLoad(load, - out_loc, - out.X(), - /* offset */ 0, - /* fixup_label */ nullptr, - kCompilerReadBarrierOption); + codegen_->GenerateGcRootFieldLoad(load, + out_loc, + out.X(), + /* offset */ 0, + /* fixup_label */ nullptr, + kCompilerReadBarrierOption); return; } default: @@ -5248,36 +5493,13 @@ void LocationsBuilderARM64::VisitNewInstance(HNewInstance* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary( instruction, LocationSummary::kCallOnMainOnly); InvokeRuntimeCallingConvention calling_convention; - if (instruction->IsStringAlloc()) { - locations->AddTemp(LocationFrom(kArtMethodRegister)); - } else { - locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); - } + locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kReference)); } void InstructionCodeGeneratorARM64::VisitNewInstance(HNewInstance* instruction) { - // Note: if heap poisoning is enabled, the entry point takes cares - // of poisoning the reference. - if (instruction->IsStringAlloc()) { - // String is allocated through StringFactory. Call NewEmptyString entry point. - Location temp = instruction->GetLocations()->GetTemp(0); - MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64PointerSize); - __ Ldr(XRegisterFrom(temp), MemOperand(tr, QUICK_ENTRY_POINT(pNewEmptyString))); - __ Ldr(lr, MemOperand(XRegisterFrom(temp), code_offset.Int32Value())); - - { - // Ensure the pc position is recorded immediately after the `blr` instruction. - ExactAssemblyScope eas(GetVIXLAssembler(), - kInstructionSize, - CodeBufferCheckScope::kExactSize); - __ blr(lr); - codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); - } - } else { - codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); - CheckEntrypointTypes(); - } + codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); + CheckEntrypointTypes(); codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__); } @@ -5433,13 +5655,75 @@ void LocationsBuilderARM64::VisitRem(HRem* rem) { } } +void InstructionCodeGeneratorARM64::GenerateIntRemForPower2Denom(HRem *instruction) { + int64_t imm = Int64FromLocation(instruction->GetLocations()->InAt(1)); + uint64_t abs_imm = static_cast(AbsOrMin(imm)); + DCHECK(IsPowerOfTwo(abs_imm)) << abs_imm; + + Register out = OutputRegister(instruction); + Register dividend = InputRegisterAt(instruction, 0); + + if (abs_imm == 2) { + __ Cmp(dividend, 0); + __ And(out, dividend, 1); + __ Csneg(out, out, out, ge); + } else { + UseScratchRegisterScope temps(GetVIXLAssembler()); + Register temp = temps.AcquireSameSizeAs(out); + + __ Negs(temp, dividend); + __ And(out, dividend, abs_imm - 1); + __ And(temp, temp, abs_imm - 1); + __ Csneg(out, out, temp, mi); + } +} + +void InstructionCodeGeneratorARM64::GenerateIntRemForConstDenom(HRem *instruction) { + int64_t imm = Int64FromLocation(instruction->GetLocations()->InAt(1)); + + if (imm == 0) { + // Do not generate anything. + // DivZeroCheck would prevent any code to be executed. + return; + } + + if (IsPowerOfTwo(AbsOrMin(imm))) { + // Cases imm == -1 or imm == 1 are handled in constant folding by + // InstructionWithAbsorbingInputSimplifier. + // If the cases have survided till code generation they are handled in + // GenerateIntRemForPower2Denom becauses -1 and 1 are the power of 2 (2^0). + // The correct code is generated for them, just more instructions. + GenerateIntRemForPower2Denom(instruction); + } else { + DCHECK(imm < -2 || imm > 2) << imm; + GenerateDivRemWithAnyConstant(instruction); + } +} + +void InstructionCodeGeneratorARM64::GenerateIntRem(HRem* instruction) { + DCHECK(DataType::IsIntOrLongType(instruction->GetResultType())) + << instruction->GetResultType(); + + if (instruction->GetLocations()->InAt(1).IsConstant()) { + GenerateIntRemForConstDenom(instruction); + } else { + Register out = OutputRegister(instruction); + Register dividend = InputRegisterAt(instruction, 0); + Register divisor = InputRegisterAt(instruction, 1); + UseScratchRegisterScope temps(GetVIXLAssembler()); + Register temp = temps.AcquireSameSizeAs(out); + __ Sdiv(temp, dividend, divisor); + __ Msub(out, temp, divisor, dividend); + } +} + void InstructionCodeGeneratorARM64::VisitRem(HRem* rem) { DataType::Type type = rem->GetResultType(); switch (type) { case DataType::Type::kInt32: case DataType::Type::kInt64: { - GenerateDivRemIntegral(rem); + GenerateIntRem(rem); break; } @@ -5462,6 +5746,62 @@ void InstructionCodeGeneratorARM64::VisitRem(HRem* rem) { } } +void LocationsBuilderARM64::VisitMin(HMin* min) { + HandleBinaryOp(min); +} + +void InstructionCodeGeneratorARM64::VisitMin(HMin* min) { + HandleBinaryOp(min); +} + +void LocationsBuilderARM64::VisitMax(HMax* max) { + HandleBinaryOp(max); +} + +void InstructionCodeGeneratorARM64::VisitMax(HMax* max) { + HandleBinaryOp(max); +} + +void LocationsBuilderARM64::VisitAbs(HAbs* abs) { + LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); + break; + default: + LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType(); + } +} + +void InstructionCodeGeneratorARM64::VisitAbs(HAbs* abs) { + switch (abs->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: { + Register in_reg = InputRegisterAt(abs, 0); + Register out_reg = OutputRegister(abs); + __ Cmp(in_reg, Operand(0)); + __ Cneg(out_reg, in_reg, lt); + break; + } + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: { + FPRegister in_reg = InputFPRegisterAt(abs, 0); + FPRegister out_reg = OutputFPRegister(abs); + __ Fabs(out_reg, in_reg); + break; + } + default: + LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType(); + } +} + void LocationsBuilderARM64::VisitConstructorFence(HConstructorFence* constructor_fence) { constructor_fence->SetLocations(nullptr); } @@ -5905,7 +6245,7 @@ void InstructionCodeGeneratorARM64::GenerateReferenceLoadTwoRegisters( } } -void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad( +void CodeGeneratorARM64::GenerateGcRootFieldLoad( HInstruction* instruction, Location root, Register obj, @@ -5939,9 +6279,8 @@ void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad( DCHECK(temps.IsAvailable(ip0)); DCHECK(temps.IsAvailable(ip1)); temps.Exclude(ip0, ip1); - uint32_t custom_data = - linker::Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg.GetCode()); - vixl::aarch64::Label* cbnz_label = codegen_->NewBakerReadBarrierPatch(custom_data); + uint32_t custom_data = EncodeBakerReadBarrierGcRootData(root_reg.GetCode()); + vixl::aarch64::Label* cbnz_label = NewBakerReadBarrierPatch(custom_data); EmissionCheckScope guard(GetVIXLAssembler(), 3 * vixl::aarch64::kInstructionSize); vixl::aarch64::Label return_address; @@ -5970,14 +6309,14 @@ void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad( // Slow path marking the GC root `root`. The entrypoint will // be loaded by the slow path code. SlowPathCodeARM64* slow_path = - new (codegen_->GetScopedAllocator()) ReadBarrierMarkSlowPathARM64(instruction, root); - codegen_->AddSlowPath(slow_path); + new (GetScopedAllocator()) ReadBarrierMarkSlowPathARM64(instruction, root); + AddSlowPath(slow_path); // /* GcRoot */ root = *(obj + offset) if (fixup_label == nullptr) { __ Ldr(root_reg, MemOperand(obj, offset)); } else { - codegen_->EmitLdrOffsetPlaceholder(fixup_label, root_reg, obj); + EmitLdrOffsetPlaceholder(fixup_label, root_reg, obj); } static_assert( sizeof(mirror::CompressedReference) == sizeof(GcRoot), @@ -5997,10 +6336,10 @@ void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad( if (fixup_label == nullptr) { __ Add(root_reg.X(), obj.X(), offset); } else { - codegen_->EmitAddPlaceholder(fixup_label, root_reg.X(), obj.X()); + EmitAddPlaceholder(fixup_label, root_reg.X(), obj.X()); } // /* mirror::Object* */ root = root->Read() - codegen_->GenerateReadBarrierForRootSlow(instruction, root, root); + GenerateReadBarrierForRootSlow(instruction, root, root); } } else { // Plain GC root load with no read barrier. @@ -6008,12 +6347,12 @@ void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad( if (fixup_label == nullptr) { __ Ldr(root_reg, MemOperand(obj, offset)); } else { - codegen_->EmitLdrOffsetPlaceholder(fixup_label, root_reg, obj.X()); + EmitLdrOffsetPlaceholder(fixup_label, root_reg, obj.X()); } // Note that GC roots are not affected by heap poisoning, thus we // do not have to unpoison `root_reg` here. } - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__); + MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__); } void CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, @@ -6062,9 +6401,7 @@ void CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier(HInstruction* ins DCHECK(temps.IsAvailable(ip0)); DCHECK(temps.IsAvailable(ip1)); temps.Exclude(ip0, ip1); - uint32_t custom_data = linker::Arm64RelativePatcher::EncodeBakerReadBarrierFieldData( - base.GetCode(), - obj.GetCode()); + uint32_t custom_data = EncodeBakerReadBarrierFieldData(base.GetCode(), obj.GetCode()); vixl::aarch64::Label* cbnz_label = NewBakerReadBarrierPatch(custom_data); { @@ -6149,8 +6486,7 @@ void CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* ins DCHECK(temps.IsAvailable(ip0)); DCHECK(temps.IsAvailable(ip1)); temps.Exclude(ip0, ip1); - uint32_t custom_data = - linker::Arm64RelativePatcher::EncodeBakerReadBarrierArrayData(temp.GetCode()); + uint32_t custom_data = EncodeBakerReadBarrierArrayData(temp.GetCode()); vixl::aarch64::Label* cbnz_label = NewBakerReadBarrierPatch(custom_data); __ Add(temp.X(), obj.X(), Operand(data_offset)); @@ -6344,7 +6680,7 @@ void CodeGeneratorARM64::GenerateRawReferenceLoad(HInstruction* instruction, // ArrayGet and UnsafeGetObject and UnsafeCASObject intrinsics cases. // /* HeapReference */ ref = *(obj + offset + (index << scale_factor)) if (index.IsConstant()) { - uint32_t computed_offset = offset + (Int64ConstantFrom(index) << scale_factor); + uint32_t computed_offset = offset + (Int64FromLocation(index) << scale_factor); EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes); Load(type, ref_reg, HeapOperand(obj, computed_offset)); if (needs_null_check) { @@ -6510,5 +6846,176 @@ void CodeGeneratorARM64::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_ #undef __ #undef QUICK_ENTRY_POINT +#define __ assembler.GetVIXLAssembler()-> + +static void EmitGrayCheckAndFastPath(arm64::Arm64Assembler& assembler, + vixl::aarch64::Register base_reg, + vixl::aarch64::MemOperand& lock_word, + vixl::aarch64::Label* slow_path, + vixl::aarch64::Label* throw_npe = nullptr) { + // Load the lock word containing the rb_state. + __ Ldr(ip0.W(), lock_word); + // Given the numeric representation, it's enough to check the low bit of the rb_state. + static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0"); + static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1"); + __ Tbnz(ip0.W(), LockWord::kReadBarrierStateShift, slow_path); + static_assert( + BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET == BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET, + "Field and array LDR offsets must be the same to reuse the same code."); + // To throw NPE, we return to the fast path; the artificial dependence below does not matter. + if (throw_npe != nullptr) { + __ Bind(throw_npe); + } + // Adjust the return address back to the LDR (1 instruction; 2 for heap poisoning). + static_assert(BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4), + "Field LDR must be 1 instruction (4B) before the return address label; " + " 2 instructions (8B) for heap poisoning."); + __ Add(lr, lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET); + // Introduce a dependency on the lock_word including rb_state, + // to prevent load-load reordering, and without using + // a memory barrier (which would be more expensive). + __ Add(base_reg, base_reg, Operand(ip0, LSR, 32)); + __ Br(lr); // And return back to the function. + // Note: The fake dependency is unnecessary for the slow path. +} + +// Load the read barrier introspection entrypoint in register `entrypoint`. +static void LoadReadBarrierMarkIntrospectionEntrypoint(arm64::Arm64Assembler& assembler, + vixl::aarch64::Register entrypoint) { + // entrypoint = Thread::Current()->pReadBarrierMarkReg16, i.e. pReadBarrierMarkIntrospection. + DCHECK_EQ(ip0.GetCode(), 16u); + const int32_t entry_point_offset = + Thread::ReadBarrierMarkEntryPointsOffset(ip0.GetCode()); + __ Ldr(entrypoint, MemOperand(tr, entry_point_offset)); +} + +void CodeGeneratorARM64::CompileBakerReadBarrierThunk(Arm64Assembler& assembler, + uint32_t encoded_data, + /*out*/ std::string* debug_name) { + BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); + switch (kind) { + case BakerReadBarrierKind::kField: { + auto base_reg = + Register::GetXRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(base_reg.GetCode()); + auto holder_reg = + Register::GetXRegFromCode(BakerReadBarrierSecondRegField::Decode(encoded_data)); + CheckValidReg(holder_reg.GetCode()); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip0, ip1); + // If base_reg differs from holder_reg, the offset was too large and we must have emitted + // an explicit null check before the load. Otherwise, for implicit null checks, we need to + // null-check the holder as we do not necessarily do that check before going to the thunk. + vixl::aarch64::Label throw_npe_label; + vixl::aarch64::Label* throw_npe = nullptr; + if (GetCompilerOptions().GetImplicitNullChecks() && holder_reg.Is(base_reg)) { + throw_npe = &throw_npe_label; + __ Cbz(holder_reg.W(), throw_npe); + } + // Check if the holder is gray and, if not, add fake dependency to the base register + // and return to the LDR instruction to load the reference. Otherwise, use introspection + // to load the reference and call the entrypoint that performs further checks on the + // reference and marks it if needed. + vixl::aarch64::Label slow_path; + MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value()); + EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, throw_npe); + __ Bind(&slow_path); + MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET); + __ Ldr(ip0.W(), ldr_address); // Load the LDR (immediate) unsigned offset. + LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); + __ Ubfx(ip0.W(), ip0.W(), 10, 12); // Extract the offset. + __ Ldr(ip0.W(), MemOperand(base_reg, ip0, LSL, 2)); // Load the reference. + // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference. + __ Br(ip1); // Jump to the entrypoint. + break; + } + case BakerReadBarrierKind::kArray: { + auto base_reg = + Register::GetXRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(base_reg.GetCode()); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip0, ip1); + vixl::aarch64::Label slow_path; + int32_t data_offset = + mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value(); + MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset); + DCHECK_LT(lock_word.GetOffset(), 0); + EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path); + __ Bind(&slow_path); + MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET); + __ Ldr(ip0.W(), ldr_address); // Load the LDR (register) unsigned offset. + LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); + __ Ubfx(ip0, ip0, 16, 6); // Extract the index register, plus 32 (bit 21 is set). + __ Bfi(ip1, ip0, 3, 6); // Insert ip0 to the entrypoint address to create + // a switch case target based on the index register. + __ Mov(ip0, base_reg); // Move the base register to ip0. + __ Br(ip1); // Jump to the entrypoint's array switch case. + break; + } + case BakerReadBarrierKind::kGcRoot: { + // Check if the reference needs to be marked and if so (i.e. not null, not marked yet + // and it does not have a forwarding address), call the correct introspection entrypoint; + // otherwise return the reference (or the extracted forwarding address). + // There is no gray bit check for GC roots. + auto root_reg = + Register::GetWRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(root_reg.GetCode()); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip0, ip1); + vixl::aarch64::Label return_label, not_marked, forwarding_address; + __ Cbz(root_reg, &return_label); + MemOperand lock_word(root_reg.X(), mirror::Object::MonitorOffset().Int32Value()); + __ Ldr(ip0.W(), lock_word); + __ Tbz(ip0.W(), LockWord::kMarkBitStateShift, ¬_marked); + __ Bind(&return_label); + __ Br(lr); + __ Bind(¬_marked); + __ Tst(ip0.W(), Operand(ip0.W(), LSL, 1)); + __ B(&forwarding_address, mi); + LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); + // Adjust the art_quick_read_barrier_mark_introspection address in IP1 to + // art_quick_read_barrier_mark_introspection_gc_roots. + __ Add(ip1, ip1, Operand(BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRYPOINT_OFFSET)); + __ Mov(ip0.W(), root_reg); + __ Br(ip1); + __ Bind(&forwarding_address); + __ Lsl(root_reg, ip0.W(), LockWord::kForwardingAddressShift); + __ Br(lr); + break; + } + default: + LOG(FATAL) << "Unexpected kind: " << static_cast(kind); + UNREACHABLE(); + } + + if (GetCompilerOptions().GenerateAnyDebugInfo()) { + std::ostringstream oss; + oss << "BakerReadBarrierThunk"; + switch (kind) { + case BakerReadBarrierKind::kField: + oss << "Field_r" << BakerReadBarrierFirstRegField::Decode(encoded_data) + << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data); + break; + case BakerReadBarrierKind::kArray: + oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + break; + case BakerReadBarrierKind::kGcRoot: + oss << "GcRoot_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + break; + } + *debug_name = oss.str(); + } +} + +#undef __ + } // namespace arm64 } // namespace art diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 0654046de5d6847fd26edab5536740d6d6fa8072..93bab3180c04c6da3a0b5f6af3acbb175f8acc40 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -17,7 +17,7 @@ #ifndef ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_ #define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_ -#include "arch/arm64/quick_method_frame_info_arm64.h" +#include "base/bit_field.h" #include "code_generator.h" #include "common_arm64.h" #include "dex/dex_file_types.h" @@ -36,6 +36,11 @@ #pragma GCC diagnostic pop namespace art { + +namespace linker { +class Arm64RelativePatcherTest; +} // namespace linker + namespace arm64 { class CodeGeneratorARM64; @@ -264,6 +269,8 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator { private: void GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path, vixl::aarch64::Register class_reg); + void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, + vixl::aarch64::Register temp); void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor); void HandleBinaryOp(HBinaryOperation* instr); @@ -303,17 +310,6 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator { uint32_t offset, Location maybe_temp, ReadBarrierOption read_barrier_option); - // Generate a GC root reference load: - // - // root <- *(obj + offset) - // - // while honoring read barriers based on read_barrier_option. - void GenerateGcRootFieldLoad(HInstruction* instruction, - Location root, - vixl::aarch64::Register obj, - uint32_t offset, - vixl::aarch64::Label* fixup_label, - ReadBarrierOption read_barrier_option); // Generate a floating-point comparison. void GenerateFcmp(HInstruction* instruction); @@ -326,7 +322,12 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator { void DivRemOneOrMinusOne(HBinaryOperation* instruction); void DivRemByPowerOfTwo(HBinaryOperation* instruction); void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction); - void GenerateDivRemIntegral(HBinaryOperation* instruction); + void GenerateIntDiv(HDiv* instruction); + void GenerateIntDivForConstDenom(HDiv *instruction); + void GenerateIntDivForPower2Denom(HDiv *instruction); + void GenerateIntRem(HRem* instruction); + void GenerateIntRemForConstDenom(HRem *instruction); + void GenerateIntRemForPower2Denom(HRem *instruction); void HandleGoto(HInstruction* got, HBasicBlock* successor); vixl::aarch64::MemOperand VecAddress( @@ -403,7 +404,6 @@ class ParallelMoveResolverARM64 : public ParallelMoveResolverNoSwap { class CodeGeneratorARM64 : public CodeGenerator { public: CodeGeneratorARM64(HGraph* graph, - const Arm64InstructionSetFeatures& isa_features, const CompilerOptions& compiler_options, OptimizingCompilerStats* stats = nullptr); virtual ~CodeGeneratorARM64() {} @@ -476,9 +476,7 @@ class CodeGeneratorARM64 : public CodeGenerator { return InstructionSet::kArm64; } - const Arm64InstructionSetFeatures& GetInstructionSetFeatures() const { - return isa_features_; - } + const Arm64InstructionSetFeatures& GetInstructionSetFeatures() const; void Initialize() OVERRIDE { block_labels_.resize(GetGraph()->GetBlocks().size()); @@ -561,7 +559,21 @@ class CodeGeneratorARM64 : public CodeGenerator { UNIMPLEMENTED(FATAL); } - // Add a new PC-relative method patch for an instruction and return the label + // Add a new boot image intrinsic patch for an instruction and return the label + // to be bound before the instruction. The instruction will be either the + // ADRP (pass `adrp_label = null`) or the ADD (pass `adrp_label` pointing + // to the associated ADRP patch label). + vixl::aarch64::Label* NewBootImageIntrinsicPatch(uint32_t intrinsic_data, + vixl::aarch64::Label* adrp_label = nullptr); + + // Add a new boot image relocation patch for an instruction and return the label + // to be bound before the instruction. The instruction will be either the + // ADRP (pass `adrp_label = null`) or the LDR (pass `adrp_label` pointing + // to the associated ADRP patch label). + vixl::aarch64::Label* NewBootImageRelRoPatch(uint32_t boot_image_offset, + vixl::aarch64::Label* adrp_label = nullptr); + + // Add a new boot image method patch for an instruction and return the label // to be bound before the instruction. The instruction will be either the // ADRP (pass `adrp_label = null`) or the ADD (pass `adrp_label` pointing // to the associated ADRP patch label). @@ -575,7 +587,7 @@ class CodeGeneratorARM64 : public CodeGenerator { vixl::aarch64::Label* NewMethodBssEntryPatch(MethodReference target_method, vixl::aarch64::Label* adrp_label = nullptr); - // Add a new PC-relative type patch for an instruction and return the label + // Add a new boot image type patch for an instruction and return the label // to be bound before the instruction. The instruction will be either the // ADRP (pass `adrp_label = null`) or the ADD (pass `adrp_label` pointing // to the associated ADRP patch label). @@ -591,7 +603,7 @@ class CodeGeneratorARM64 : public CodeGenerator { dex::TypeIndex type_index, vixl::aarch64::Label* adrp_label = nullptr); - // Add a new PC-relative string patch for an instruction and return the label + // Add a new boot image string patch for an instruction and return the label // to be bound before the instruction. The instruction will be either the // ADRP (pass `adrp_label = null`) or the ADD (pass `adrp_label` pointing // to the associated ADRP patch label). @@ -627,10 +639,28 @@ class CodeGeneratorARM64 : public CodeGenerator { vixl::aarch64::Register out, vixl::aarch64::Register base); + void LoadBootImageAddress(vixl::aarch64::Register reg, uint32_t boot_image_reference); + void AllocateInstanceForIntrinsic(HInvokeStaticOrDirect* invoke, uint32_t boot_image_offset); + void EmitLinkerPatches(ArenaVector* linker_patches) OVERRIDE; + bool NeedsThunkCode(const linker::LinkerPatch& patch) const OVERRIDE; + void EmitThunkCode(const linker::LinkerPatch& patch, + /*out*/ ArenaVector* code, + /*out*/ std::string* debug_name) OVERRIDE; void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; + // Generate a GC root reference load: + // + // root <- *(obj + offset) + // + // while honoring read barriers based on read_barrier_option. + void GenerateGcRootFieldLoad(HInstruction* instruction, + Location root, + vixl::aarch64::Register obj, + uint32_t offset, + vixl::aarch64::Label* fixup_label, + ReadBarrierOption read_barrier_option); // Fast path implementation of ReadBarrier::Barrier for a heap // reference field load when Baker's read barriers are used. void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, @@ -765,6 +795,62 @@ class CodeGeneratorARM64 : public CodeGenerator { void GenerateExplicitNullCheck(HNullCheck* instruction) OVERRIDE; private: + // Encoding of thunk type and data for link-time generated thunks for Baker read barriers. + + enum class BakerReadBarrierKind : uint8_t { + kField, // Field get or array get with constant offset (i.e. constant index). + kArray, // Array get with index in register. + kGcRoot, // GC root load. + kLast = kGcRoot + }; + + static constexpr uint32_t kBakerReadBarrierInvalidEncodedReg = /* sp/zr is invalid */ 31u; + + static constexpr size_t kBitsForBakerReadBarrierKind = + MinimumBitsToStore(static_cast(BakerReadBarrierKind::kLast)); + static constexpr size_t kBakerReadBarrierBitsForRegister = + MinimumBitsToStore(kBakerReadBarrierInvalidEncodedReg); + using BakerReadBarrierKindField = + BitField; + using BakerReadBarrierFirstRegField = + BitField; + using BakerReadBarrierSecondRegField = + BitField; + + static void CheckValidReg(uint32_t reg) { + DCHECK(reg < vixl::aarch64::lr.GetCode() && + reg != vixl::aarch64::ip0.GetCode() && + reg != vixl::aarch64::ip1.GetCode()) << reg; + } + + static inline uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, uint32_t holder_reg) { + CheckValidReg(base_reg); + CheckValidReg(holder_reg); + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kField) | + BakerReadBarrierFirstRegField::Encode(base_reg) | + BakerReadBarrierSecondRegField::Encode(holder_reg); + } + + static inline uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { + CheckValidReg(base_reg); + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kArray) | + BakerReadBarrierFirstRegField::Encode(base_reg) | + BakerReadBarrierSecondRegField::Encode(kBakerReadBarrierInvalidEncodedReg); + } + + static inline uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg) { + CheckValidReg(root_reg); + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kGcRoot) | + BakerReadBarrierFirstRegField::Encode(root_reg) | + BakerReadBarrierSecondRegField::Encode(kBakerReadBarrierInvalidEncodedReg); + } + + void CompileBakerReadBarrierThunk(Arm64Assembler& assembler, + uint32_t encoded_data, + /*out*/ std::string* debug_name); + using Uint64ToLiteralMap = ArenaSafeMap*>; using Uint32ToLiteralMap = ArenaSafeMap*>; using StringToLiteralMap = ArenaSafeMap boot_image_method_patches_; // PC-relative method patch info for kBssEntry. ArenaDeque method_bss_entry_patches_; @@ -828,10 +914,12 @@ class CodeGeneratorARM64 : public CodeGenerator { ArenaDeque boot_image_type_patches_; // PC-relative type patch info for kBssEntry. ArenaDeque type_bss_entry_patches_; - // PC-relative String patch info; type depends on configuration (intern table or boot image PIC). + // PC-relative String patch info for kBootImageLinkTimePcRelative. ArenaDeque boot_image_string_patches_; // PC-relative String patch info for kBssEntry. ArenaDeque string_bss_entry_patches_; + // PC-relative patch info for IntrinsicObjects. + ArenaDeque boot_image_intrinsic_patches_; // Baker read barrier patch info. ArenaDeque baker_read_barrier_patches_; @@ -840,6 +928,7 @@ class CodeGeneratorARM64 : public CodeGenerator { // Patches for class literals in JIT compiled code. TypeToLiteralMap jit_class_patches_; + friend class linker::Arm64RelativePatcherTest; DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM64); }; diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 2f495fc15fdf0cd78ff8a73a1a8a67c20fa330fc..deab239362dacc3bc0c8d3d41f1dfd9f56f35477 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -27,9 +27,10 @@ #include "compiled_method.h" #include "entrypoints/quick/quick_entrypoints.h" #include "gc/accounting/card_table.h" +#include "gc/space/image_space.h" #include "heap_poisoning.h" +#include "intrinsics.h" #include "intrinsics_arm_vixl.h" -#include "linker/arm/relative_patcher_thumb2.h" #include "linker/linker_patch.h" #include "mirror/array-inl.h" #include "mirror/class-inl.h" @@ -94,9 +95,6 @@ constexpr bool kBakerReadBarrierLinkTimeThunksEnableForFields = true; constexpr bool kBakerReadBarrierLinkTimeThunksEnableForArrays = true; constexpr bool kBakerReadBarrierLinkTimeThunksEnableForGcRoots = true; -// The reserved entrypoint register for link-time generated thunks. -const vixl32::Register kBakerCcEntrypointRegister = r4; - // Using a base helps identify when we hit Marking Register check breakpoints. constexpr int kMarkingRegisterCheckBreakCodeBaseCode = 0x10; @@ -111,18 +109,6 @@ constexpr int kMarkingRegisterCheckBreakCodeBaseCode = 0x10; // Marker that code is yet to be, and must, be implemented. #define TODO_VIXL32(level) LOG(level) << __PRETTY_FUNCTION__ << " unimplemented " -static inline void ExcludeIPAndBakerCcEntrypointRegister(UseScratchRegisterScope* temps, - HInstruction* instruction) { - DCHECK(temps->IsAvailable(ip)); - temps->Exclude(ip); - DCHECK(!temps->IsAvailable(kBakerCcEntrypointRegister)); - DCHECK_EQ(kBakerCcEntrypointRegister.GetCode(), - linker::Thumb2RelativePatcher::kBakerCcEntrypointRegister); - DCHECK_NE(instruction->GetLocations()->GetTempCount(), 0u); - DCHECK(RegisterFrom(instruction->GetLocations()->GetTemp( - instruction->GetLocations()->GetTempCount() - 1u)).Is(kBakerCcEntrypointRegister)); -} - static inline void EmitPlaceholderBne(CodeGeneratorARMVIXL* codegen, vixl32::Label* patch_label) { ExactAssemblyScope eas(codegen->GetVIXLAssembler(), kMaxInstructionSizeInBytes); __ bind(patch_label); @@ -1517,6 +1503,10 @@ void CodeGeneratorARMVIXL::DumpFloatingPointRegister(std::ostream& stream, int r stream << vixl32::SRegister(reg); } +const ArmInstructionSetFeatures& CodeGeneratorARMVIXL::GetInstructionSetFeatures() const { + return *GetCompilerOptions().GetInstructionSetFeatures()->AsArmInstructionSetFeatures(); +} + static uint32_t ComputeSRegisterListMask(const SRegisterList& regs) { uint32_t mask = 0; for (uint32_t i = regs.GetFirstSRegister().GetCode(); @@ -2334,7 +2324,6 @@ vixl32::Label* CodeGeneratorARMVIXL::GetFinalLabel(HInstruction* instruction, } CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph, - const ArmInstructionSetFeatures& isa_features, const CompilerOptions& compiler_options, OptimizingCompilerStats* stats) : CodeGenerator(graph, @@ -2351,7 +2340,6 @@ CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph, instruction_visitor_(graph, this), move_resolver_(graph->GetAllocator(), this), assembler_(graph->GetAllocator()), - isa_features_(isa_features), uint32_literals_(std::less(), graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), @@ -2360,6 +2348,7 @@ CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph, type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), + boot_image_intrinsic_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), baker_read_barrier_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), jit_string_patches_(StringReferenceValueComparator(), graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), @@ -2422,6 +2411,80 @@ void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) { FixJumpTables(); GetAssembler()->FinalizeCode(); CodeGenerator::Finalize(allocator); + + // Verify Baker read barrier linker patches. + if (kIsDebugBuild) { + ArrayRef code = allocator->GetMemory(); + for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) { + DCHECK(info.label.IsBound()); + uint32_t literal_offset = info.label.GetLocation(); + DCHECK_ALIGNED(literal_offset, 2u); + + auto GetInsn16 = [&code](uint32_t offset) { + DCHECK_ALIGNED(offset, 2u); + return (static_cast(code[offset + 0]) << 0) + + (static_cast(code[offset + 1]) << 8); + }; + auto GetInsn32 = [=](uint32_t offset) { + return (GetInsn16(offset) << 16) + (GetInsn16(offset + 2u) << 0); + }; + + uint32_t encoded_data = info.custom_data; + BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); + // Check that the next instruction matches the expected LDR. + switch (kind) { + case BakerReadBarrierKind::kField: { + BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); + if (width == BakerReadBarrierWidth::kWide) { + DCHECK_GE(code.size() - literal_offset, 8u); + uint32_t next_insn = GetInsn32(literal_offset + 4u); + // LDR (immediate), encoding T3, with correct base_reg. + CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register. + const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(next_insn & 0xffff0000u, 0xf8d00000u | (base_reg << 16)); + } else { + DCHECK_GE(code.size() - literal_offset, 6u); + uint32_t next_insn = GetInsn16(literal_offset + 4u); + // LDR (immediate), encoding T1, with correct base_reg. + CheckValidReg(next_insn & 0x7u); // Check destination register. + const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(next_insn & 0xf838u, 0x6800u | (base_reg << 3)); + } + break; + } + case BakerReadBarrierKind::kArray: { + DCHECK_GE(code.size() - literal_offset, 8u); + uint32_t next_insn = GetInsn32(literal_offset + 4u); + // LDR (register) with correct base_reg, S=1 and option=011 (LDR Wt, [Xn, Xm, LSL #2]). + CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register. + const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(next_insn & 0xffff0ff0u, 0xf8500020u | (base_reg << 16)); + CheckValidReg(next_insn & 0xf); // Check index register + break; + } + case BakerReadBarrierKind::kGcRoot: { + BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); + if (width == BakerReadBarrierWidth::kWide) { + DCHECK_GE(literal_offset, 4u); + uint32_t prev_insn = GetInsn32(literal_offset - 4u); + // LDR (immediate), encoding T3, with correct root_reg. + const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(prev_insn & 0xfff0f000u, 0xf8d00000u | (root_reg << 12)); + } else { + DCHECK_GE(literal_offset, 2u); + uint32_t prev_insn = GetInsn16(literal_offset - 2u); + // LDR (immediate), encoding T1, with correct root_reg. + const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(prev_insn & 0xf807u, 0x6800u | root_reg); + } + break; + } + default: + LOG(FATAL) << "Unexpected kind: " << static_cast(kind); + UNREACHABLE(); + } + } + } } void CodeGeneratorARMVIXL::SetupBlockedRegisters() const { @@ -3684,6 +3747,15 @@ void InstructionCodeGeneratorARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* 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); @@ -4690,6 +4762,299 @@ void InstructionCodeGeneratorARMVIXL::VisitRem(HRem* rem) { } } +static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) { + LocationSummary* locations = new (allocator) LocationSummary(minmax); + switch (minmax->GetResultType()) { + case DataType::Type::kInt32: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + case DataType::Type::kFloat32: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); + locations->AddTemp(Location::RequiresRegister()); + break; + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + default: + LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType(); + } +} + +void InstructionCodeGeneratorARMVIXL::GenerateMinMaxInt(LocationSummary* locations, bool is_min) { + Location op1_loc = locations->InAt(0); + Location op2_loc = locations->InAt(1); + Location out_loc = locations->Out(); + + vixl32::Register op1 = RegisterFrom(op1_loc); + vixl32::Register op2 = RegisterFrom(op2_loc); + vixl32::Register out = RegisterFrom(out_loc); + + __ Cmp(op1, op2); + + { + ExactAssemblyScope aas(GetVIXLAssembler(), + 3 * kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); + + __ ite(is_min ? lt : gt); + __ mov(is_min ? lt : gt, out, op1); + __ mov(is_min ? ge : le, out, op2); + } +} + +void InstructionCodeGeneratorARMVIXL::GenerateMinMaxLong(LocationSummary* locations, bool is_min) { + Location op1_loc = locations->InAt(0); + Location op2_loc = locations->InAt(1); + Location out_loc = locations->Out(); + + // Optimization: don't generate any code if inputs are the same. + if (op1_loc.Equals(op2_loc)) { + DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in location builder. + return; + } + + vixl32::Register op1_lo = LowRegisterFrom(op1_loc); + vixl32::Register op1_hi = HighRegisterFrom(op1_loc); + vixl32::Register op2_lo = LowRegisterFrom(op2_loc); + vixl32::Register op2_hi = HighRegisterFrom(op2_loc); + vixl32::Register out_lo = LowRegisterFrom(out_loc); + vixl32::Register out_hi = HighRegisterFrom(out_loc); + UseScratchRegisterScope temps(GetVIXLAssembler()); + const vixl32::Register temp = temps.Acquire(); + + DCHECK(op1_lo.Is(out_lo)); + DCHECK(op1_hi.Is(out_hi)); + + // Compare op1 >= op2, or op1 < op2. + __ Cmp(out_lo, op2_lo); + __ Sbcs(temp, out_hi, op2_hi); + + // Now GE/LT condition code is correct for the long comparison. + { + vixl32::ConditionType cond = is_min ? ge : lt; + ExactAssemblyScope it_scope(GetVIXLAssembler(), + 3 * kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); + __ itt(cond); + __ mov(cond, out_lo, op2_lo); + __ mov(cond, out_hi, op2_hi); + } +} + +void InstructionCodeGeneratorARMVIXL::GenerateMinMaxFloat(HInstruction* minmax, bool is_min) { + LocationSummary* locations = minmax->GetLocations(); + Location op1_loc = locations->InAt(0); + Location op2_loc = locations->InAt(1); + Location out_loc = locations->Out(); + + // Optimization: don't generate any code if inputs are the same. + if (op1_loc.Equals(op2_loc)) { + DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in location builder. + return; + } + + vixl32::SRegister op1 = SRegisterFrom(op1_loc); + vixl32::SRegister op2 = SRegisterFrom(op2_loc); + vixl32::SRegister out = SRegisterFrom(out_loc); + + UseScratchRegisterScope temps(GetVIXLAssembler()); + const vixl32::Register temp1 = temps.Acquire(); + vixl32::Register temp2 = RegisterFrom(locations->GetTemp(0)); + vixl32::Label nan, done; + vixl32::Label* final_label = codegen_->GetFinalLabel(minmax, &done); + + DCHECK(op1.Is(out)); + + __ Vcmp(op1, op2); + __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR); + __ B(vs, &nan, /* far_target */ false); // if un-ordered, go to NaN handling. + + // op1 <> op2 + vixl32::ConditionType cond = is_min ? gt : lt; + { + ExactAssemblyScope it_scope(GetVIXLAssembler(), + 2 * kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); + __ it(cond); + __ vmov(cond, F32, out, op2); + } + // for <>(not equal), we've done min/max calculation. + __ B(ne, final_label, /* far_target */ false); + + // handle op1 == op2, max(+0.0,-0.0), min(+0.0,-0.0). + __ Vmov(temp1, op1); + __ Vmov(temp2, op2); + if (is_min) { + __ Orr(temp1, temp1, temp2); + } else { + __ And(temp1, temp1, temp2); + } + __ Vmov(out, temp1); + __ B(final_label); + + // handle NaN input. + __ Bind(&nan); + __ Movt(temp1, High16Bits(kNanFloat)); // 0x7FC0xxxx is a NaN. + __ Vmov(out, temp1); + + if (done.IsReferenced()) { + __ Bind(&done); + } +} + +void InstructionCodeGeneratorARMVIXL::GenerateMinMaxDouble(HInstruction* minmax, bool is_min) { + LocationSummary* locations = minmax->GetLocations(); + Location op1_loc = locations->InAt(0); + Location op2_loc = locations->InAt(1); + Location out_loc = locations->Out(); + + // Optimization: don't generate any code if inputs are the same. + if (op1_loc.Equals(op2_loc)) { + DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in. + return; + } + + vixl32::DRegister op1 = DRegisterFrom(op1_loc); + vixl32::DRegister op2 = DRegisterFrom(op2_loc); + vixl32::DRegister out = DRegisterFrom(out_loc); + vixl32::Label handle_nan_eq, done; + vixl32::Label* final_label = codegen_->GetFinalLabel(minmax, &done); + + DCHECK(op1.Is(out)); + + __ Vcmp(op1, op2); + __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR); + __ B(vs, &handle_nan_eq, /* far_target */ false); // if un-ordered, go to NaN handling. + + // op1 <> op2 + vixl32::ConditionType cond = is_min ? gt : lt; + { + ExactAssemblyScope it_scope(GetVIXLAssembler(), + 2 * kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); + __ it(cond); + __ vmov(cond, F64, out, op2); + } + // for <>(not equal), we've done min/max calculation. + __ B(ne, final_label, /* far_target */ false); + + // handle op1 == op2, max(+0.0,-0.0). + if (!is_min) { + __ Vand(F64, out, op1, op2); + __ B(final_label); + } + + // handle op1 == op2, min(+0.0,-0.0), NaN input. + __ Bind(&handle_nan_eq); + __ Vorr(F64, out, op1, op2); // assemble op1/-0.0/NaN. + + if (done.IsReferenced()) { + __ Bind(&done); + } +} + +void InstructionCodeGeneratorARMVIXL::GenerateMinMax(HBinaryOperation* minmax, bool is_min) { + DataType::Type type = minmax->GetResultType(); + switch (type) { + case DataType::Type::kInt32: + GenerateMinMaxInt(minmax->GetLocations(), is_min); + break; + case DataType::Type::kInt64: + GenerateMinMaxLong(minmax->GetLocations(), is_min); + break; + case DataType::Type::kFloat32: + GenerateMinMaxFloat(minmax, is_min); + break; + case DataType::Type::kFloat64: + GenerateMinMaxDouble(minmax, is_min); + break; + default: + LOG(FATAL) << "Unexpected type for HMinMax " << type; + } +} + +void LocationsBuilderARMVIXL::VisitMin(HMin* min) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), min); +} + +void InstructionCodeGeneratorARMVIXL::VisitMin(HMin* min) { + GenerateMinMax(min, /*is_min*/ true); +} + +void LocationsBuilderARMVIXL::VisitMax(HMax* max) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), max); +} + +void InstructionCodeGeneratorARMVIXL::VisitMax(HMax* max) { + GenerateMinMax(max, /*is_min*/ false); +} + +void LocationsBuilderARMVIXL::VisitAbs(HAbs* abs) { + LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + locations->AddTemp(Location::RequiresRegister()); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); + break; + default: + LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType(); + } +} + +void InstructionCodeGeneratorARMVIXL::VisitAbs(HAbs* abs) { + LocationSummary* locations = abs->GetLocations(); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: { + vixl32::Register in_reg = RegisterFrom(locations->InAt(0)); + vixl32::Register out_reg = RegisterFrom(locations->Out()); + vixl32::Register mask = RegisterFrom(locations->GetTemp(0)); + __ Asr(mask, in_reg, 31); + __ Add(out_reg, in_reg, mask); + __ Eor(out_reg, out_reg, mask); + break; + } + case DataType::Type::kInt64: { + Location in = locations->InAt(0); + vixl32::Register in_reg_lo = LowRegisterFrom(in); + vixl32::Register in_reg_hi = HighRegisterFrom(in); + Location output = locations->Out(); + vixl32::Register out_reg_lo = LowRegisterFrom(output); + vixl32::Register out_reg_hi = HighRegisterFrom(output); + DCHECK(!out_reg_lo.Is(in_reg_hi)) << "Diagonal overlap unexpected."; + vixl32::Register mask = RegisterFrom(locations->GetTemp(0)); + __ Asr(mask, in_reg_hi, 31); + __ Adds(out_reg_lo, in_reg_lo, mask); + __ Adc(out_reg_hi, in_reg_hi, mask); + __ Eor(out_reg_lo, out_reg_lo, mask); + __ Eor(out_reg_hi, out_reg_hi, mask); + break; + } + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + __ Vabs(OutputVRegister(abs), InputVRegisterAt(abs, 0)); + break; + default: + LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType(); + } +} void LocationsBuilderARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) { LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction); @@ -5114,35 +5479,15 @@ void InstructionCodeGeneratorARMVIXL::VisitUShr(HUShr* ushr) { void LocationsBuilderARMVIXL::VisitNewInstance(HNewInstance* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary( instruction, LocationSummary::kCallOnMainOnly); - if (instruction->IsStringAlloc()) { - locations->AddTemp(LocationFrom(kMethodRegister)); - } else { - InvokeRuntimeCallingConventionARMVIXL calling_convention; - locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); - } + InvokeRuntimeCallingConventionARMVIXL calling_convention; + locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); locations->SetOut(LocationFrom(r0)); } void InstructionCodeGeneratorARMVIXL::VisitNewInstance(HNewInstance* instruction) { - // Note: if heap poisoning is enabled, the entry point takes cares - // of poisoning the reference. - if (instruction->IsStringAlloc()) { - // String is allocated through StringFactory. Call NewEmptyString entry point. - vixl32::Register temp = RegisterFrom(instruction->GetLocations()->GetTemp(0)); - MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize); - GetAssembler()->LoadFromOffset(kLoadWord, temp, tr, QUICK_ENTRY_POINT(pNewEmptyString)); - GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, code_offset.Int32Value()); - // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used. - ExactAssemblyScope aas(GetVIXLAssembler(), - vixl32::k16BitT32InstructionSizeInBytes, - CodeBufferCheckScope::kExactSize); - __ blx(lr); - codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); - } else { - codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); - CheckEntrypointTypes(); - } - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 10); + codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); + CheckEntrypointTypes(); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 11); } void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) { @@ -5162,7 +5507,7 @@ void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) { codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc()); CheckEntrypointTypes(); DCHECK(!codegen_->IsLeafMethod()); - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 11); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 12); } void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) { @@ -5612,8 +5957,6 @@ void LocationsBuilderARMVIXL::HandleFieldGet(HInstruction* instruction, if (field_info.GetFieldOffset().Uint32Value() >= kReferenceLoadMinFarOffset) { locations->AddTemp(Location::RequiresRegister()); } - // And we always need the reserved entrypoint register. - locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister.GetCode())); } else { locations->AddTemp(Location::RequiresRegister()); } @@ -5726,11 +6069,11 @@ void InstructionCodeGeneratorARMVIXL::HandleFieldGet(HInstruction* instruction, case DataType::Type::kReference: { // /* HeapReference */ out = *(base + offset) if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { - Location temp_loc = locations->GetTemp(0); + Location maybe_temp = (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location(); // Note that a potential implicit null check is handled in this // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier call. codegen_->GenerateFieldLoadWithBakerReadBarrier( - instruction, out, base, offset, temp_loc, /* needs_null_check */ true); + instruction, out, base, offset, maybe_temp, /* needs_null_check */ true); if (is_volatile) { codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny); } @@ -6029,8 +6372,6 @@ void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) { object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap); } if (object_array_get_with_read_barrier && kUseBakerReadBarrier) { - // We need a temporary register for the read barrier marking slow - // path in CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier. if (kBakerReadBarrierLinkTimeThunksEnableForFields && !Runtime::Current()->UseJitCompilation() && instruction->GetIndex()->IsConstant()) { @@ -6043,16 +6384,10 @@ void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) { if (offset >= kReferenceLoadMinFarOffset) { locations->AddTemp(Location::RequiresRegister()); } - // And we always need the reserved entrypoint register. - locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister.GetCode())); - } else if (kBakerReadBarrierLinkTimeThunksEnableForArrays && - !Runtime::Current()->UseJitCompilation() && - !instruction->GetIndex()->IsConstant()) { - // We need a non-scratch temporary for the array data pointer. - locations->AddTemp(Location::RequiresRegister()); - // And we always need the reserved entrypoint register. - locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister.GetCode())); } else { + // If using introspection, we need a non-scratch temporary for the array data pointer. + // Otherwise, we need a temporary register for the read barrier marking slow + // path in CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier. locations->AddTemp(Location::RequiresRegister()); } } else if (mirror::kUseStringCompression && instruction->IsStringCharAt()) { @@ -6165,20 +6500,22 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { // /* HeapReference */ out = // *(obj + data_offset + index * sizeof(HeapReference)) if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { - Location temp = locations->GetTemp(0); // Note that a potential implicit null check is handled in this // CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier call. DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0))); if (index.IsConstant()) { // Array load with a constant index can be treated as a field load. + Location maybe_temp = + (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location(); data_offset += Int32ConstantFrom(index) << DataType::SizeShift(type); codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction, out_loc, obj, data_offset, - locations->GetTemp(0), + maybe_temp, /* needs_null_check */ false); } else { + Location temp = locations->GetTemp(0); codegen_->GenerateArrayLoadWithBakerReadBarrier( instruction, out_loc, obj, data_offset, index, temp, /* needs_null_check */ false); } @@ -6741,7 +7078,7 @@ void InstructionCodeGeneratorARMVIXL::VisitSuspendCheck(HSuspendCheck* instructi return; } GenerateSuspendCheck(instruction, nullptr); - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 12); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 13); } void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instruction, @@ -7033,7 +7370,7 @@ HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind( case HLoadClass::LoadKind::kReferrersClass: break; case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: - case HLoadClass::LoadKind::kBootImageClassTable: + case HLoadClass::LoadKind::kBootImageRelRo: case HLoadClass::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -7086,13 +7423,6 @@ void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) { // For non-Baker read barrier we have a temp-clobbering call. } } - if (kUseBakerReadBarrier && kBakerReadBarrierLinkTimeThunksEnableForGcRoots) { - if (load_kind == HLoadClass::LoadKind::kBssEntry || - (load_kind == HLoadClass::LoadKind::kReferrersClass && - !Runtime::Current()->UseJitCompilation())) { - locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister.GetCode())); - } - } } // NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not @@ -7101,7 +7431,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ 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()); @@ -7120,11 +7450,11 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ DCHECK(!cls->MustGenerateClinitCheck()); // /* GcRoot */ out = current_method->declaring_class_ vixl32::Register current_method = InputRegisterAt(cls, 0); - GenerateGcRootFieldLoad(cls, - out_loc, - current_method, - ArtMethod::DeclaringClassOffset().Int32Value(), - read_barrier_option); + codegen_->GenerateGcRootFieldLoad(cls, + out_loc, + current_method, + ArtMethod::DeclaringClassOffset().Int32Value(), + read_barrier_option); break; } case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: { @@ -7143,25 +7473,19 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address)); break; } - case HLoadClass::LoadKind::kBootImageClassTable: { + case HLoadClass::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = - codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex()); + codegen_->NewBootImageRelRoPatch(codegen_->GetBootImageOffset(cls)); codegen_->EmitMovwMovtPlaceholder(labels, out); __ Ldr(out, MemOperand(out, /* offset */ 0)); - // Extract the reference from the slot data, i.e. clear the hash bits. - int32_t masked_hash = ClassTable::TableSlot::MaskHash( - ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex()))); - if (masked_hash != 0) { - __ Sub(out, out, Operand(masked_hash)); - } break; } case HLoadClass::LoadKind::kBssEntry: { CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex()); codegen_->EmitMovwMovtPlaceholder(labels, out); - GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option); + codegen_->GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option); generate_null_check = true; break; } @@ -7170,7 +7494,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ cls->GetTypeIndex(), cls->GetClass())); // /* GcRoot */ out = *out - GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option); + codegen_->GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option); break; } case HLoadClass::LoadKind::kRuntimeCall: @@ -7193,10 +7517,30 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ } else { __ Bind(slow_path->GetExitLabel()); } - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 14); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 15); } } +void LocationsBuilderARMVIXL::VisitLoadMethodHandle(HLoadMethodHandle* load) { + InvokeRuntimeCallingConventionARMVIXL calling_convention; + Location location = LocationFrom(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorARMVIXL::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + +void LocationsBuilderARMVIXL::VisitLoadMethodType(HLoadMethodType* load) { + InvokeRuntimeCallingConventionARMVIXL calling_convention; + Location location = LocationFrom(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorARMVIXL::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + void LocationsBuilderARMVIXL::VisitClinitCheck(HClinitCheck* check) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath); @@ -7236,11 +7580,72 @@ void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck( __ Bind(slow_path->GetExitLabel()); } +void InstructionCodeGeneratorARMVIXL::GenerateBitstringTypeCheckCompare( + HTypeCheckInstruction* check, + vixl32::Register temp, + vixl32::FlagsUpdate flags_update) { + uint32_t path_to_root = check->GetBitstringPathToRoot(); + uint32_t mask = check->GetBitstringMask(); + DCHECK(IsPowerOfTwo(mask + 1)); + size_t mask_bits = WhichPowerOf2(mask + 1); + + // Note that HInstanceOf shall check for zero value in `temp` but HCheckCast needs + // the Z flag for BNE. This is indicated by the `flags_update` parameter. + if (mask_bits == 16u) { + // Load only the bitstring part of the status word. + __ Ldrh(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value())); + // Check if the bitstring bits are equal to `path_to_root`. + if (flags_update == SetFlags) { + __ Cmp(temp, path_to_root); + } else { + __ Sub(temp, temp, path_to_root); + } + } else { + // /* uint32_t */ temp = temp->status_ + __ Ldr(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value())); + if (GetAssembler()->ShifterOperandCanHold(SUB, path_to_root)) { + // Compare the bitstring bits using SUB. + __ Sub(temp, temp, path_to_root); + // Shift out bits that do not contribute to the comparison. + __ Lsl(flags_update, temp, temp, dchecked_integral_cast(32u - mask_bits)); + } else if (IsUint<16>(path_to_root)) { + if (temp.IsLow()) { + // Note: Optimized for size but contains one more dependent instruction than necessary. + // MOVW+SUB(register) would be 8 bytes unless we find a low-reg temporary but the + // macro assembler would use the high reg IP for the constant by default. + // Compare the bitstring bits using SUB. + __ Sub(temp, temp, path_to_root & 0x00ffu); // 16-bit SUB (immediate) T2 + __ Sub(temp, temp, path_to_root & 0xff00u); // 32-bit SUB (immediate) T3 + // Shift out bits that do not contribute to the comparison. + __ Lsl(flags_update, temp, temp, dchecked_integral_cast(32u - mask_bits)); + } else { + // Extract the bitstring bits. + __ Ubfx(temp, temp, 0, mask_bits); + // Check if the bitstring bits are equal to `path_to_root`. + if (flags_update == SetFlags) { + __ Cmp(temp, path_to_root); + } else { + __ Sub(temp, temp, path_to_root); + } + } + } else { + // Shift out bits that do not contribute to the comparison. + __ Lsl(temp, temp, dchecked_integral_cast(32u - mask_bits)); + // Check if the shifted bitstring bits are equal to `path_to_root << (32u - mask_bits)`. + if (flags_update == SetFlags) { + __ Cmp(temp, path_to_root << (32u - mask_bits)); + } else { + __ Sub(temp, temp, path_to_root << (32u - mask_bits)); + } + } + } +} + HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) { switch (desired_string_load_kind) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: - case HLoadString::LoadKind::kBootImageInternTable: + case HLoadString::LoadKind::kBootImageRelRo: case HLoadString::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -7271,9 +7676,6 @@ void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) { // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK() // that the the kPrimNot result register is the same as the first argument register. locations->SetCustomSlowPathCallerSaves(caller_saves); - if (kUseBakerReadBarrier && kBakerReadBarrierLinkTimeThunksEnableForGcRoots) { - locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister.GetCode())); - } } else { // For non-Baker read barrier we have a temp-clobbering call. } @@ -7304,10 +7706,10 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address)); return; } - case HLoadString::LoadKind::kBootImageInternTable: { + case HLoadString::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = - codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex()); + codegen_->NewBootImageRelRoPatch(codegen_->GetBootImageOffset(load)); codegen_->EmitMovwMovtPlaceholder(labels, out); __ Ldr(out, MemOperand(out, /* offset */ 0)); return; @@ -7317,13 +7719,14 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex()); codegen_->EmitMovwMovtPlaceholder(labels, out); - GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); + codegen_->GenerateGcRootFieldLoad( + load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); LoadStringSlowPathARMVIXL* slow_path = new (codegen_->GetScopedAllocator()) LoadStringSlowPathARMVIXL(load); 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: { @@ -7331,7 +7734,8 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE load->GetStringIndex(), load->GetString())); // /* GcRoot */ out = *out - GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); + codegen_->GenerateGcRootFieldLoad( + load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); return; } default: @@ -7344,7 +7748,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE __ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_); codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc()); CheckEntrypointTypes(); - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 16); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 17); } static int32_t GetExceptionTlsOffset() { @@ -7427,6 +7831,8 @@ void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; + case TypeCheckKind::kBitstringCheck: + break; } LocationSummary* locations = @@ -7435,14 +7841,17 @@ void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } // The "out" register is used as a temporary, so it overlaps with the inputs. // Note that TypeCheckSlowPathARM uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind)); - if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { - codegen_->MaybeAddBakerCcEntrypointTempForFields(locations); - } } void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) { @@ -7450,7 +7859,9 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); vixl32::Register obj = InputRegisterAt(instruction, 0); - vixl32::Register cls = InputRegisterAt(instruction, 1); + vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) + ? vixl32::Register() + : InputRegisterAt(instruction, 1); Location out_loc = locations->Out(); vixl32::Register out = OutputRegister(instruction); const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -7690,6 +8101,26 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) __ B(slow_path->GetEntryLabel()); break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, out, DontCare); + // If `out` is a low reg and we would have another low reg temp, we could + // optimize this as RSBS+ADC, see GenerateConditionWithZero(). + // + // Also, in some cases when `out` is a low reg and we're loading a constant to IP + // it would make sense to use CMP+MOV+IT+MOV instead of SUB+CLZ+LSR as the code size + // would be the same and we would have fewer direct data dependencies. + codegen_->GenerateConditionWithZero(kCondEQ, out, out); // CLZ+LSR + break; + } } if (done.IsReferenced()) { @@ -7707,7 +8138,13 @@ void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } @@ -7716,7 +8153,9 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); vixl32::Register obj = InputRegisterAt(instruction, 0); - vixl32::Register cls = InputRegisterAt(instruction, 1); + vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) + ? vixl32::Register() + : InputRegisterAt(instruction, 1); Location temp_loc = locations->GetTemp(0); vixl32::Register temp = RegisterFrom(temp_loc); const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); @@ -7901,6 +8340,20 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { __ B(ne, &start_loop, /* far_target */ false); break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + maybe_temp2_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, temp, SetFlags); + __ B(ne, type_check_slow_path->GetEntryLabel()); + break; + } } if (done.IsReferenced()) { __ Bind(&done); @@ -7925,7 +8378,7 @@ void InstructionCodeGeneratorARMVIXL::VisitMonitorOperation(HMonitorOperation* i } else { CheckEntrypointTypes(); } - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 17); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 18); } void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) { @@ -8330,7 +8783,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadTwoRegisters( } } -void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad( +void CodeGeneratorARMVIXL::GenerateGcRootFieldLoad( HInstruction* instruction, Location root, vixl32::Register obj, @@ -8359,11 +8812,10 @@ void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad( // return_address: UseScratchRegisterScope temps(GetVIXLAssembler()); - ExcludeIPAndBakerCcEntrypointRegister(&temps, instruction); + temps.Exclude(ip); bool narrow = CanEmitNarrowLdr(root_reg, obj, offset); - uint32_t custom_data = linker::Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData( - root_reg.GetCode(), narrow); - vixl32::Label* bne_label = codegen_->NewBakerReadBarrierPatch(custom_data); + uint32_t custom_data = EncodeBakerReadBarrierGcRootData(root_reg.GetCode(), narrow); + vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data); vixl::EmissionCheckScope guard(GetVIXLAssembler(), 4 * vixl32::kMaxInstructionSizeInBytes); vixl32::Label return_address; @@ -8374,7 +8826,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad( DCHECK_LT(offset, kReferenceLoadMinFarOffset); ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset(); __ ldr(EncodingSize(narrow ? Narrow : Wide), root_reg, MemOperand(obj, offset)); - EmitPlaceholderBne(codegen_, bne_label); + EmitPlaceholderBne(this, bne_label); __ Bind(&return_address); DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(), narrow ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET @@ -8394,8 +8846,8 @@ void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad( // Slow path marking the GC root `root`. The entrypoint will // be loaded by the slow path code. SlowPathCodeARMVIXL* slow_path = - new (codegen_->GetScopedAllocator()) ReadBarrierMarkSlowPathARMVIXL(instruction, root); - codegen_->AddSlowPath(slow_path); + new (GetScopedAllocator()) ReadBarrierMarkSlowPathARMVIXL(instruction, root); + AddSlowPath(slow_path); // /* GcRoot */ root = *(obj + offset) GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset); @@ -8416,7 +8868,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad( // /* GcRoot* */ root = obj + offset __ Add(root_reg, obj, offset); // /* mirror::Object* */ root = root->Read() - codegen_->GenerateReadBarrierForRootSlow(instruction, root, root); + GenerateReadBarrierForRootSlow(instruction, root, root); } } else { // Plain GC root load with no read barrier. @@ -8425,17 +8877,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad( // Note that GC roots are not affected by heap poisoning, thus we // do not have to unpoison `root_reg` here. } - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 18); -} - -void CodeGeneratorARMVIXL::MaybeAddBakerCcEntrypointTempForFields(LocationSummary* locations) { - DCHECK(kEmitCompilerReadBarrier); - DCHECK(kUseBakerReadBarrier); - if (kBakerReadBarrierLinkTimeThunksEnableForFields) { - if (!Runtime::Current()->UseJitCompilation()) { - locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister.GetCode())); - } - } + MaybeGenerateMarkingRegisterCheck(/* code */ 19); } void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, @@ -8475,7 +8917,6 @@ void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* i vixl32::Register base = obj; if (offset >= kReferenceLoadMinFarOffset) { base = RegisterFrom(temp); - DCHECK(!base.Is(kBakerCcEntrypointRegister)); static_assert(IsPowerOfTwo(kReferenceLoadMinFarOffset), "Expecting a power of 2."); __ Add(base, obj, Operand(offset & ~(kReferenceLoadMinFarOffset - 1u))); offset &= (kReferenceLoadMinFarOffset - 1u); @@ -8485,9 +8926,8 @@ void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* i DCHECK(!narrow); } UseScratchRegisterScope temps(GetVIXLAssembler()); - ExcludeIPAndBakerCcEntrypointRegister(&temps, instruction); - uint32_t custom_data = linker::Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( - base.GetCode(), obj.GetCode(), narrow); + temps.Exclude(ip); + uint32_t custom_data = EncodeBakerReadBarrierFieldData(base.GetCode(), obj.GetCode(), narrow); vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data); { @@ -8517,7 +8957,7 @@ void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* i 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; } @@ -8569,12 +9009,10 @@ void CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier(HInstruction* i vixl32::Register index_reg = RegisterFrom(index, DataType::Type::kInt32); vixl32::Register ref_reg = RegisterFrom(ref, DataType::Type::kReference); vixl32::Register data_reg = RegisterFrom(temp, DataType::Type::kInt32); // Raw pointer. - DCHECK(!data_reg.Is(kBakerCcEntrypointRegister)); UseScratchRegisterScope temps(GetVIXLAssembler()); - ExcludeIPAndBakerCcEntrypointRegister(&temps, instruction); - uint32_t custom_data = - linker::Thumb2RelativePatcher::EncodeBakerReadBarrierArrayData(data_reg.GetCode()); + temps.Exclude(ip); + uint32_t custom_data = EncodeBakerReadBarrierArrayData(data_reg.GetCode()); vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data); __ Add(data_reg, obj, Operand(data_offset)); @@ -8597,7 +9035,7 @@ void CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier(HInstruction* i 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; } @@ -8651,7 +9089,7 @@ void CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier(HInstructio // 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, @@ -8706,12 +9144,12 @@ void CodeGeneratorARMVIXL::UpdateReferenceFieldWithBakerReadBarrier(HInstruction // 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, Location ref, - vixl::aarch32::Register obj, + vixl32::Register obj, uint32_t offset, Location index, ScaleFactor scale_factor, @@ -8901,6 +9339,14 @@ void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall( case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: __ Mov(RegisterFrom(temp), Operand::From(invoke->GetMethodAddress())); break; + case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: { + uint32_t boot_image_offset = GetBootImageOffset(invoke); + PcRelativePatchInfo* labels = NewBootImageRelRoPatch(boot_image_offset); + vixl32::Register temp_reg = RegisterFrom(temp); + EmitMovwMovtPlaceholder(labels, temp_reg); + GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, temp_reg, /* offset*/ 0); + break; + } case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: { PcRelativePatchInfo* labels = NewMethodBssEntryPatch( MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex())); @@ -8998,6 +9444,18 @@ void CodeGeneratorARMVIXL::GenerateVirtualCall( } } +CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageIntrinsicPatch( + uint32_t intrinsic_data) { + return NewPcRelativePatch(/* dex_file */ nullptr, intrinsic_data, &boot_image_intrinsic_patches_); +} + +CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageRelRoPatch( + uint32_t boot_image_offset) { + return NewPcRelativePatch(/* dex_file */ nullptr, + boot_image_offset, + &boot_image_method_patches_); +} + CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageMethodPatch( MethodReference target_method) { return NewPcRelativePatch( @@ -9036,7 +9494,7 @@ CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativePa return &patches->back(); } -vixl::aarch32::Label* CodeGeneratorARMVIXL::NewBakerReadBarrierPatch(uint32_t custom_data) { +vixl32::Label* CodeGeneratorARMVIXL::NewBakerReadBarrierPatch(uint32_t custom_data) { baker_read_barrier_patches_.emplace_back(custom_data); return &baker_read_barrier_patches_.back().label; } @@ -9068,6 +9526,46 @@ VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitClassLiteral(const DexFil }); } +void CodeGeneratorARMVIXL::LoadBootImageAddress(vixl32::Register reg, + uint32_t boot_image_reference) { + if (GetCompilerOptions().IsBootImage()) { + CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = + NewBootImageIntrinsicPatch(boot_image_reference); + EmitMovwMovtPlaceholder(labels, reg); + } else if (GetCompilerOptions().GetCompilePic()) { + DCHECK(Runtime::Current()->IsAotCompiler()); + CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = + NewBootImageRelRoPatch(boot_image_reference); + EmitMovwMovtPlaceholder(labels, reg); + __ Ldr(reg, MemOperand(reg, /* offset */ 0)); + } else { + gc::Heap* heap = Runtime::Current()->GetHeap(); + DCHECK(!heap->GetBootImageSpaces().empty()); + uintptr_t address = + reinterpret_cast(heap->GetBootImageSpaces()[0]->Begin() + boot_image_reference); + __ Ldr(reg, DeduplicateBootImageAddressLiteral(dchecked_integral_cast(address))); + } +} + +void CodeGeneratorARMVIXL::AllocateInstanceForIntrinsic(HInvokeStaticOrDirect* invoke, + uint32_t boot_image_offset) { + DCHECK(invoke->IsStatic()); + InvokeRuntimeCallingConventionARMVIXL calling_convention; + vixl32::Register argument = calling_convention.GetRegisterAt(0); + if (GetCompilerOptions().IsBootImage()) { + DCHECK_EQ(boot_image_offset, IntrinsicVisitor::IntegerValueOfInfo::kInvalidReference); + // Load the class the same way as for HLoadClass::LoadKind::kBootImageLinkTimePcRelative. + MethodReference target_method = invoke->GetTargetMethod(); + dex::TypeIndex type_idx = target_method.dex_file->GetMethodId(target_method.index).class_idx_; + PcRelativePatchInfo* labels = NewBootImageTypePatch(*target_method.dex_file, type_idx); + EmitMovwMovtPlaceholder(labels, argument); + } else { + LoadBootImageAddress(argument, boot_image_offset); + } + InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc()); + CheckEntrypointTypes(); +} + template inline void CodeGeneratorARMVIXL::EmitPcRelativeLinkerPatches( const ArenaDeque& infos, @@ -9088,6 +9586,15 @@ inline void CodeGeneratorARMVIXL::EmitPcRelativeLinkerPatches( } } +template +linker::LinkerPatch NoDexFileAdapter(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t boot_image_offset) { + DCHECK(target_dex_file == nullptr); // Unused for these patches, should be null. + return Factory(literal_offset, pc_insn_offset, boot_image_offset); +} + void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector* linker_patches) { DCHECK(linker_patches->empty()); size_t size = @@ -9097,6 +9604,7 @@ void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector* l /* MOVW+MOVT for each entry */ 2u * type_bss_entry_patches_.size() + /* MOVW+MOVT for each entry */ 2u * boot_image_string_patches_.size() + /* MOVW+MOVT for each entry */ 2u * string_bss_entry_patches_.size() + + /* MOVW+MOVT for each entry */ 2u * boot_image_intrinsic_patches_.size() + baker_read_barrier_patches_.size(); linker_patches->reserve(size); if (GetCompilerOptions().IsBootImage()) { @@ -9106,12 +9614,14 @@ void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector* l boot_image_type_patches_, linker_patches); EmitPcRelativeLinkerPatches( boot_image_string_patches_, linker_patches); + EmitPcRelativeLinkerPatches>( + boot_image_intrinsic_patches_, linker_patches); } else { - DCHECK(boot_image_method_patches_.empty()); - EmitPcRelativeLinkerPatches( - boot_image_type_patches_, linker_patches); - EmitPcRelativeLinkerPatches( - boot_image_string_patches_, linker_patches); + EmitPcRelativeLinkerPatches>( + boot_image_method_patches_, linker_patches); + DCHECK(boot_image_type_patches_.empty()); + DCHECK(boot_image_string_patches_.empty()); + DCHECK(boot_image_intrinsic_patches_.empty()); } EmitPcRelativeLinkerPatches( method_bss_entry_patches_, linker_patches); @@ -9126,6 +9636,45 @@ void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector* l DCHECK_EQ(size, linker_patches->size()); } +bool CodeGeneratorARMVIXL::NeedsThunkCode(const linker::LinkerPatch& patch) const { + return patch.GetType() == linker::LinkerPatch::Type::kBakerReadBarrierBranch || + patch.GetType() == linker::LinkerPatch::Type::kCallRelative; +} + +void CodeGeneratorARMVIXL::EmitThunkCode(const linker::LinkerPatch& patch, + /*out*/ ArenaVector* code, + /*out*/ std::string* debug_name) { + arm::ArmVIXLAssembler assembler(GetGraph()->GetAllocator()); + switch (patch.GetType()) { + case linker::LinkerPatch::Type::kCallRelative: + // The thunk just uses the entry point in the ArtMethod. This works even for calls + // to the generic JNI and interpreter trampolines. + assembler.LoadFromOffset( + arm::kLoadWord, + vixl32::pc, + vixl32::r0, + ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value()); + assembler.GetVIXLAssembler()->Bkpt(0); + if (GetCompilerOptions().GenerateAnyDebugInfo()) { + *debug_name = "MethodCallThunk"; + } + break; + case linker::LinkerPatch::Type::kBakerReadBarrierBranch: + DCHECK_EQ(patch.GetBakerCustomValue2(), 0u); + CompileBakerReadBarrierThunk(assembler, patch.GetBakerCustomValue1(), debug_name); + break; + default: + LOG(FATAL) << "Unexpected patch type " << patch.GetType(); + UNREACHABLE(); + } + + // Ensure we emit the literal pool if any. + assembler.FinalizeCode(); + code->resize(assembler.CodeSize()); + MemoryRegion code_region(code->data(), code->size()); + assembler.FinalizeInstructions(code_region); +} + VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateUint32Literal( uint32_t value, Uint32ToLiteralMap* map) { @@ -9370,5 +9919,208 @@ void CodeGeneratorARMVIXL::EmitMovwMovtPlaceholder( #undef QUICK_ENTRY_POINT #undef TODO_VIXL32 +#define __ assembler.GetVIXLAssembler()-> + +static void EmitGrayCheckAndFastPath(ArmVIXLAssembler& assembler, + vixl32::Register base_reg, + vixl32::MemOperand& lock_word, + vixl32::Label* slow_path, + int32_t raw_ldr_offset, + vixl32::Label* throw_npe = nullptr) { + // Load the lock word containing the rb_state. + __ Ldr(ip, lock_word); + // Given the numeric representation, it's enough to check the low bit of the rb_state. + static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0"); + static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1"); + __ Tst(ip, Operand(LockWord::kReadBarrierStateMaskShifted)); + __ B(ne, slow_path, /* is_far_target */ false); + // To throw NPE, we return to the fast path; the artificial dependence below does not matter. + if (throw_npe != nullptr) { + __ Bind(throw_npe); + } + __ Add(lr, lr, raw_ldr_offset); + // Introduce a dependency on the lock_word including rb_state, + // to prevent load-load reordering, and without using + // a memory barrier (which would be more expensive). + __ Add(base_reg, base_reg, Operand(ip, LSR, 32)); + __ Bx(lr); // And return back to the function. + // Note: The fake dependency is unnecessary for the slow path. +} + +// Load the read barrier introspection entrypoint in register `entrypoint` +static vixl32::Register LoadReadBarrierMarkIntrospectionEntrypoint(ArmVIXLAssembler& assembler) { + // The register where the read barrier introspection entrypoint is loaded + // is the marking register. We clobber it here and the entrypoint restores it to 1. + vixl32::Register entrypoint = mr; + // entrypoint = Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection. + DCHECK_EQ(ip.GetCode(), 12u); + const int32_t entry_point_offset = + Thread::ReadBarrierMarkEntryPointsOffset(ip.GetCode()); + __ Ldr(entrypoint, MemOperand(tr, entry_point_offset)); + return entrypoint; +} + +void CodeGeneratorARMVIXL::CompileBakerReadBarrierThunk(ArmVIXLAssembler& assembler, + uint32_t encoded_data, + /*out*/ std::string* debug_name) { + BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); + switch (kind) { + case BakerReadBarrierKind::kField: { + vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(base_reg.GetCode()); + vixl32::Register holder_reg(BakerReadBarrierSecondRegField::Decode(encoded_data)); + CheckValidReg(holder_reg.GetCode()); + BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip); + // If base_reg differs from holder_reg, the offset was too large and we must have emitted + // an explicit null check before the load. Otherwise, for implicit null checks, we need to + // null-check the holder as we do not necessarily do that check before going to the thunk. + vixl32::Label throw_npe_label; + vixl32::Label* throw_npe = nullptr; + if (GetCompilerOptions().GetImplicitNullChecks() && holder_reg.Is(base_reg)) { + throw_npe = &throw_npe_label; + __ CompareAndBranchIfZero(holder_reg, throw_npe, /* is_far_target */ false); + } + // Check if the holder is gray and, if not, add fake dependency to the base register + // and return to the LDR instruction to load the reference. Otherwise, use introspection + // to load the reference and call the entrypoint that performs further checks on the + // reference and marks it if needed. + vixl32::Label slow_path; + MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value()); + const int32_t raw_ldr_offset = (width == BakerReadBarrierWidth::kWide) + ? BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET + : BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET; + EmitGrayCheckAndFastPath( + assembler, base_reg, lock_word, &slow_path, raw_ldr_offset, throw_npe); + __ Bind(&slow_path); + const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 + + raw_ldr_offset; + vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler); + if (width == BakerReadBarrierWidth::kWide) { + MemOperand ldr_half_address(lr, ldr_offset + 2); + __ Ldrh(ip, ldr_half_address); // Load the LDR immediate half-word with "Rt | imm12". + __ Ubfx(ip, ip, 0, 12); // Extract the offset imm12. + __ Ldr(ip, MemOperand(base_reg, ip)); // Load the reference. + } else { + MemOperand ldr_address(lr, ldr_offset); + __ Ldrh(ip, ldr_address); // Load the LDR immediate, encoding T1. + __ Add(ep_reg, // Adjust the entrypoint address to the entrypoint + ep_reg, // for narrow LDR. + Operand(BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET)); + __ Ubfx(ip, ip, 6, 5); // Extract the imm5, i.e. offset / 4. + __ Ldr(ip, MemOperand(base_reg, ip, LSL, 2)); // Load the reference. + } + // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference. + __ Bx(ep_reg); // Jump to the entrypoint. + break; + } + case BakerReadBarrierKind::kArray: { + vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(base_reg.GetCode()); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip); + vixl32::Label slow_path; + int32_t data_offset = + mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value(); + MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset); + DCHECK_LT(lock_word.GetOffsetImmediate(), 0); + const int32_t raw_ldr_offset = BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET; + EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset); + __ Bind(&slow_path); + const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 + + raw_ldr_offset; + MemOperand ldr_address(lr, ldr_offset + 2); + __ Ldrb(ip, ldr_address); // Load the LDR (register) byte with "00 | imm2 | Rm", + // i.e. Rm+32 because the scale in imm2 is 2. + vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler); + __ Bfi(ep_reg, ip, 3, 6); // Insert ip to the entrypoint address to create + // a switch case target based on the index register. + __ Mov(ip, base_reg); // Move the base register to ip0. + __ Bx(ep_reg); // Jump to the entrypoint's array switch case. + break; + } + case BakerReadBarrierKind::kGcRoot: { + // Check if the reference needs to be marked and if so (i.e. not null, not marked yet + // and it does not have a forwarding address), call the correct introspection entrypoint; + // otherwise return the reference (or the extracted forwarding address). + // There is no gray bit check for GC roots. + vixl32::Register root_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(root_reg.GetCode()); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip); + vixl32::Label return_label, not_marked, forwarding_address; + __ CompareAndBranchIfZero(root_reg, &return_label, /* is_far_target */ false); + MemOperand lock_word(root_reg, mirror::Object::MonitorOffset().Int32Value()); + __ Ldr(ip, lock_word); + __ Tst(ip, LockWord::kMarkBitStateMaskShifted); + __ B(eq, ¬_marked); + __ Bind(&return_label); + __ Bx(lr); + __ Bind(¬_marked); + static_assert(LockWord::kStateShift == 30 && LockWord::kStateForwardingAddress == 3, + "To use 'CMP ip, #modified-immediate; BHS', we need the lock word state in " + " the highest bits and the 'forwarding address' state to have all bits set"); + __ Cmp(ip, Operand(0xc0000000)); + __ B(hs, &forwarding_address); + vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler); + // Adjust the art_quick_read_barrier_mark_introspection address in kBakerCcEntrypointRegister + // to art_quick_read_barrier_mark_introspection_gc_roots. + int32_t entrypoint_offset = (width == BakerReadBarrierWidth::kWide) + ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET + : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET; + __ Add(ep_reg, ep_reg, Operand(entrypoint_offset)); + __ Mov(ip, root_reg); + __ Bx(ep_reg); + __ Bind(&forwarding_address); + __ Lsl(root_reg, ip, LockWord::kForwardingAddressShift); + __ Bx(lr); + break; + } + default: + LOG(FATAL) << "Unexpected kind: " << static_cast(kind); + UNREACHABLE(); + } + + if (GetCompilerOptions().GenerateAnyDebugInfo()) { + std::ostringstream oss; + oss << "BakerReadBarrierThunk"; + switch (kind) { + case BakerReadBarrierKind::kField: + oss << "Field"; + if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) { + oss << "Wide"; + } + oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data) + << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data); + break; + case BakerReadBarrierKind::kArray: + oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide); + break; + case BakerReadBarrierKind::kGcRoot: + oss << "GcRoot"; + if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) { + oss << "Wide"; + } + oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + break; + } + *debug_name = oss.str(); + } +} + +#undef __ + } // namespace arm } // namespace art diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 536da41d07fae4c67b3a81297a4e856181612799..fc8cf981734eb82f8ab72638a191cbc25163192e 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -36,6 +36,11 @@ #pragma GCC diagnostic pop namespace art { + +namespace linker { +class Thumb2RelativePatcherTest; +} // namespace linker + namespace arm { // This constant is used as an approximate margin when emission of veneer and literal pools @@ -322,6 +327,9 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor); void GenerateClassInitializationCheck(LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg); + void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, + vixl::aarch32::Register temp, + vixl::aarch32::FlagsUpdate flags_update); void GenerateAndConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value); void GenerateOrrConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value); void GenerateEorConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value); @@ -349,6 +357,12 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { bool value_can_be_null); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); + void GenerateMinMaxInt(LocationSummary* locations, bool is_min); + void GenerateMinMaxLong(LocationSummary* locations, bool is_min); + void GenerateMinMaxFloat(HInstruction* minmax, bool is_min); + void GenerateMinMaxDouble(HInstruction* minmax, bool is_min); + void GenerateMinMax(HBinaryOperation* minmax, bool is_min); + // Generate a heap reference load using one register `out`: // // out <- *(out + offset) @@ -379,16 +393,6 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { uint32_t offset, Location maybe_temp, ReadBarrierOption read_barrier_option); - // Generate a GC root reference load: - // - // root <- *(obj + offset) - // - // while honoring read barriers based on read_barrier_option. - void GenerateGcRootFieldLoad(HInstruction* instruction, - Location root, - vixl::aarch32::Register obj, - uint32_t offset, - ReadBarrierOption read_barrier_option); void GenerateTestAndBranch(HInstruction* instruction, size_t condition_input_index, vixl::aarch32::Label* true_target, @@ -424,7 +428,6 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { class CodeGeneratorARMVIXL : public CodeGenerator { public: CodeGeneratorARMVIXL(HGraph* graph, - const ArmInstructionSetFeatures& isa_features, const CompilerOptions& compiler_options, OptimizingCompilerStats* stats = nullptr); virtual ~CodeGeneratorARMVIXL() {} @@ -471,6 +474,9 @@ class CodeGeneratorARMVIXL : public CodeGenerator { ParallelMoveResolver* GetMoveResolver() OVERRIDE { return &move_resolver_; } InstructionSet GetInstructionSet() const OVERRIDE { return InstructionSet::kThumb2; } + + const ArmInstructionSetFeatures& GetInstructionSetFeatures() const; + // Helper method to move a 32-bit value between two locations. void Move32(Location destination, Location source); @@ -519,8 +525,6 @@ class CodeGeneratorARMVIXL : public CodeGenerator { void Finalize(CodeAllocator* allocator) OVERRIDE; - const ArmInstructionSetFeatures& GetInstructionSetFeatures() const { return isa_features_; } - bool NeedsTwoRegisters(DataType::Type type) const OVERRIDE { return type == DataType::Type::kFloat64 || type == DataType::Type::kInt64; } @@ -574,6 +578,8 @@ class CodeGeneratorARMVIXL : public CodeGenerator { vixl::aarch32::Label add_pc_label; }; + PcRelativePatchInfo* NewBootImageIntrinsicPatch(uint32_t intrinsic_data); + PcRelativePatchInfo* NewBootImageRelRoPatch(uint32_t boot_image_offset); PcRelativePatchInfo* NewBootImageMethodPatch(MethodReference target_method); PcRelativePatchInfo* NewMethodBssEntryPatch(MethodReference target_method); PcRelativePatchInfo* NewBootImageTypePatch(const DexFile& dex_file, dex::TypeIndex type_index); @@ -595,14 +601,27 @@ class CodeGeneratorARMVIXL : public CodeGenerator { dex::TypeIndex type_index, Handle handle); + void LoadBootImageAddress(vixl::aarch32::Register reg, uint32_t boot_image_reference); + void AllocateInstanceForIntrinsic(HInvokeStaticOrDirect* invoke, uint32_t boot_image_offset); + void EmitLinkerPatches(ArenaVector* linker_patches) OVERRIDE; + bool NeedsThunkCode(const linker::LinkerPatch& patch) const OVERRIDE; + void EmitThunkCode(const linker::LinkerPatch& patch, + /*out*/ ArenaVector* code, + /*out*/ std::string* debug_name) OVERRIDE; void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; - // Maybe add the reserved entrypoint register as a temporary for field load. This temp - // is added only for AOT compilation if link-time generated thunks for fields are enabled. - void MaybeAddBakerCcEntrypointTempForFields(LocationSummary* locations); - + // Generate a GC root reference load: + // + // root <- *(obj + offset) + // + // while honoring read barriers based on read_barrier_option. + void GenerateGcRootFieldLoad(HInstruction* instruction, + Location root, + vixl::aarch32::Register obj, + uint32_t offset, + ReadBarrierOption read_barrier_option); // Fast path implementation of ReadBarrier::Barrier for a heap // reference field load when Baker's read barriers are used. void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, @@ -757,6 +776,83 @@ class CodeGeneratorARMVIXL : public CodeGenerator { vixl::aarch32::Register temp = vixl32::Register()); private: + // Encoding of thunk type and data for link-time generated thunks for Baker read barriers. + + enum class BakerReadBarrierKind : uint8_t { + kField, // Field get or array get with constant offset (i.e. constant index). + kArray, // Array get with index in register. + kGcRoot, // GC root load. + kLast = kGcRoot + }; + + enum class BakerReadBarrierWidth : uint8_t { + kWide, // 32-bit LDR (and 32-bit NEG if heap poisoning is enabled). + kNarrow, // 16-bit LDR (and 16-bit NEG if heap poisoning is enabled). + kLast = kNarrow + }; + + static constexpr uint32_t kBakerReadBarrierInvalidEncodedReg = /* pc is invalid */ 15u; + + static constexpr size_t kBitsForBakerReadBarrierKind = + MinimumBitsToStore(static_cast(BakerReadBarrierKind::kLast)); + static constexpr size_t kBakerReadBarrierBitsForRegister = + MinimumBitsToStore(kBakerReadBarrierInvalidEncodedReg); + using BakerReadBarrierKindField = + BitField; + using BakerReadBarrierFirstRegField = + BitField; + using BakerReadBarrierSecondRegField = + BitField; + static constexpr size_t kBitsForBakerReadBarrierWidth = + MinimumBitsToStore(static_cast(BakerReadBarrierWidth::kLast)); + using BakerReadBarrierWidthField = + BitField; + + static void CheckValidReg(uint32_t reg) { + DCHECK(reg < vixl::aarch32::ip.GetCode() && reg != mr.GetCode()) << reg; + } + + static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, + uint32_t holder_reg, + bool narrow) { + CheckValidReg(base_reg); + CheckValidReg(holder_reg); + DCHECK(!narrow || base_reg < 8u) << base_reg; + BakerReadBarrierWidth width = + narrow ? BakerReadBarrierWidth::kNarrow : BakerReadBarrierWidth::kWide; + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kField) | + BakerReadBarrierFirstRegField::Encode(base_reg) | + BakerReadBarrierSecondRegField::Encode(holder_reg) | + BakerReadBarrierWidthField::Encode(width); + } + + static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { + CheckValidReg(base_reg); + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kArray) | + BakerReadBarrierFirstRegField::Encode(base_reg) | + BakerReadBarrierSecondRegField::Encode(kBakerReadBarrierInvalidEncodedReg) | + BakerReadBarrierWidthField::Encode(BakerReadBarrierWidth::kWide); + } + + static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg, bool narrow) { + CheckValidReg(root_reg); + DCHECK(!narrow || root_reg < 8u) << root_reg; + BakerReadBarrierWidth width = + narrow ? BakerReadBarrierWidth::kNarrow : BakerReadBarrierWidth::kWide; + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kGcRoot) | + BakerReadBarrierFirstRegField::Encode(root_reg) | + BakerReadBarrierSecondRegField::Encode(kBakerReadBarrierInvalidEncodedReg) | + BakerReadBarrierWidthField::Encode(width); + } + + void CompileBakerReadBarrierThunk(ArmVIXLAssembler& assembler, + uint32_t encoded_data, + /*out*/ std::string* debug_name); + vixl::aarch32::Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, vixl::aarch32::Register temp); @@ -794,11 +890,11 @@ class CodeGeneratorARMVIXL : public CodeGenerator { ParallelMoveResolverARMVIXL move_resolver_; ArmVIXLAssembler assembler_; - const ArmInstructionSetFeatures& isa_features_; // Deduplication map for 32-bit literals, used for non-patchable boot image addresses. Uint32ToLiteralMap uint32_literals_; - // PC-relative method patch info for kBootImageLinkTimePcRelative. + // PC-relative method patch info for kBootImageLinkTimePcRelative/kBootImageRelRo. + // Also used for type/string patches for kBootImageRelRo (same linker patch as for methods). ArenaDeque boot_image_method_patches_; // PC-relative method patch info for kBssEntry. ArenaDeque method_bss_entry_patches_; @@ -806,10 +902,12 @@ class CodeGeneratorARMVIXL : public CodeGenerator { ArenaDeque boot_image_type_patches_; // PC-relative type patch info for kBssEntry. ArenaDeque type_bss_entry_patches_; - // PC-relative String patch info; type depends on configuration (intern table or boot image PIC). + // PC-relative String patch info for kBootImageLinkTimePcRelative. ArenaDeque boot_image_string_patches_; // PC-relative String patch info for kBssEntry. ArenaDeque string_bss_entry_patches_; + // PC-relative patch info for IntrinsicObjects. + ArenaDeque boot_image_intrinsic_patches_; // Baker read barrier patch info. ArenaDeque baker_read_barrier_patches_; @@ -818,6 +916,7 @@ class CodeGeneratorARMVIXL : public CodeGenerator { // Patches for class literals in JIT compiled code. TypeToLiteralMap jit_class_patches_; + friend class linker::Thumb2RelativePatcherTest; DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARMVIXL); }; diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 87e6d6834b72f91db53b53fb1190364001449d4f..c7295e4db1aed0db5b10d83804e20b09019c0b7e 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -26,6 +26,7 @@ #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "gc/accounting/card_table.h" +#include "gc/space/image_space.h" #include "heap_poisoning.h" #include "intrinsics.h" #include "intrinsics_mips.h" @@ -996,7 +997,6 @@ class ReadBarrierForRootSlowPathMIPS : public SlowPathCodeMIPS { }; CodeGeneratorMIPS::CodeGeneratorMIPS(HGraph* graph, - const MipsInstructionSetFeatures& isa_features, const CompilerOptions& compiler_options, OptimizingCompilerStats* stats) : CodeGenerator(graph, @@ -1013,8 +1013,8 @@ CodeGeneratorMIPS::CodeGeneratorMIPS(HGraph* graph, location_builder_(graph, this), instruction_visitor_(graph, this), move_resolver_(graph->GetAllocator(), this), - assembler_(graph->GetAllocator(), &isa_features), - isa_features_(isa_features), + assembler_(graph->GetAllocator(), + compiler_options.GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()), uint32_literals_(std::less(), graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), @@ -1023,6 +1023,7 @@ CodeGeneratorMIPS::CodeGeneratorMIPS(HGraph* graph, type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), + boot_image_intrinsic_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), jit_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), jit_class_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), clobbered_ra_(false) { @@ -1042,8 +1043,7 @@ void CodeGeneratorMIPS::Finalize(CodeAllocator* allocator) { // Adjust native pc offsets in stack maps. StackMapStream* stack_map_stream = GetStackMapStream(); for (size_t i = 0, num = stack_map_stream->GetNumberOfStackMaps(); i != num; ++i) { - uint32_t old_position = - stack_map_stream->GetStackMap(i).native_pc_code_offset.Uint32Value(InstructionSet::kMips); + uint32_t old_position = stack_map_stream->GetStackMapNativePcOffset(i); uint32_t new_position = __ GetAdjustedPosition(old_position); DCHECK_GE(new_position, old_position); stack_map_stream->SetStackMapNativePcOffset(i, new_position); @@ -1597,6 +1597,15 @@ inline void CodeGeneratorMIPS::EmitPcRelativeLinkerPatches( } } +template +linker::LinkerPatch NoDexFileAdapter(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t boot_image_offset) { + DCHECK(target_dex_file == nullptr); // Unused for these patches, should be null. + return Factory(literal_offset, pc_insn_offset, boot_image_offset); +} + void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector* linker_patches) { DCHECK(linker_patches->empty()); size_t size = @@ -1605,7 +1614,8 @@ void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector* link boot_image_type_patches_.size() + type_bss_entry_patches_.size() + boot_image_string_patches_.size() + - string_bss_entry_patches_.size(); + string_bss_entry_patches_.size() + + boot_image_intrinsic_patches_.size(); linker_patches->reserve(size); if (GetCompilerOptions().IsBootImage()) { EmitPcRelativeLinkerPatches( @@ -1614,12 +1624,14 @@ void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector* link boot_image_type_patches_, linker_patches); EmitPcRelativeLinkerPatches( boot_image_string_patches_, linker_patches); + EmitPcRelativeLinkerPatches>( + boot_image_intrinsic_patches_, linker_patches); } else { - DCHECK(boot_image_method_patches_.empty()); - EmitPcRelativeLinkerPatches( - boot_image_type_patches_, linker_patches); - EmitPcRelativeLinkerPatches( - boot_image_string_patches_, linker_patches); + EmitPcRelativeLinkerPatches>( + boot_image_method_patches_, linker_patches); + DCHECK(boot_image_type_patches_.empty()); + DCHECK(boot_image_string_patches_.empty()); + DCHECK(boot_image_intrinsic_patches_.empty()); } EmitPcRelativeLinkerPatches( method_bss_entry_patches_, linker_patches); @@ -1630,6 +1642,20 @@ void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector* link DCHECK_EQ(size, linker_patches->size()); } +CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewBootImageIntrinsicPatch( + uint32_t intrinsic_data, + const PcRelativePatchInfo* info_high) { + return NewPcRelativePatch( + /* dex_file */ nullptr, intrinsic_data, info_high, &boot_image_intrinsic_patches_); +} + +CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewBootImageRelRoPatch( + uint32_t boot_image_offset, + const PcRelativePatchInfo* info_high) { + return NewPcRelativePatch( + /* dex_file */ nullptr, boot_image_offset, info_high, &boot_image_method_patches_); +} + CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewBootImageMethodPatch( MethodReference target_method, const PcRelativePatchInfo* info_high) { @@ -1726,6 +1752,48 @@ void CodeGeneratorMIPS::EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo // offset to `out` (e.g. lw, jialc, addiu). } +void CodeGeneratorMIPS::LoadBootImageAddress(Register reg, uint32_t boot_image_reference) { + if (GetCompilerOptions().IsBootImage()) { + PcRelativePatchInfo* info_high = NewBootImageIntrinsicPatch(boot_image_reference); + PcRelativePatchInfo* info_low = NewBootImageIntrinsicPatch(boot_image_reference, info_high); + EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, /* base */ ZERO); + __ Addiu(reg, TMP, /* placeholder */ 0x5678, &info_low->label); + } else if (GetCompilerOptions().GetCompilePic()) { + DCHECK(Runtime::Current()->IsAotCompiler()); + PcRelativePatchInfo* info_high = NewBootImageRelRoPatch(boot_image_reference); + PcRelativePatchInfo* info_low = NewBootImageRelRoPatch(boot_image_reference, info_high); + EmitPcRelativeAddressPlaceholderHigh(info_high, reg, /* base */ ZERO); + __ Lw(reg, reg, /* placeholder */ 0x5678, &info_low->label); + } else { + gc::Heap* heap = Runtime::Current()->GetHeap(); + DCHECK(!heap->GetBootImageSpaces().empty()); + const uint8_t* address = heap->GetBootImageSpaces()[0]->Begin() + boot_image_reference; + __ LoadConst32(reg, dchecked_integral_cast(reinterpret_cast(address))); + } +} + +void CodeGeneratorMIPS::AllocateInstanceForIntrinsic(HInvokeStaticOrDirect* invoke, + uint32_t boot_image_offset) { + DCHECK(invoke->IsStatic()); + InvokeRuntimeCallingConvention calling_convention; + Register argument = calling_convention.GetRegisterAt(0); + if (GetCompilerOptions().IsBootImage()) { + DCHECK_EQ(boot_image_offset, IntrinsicVisitor::IntegerValueOfInfo::kInvalidReference); + // Load the class the same way as for HLoadClass::LoadKind::kBootImageLinkTimePcRelative. + MethodReference target_method = invoke->GetTargetMethod(); + dex::TypeIndex type_idx = target_method.dex_file->GetMethodId(target_method.index).class_idx_; + PcRelativePatchInfo* info_high = NewBootImageTypePatch(*target_method.dex_file, type_idx); + PcRelativePatchInfo* info_low = + NewBootImageTypePatch(*target_method.dex_file, type_idx, info_high); + EmitPcRelativeAddressPlaceholderHigh(info_high, argument, /* base */ ZERO); + __ Addiu(argument, argument, /* placeholder */ 0x5678, &info_low->label); + } else { + LoadBootImageAddress(argument, boot_image_offset); + } + InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc()); + CheckEntrypointTypes(); +} + CodeGeneratorMIPS::JitPatchInfo* CodeGeneratorMIPS::NewJitRootStringPatch( const DexFile& dex_file, dex::StringIndex string_index, @@ -1882,6 +1950,10 @@ void CodeGeneratorMIPS::DumpFloatingPointRegister(std::ostream& stream, int reg) stream << FRegister(reg); } +const MipsInstructionSetFeatures& CodeGeneratorMIPS::GetInstructionSetFeatures() const { + return *GetCompilerOptions().GetInstructionSetFeatures()->AsMipsInstructionSetFeatures(); +} + constexpr size_t kMipsDirectEntrypointRuntimeOffset = 16; void CodeGeneratorMIPS::InvokeRuntime(QuickEntrypointEnum entrypoint, @@ -1936,6 +2008,34 @@ void InstructionCodeGeneratorMIPS::GenerateClassInitializationCheck(SlowPathCode __ Bind(slow_path->GetExitLabel()); } +void InstructionCodeGeneratorMIPS::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, + Register temp) { + uint32_t path_to_root = check->GetBitstringPathToRoot(); + uint32_t mask = check->GetBitstringMask(); + DCHECK(IsPowerOfTwo(mask + 1)); + size_t mask_bits = WhichPowerOf2(mask + 1); + + if (mask_bits == 16u) { + // Load only the bitstring part of the status word. + __ LoadFromOffset( + kLoadUnsignedHalfword, temp, temp, mirror::Class::StatusOffset().Int32Value()); + // Compare the bitstring bits using XOR. + __ Xori(temp, temp, dchecked_integral_cast(path_to_root)); + } else { + // /* uint32_t */ temp = temp->status_ + __ LoadFromOffset(kLoadWord, temp, temp, mirror::Class::StatusOffset().Int32Value()); + // Compare the bitstring bits using XOR. + if (IsUint<16>(path_to_root)) { + __ Xori(temp, temp, dchecked_integral_cast(path_to_root)); + } else { + __ LoadConst32(TMP, path_to_root); + __ Xor(temp, temp, TMP); + } + // Shift out bits that do not contribute to the comparison. + __ Sll(temp, temp, 32 - mask_bits); + } +} + void InstructionCodeGeneratorMIPS::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) { __ Sync(0); // Only stype 0 is supported. } @@ -3287,7 +3387,13 @@ void LocationsBuilderMIPS::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } @@ -3296,7 +3402,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = obj_loc.AsRegister(); - Register cls = locations->InAt(1).AsRegister(); + Location cls = locations->InAt(1); Location temp_loc = locations->GetTemp(0); Register temp = temp_loc.AsRegister(); const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); @@ -3335,7 +3441,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { kWithoutReadBarrier); // Jump to slow path for throwing the exception or doing a // more involved array check. - __ Bne(temp, cls, slow_path->GetEntryLabel()); + __ Bne(temp, cls.AsRegister(), slow_path->GetEntryLabel()); break; } @@ -3361,7 +3467,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { // exception. __ Beqz(temp, slow_path->GetEntryLabel()); // Otherwise, compare the classes. - __ Bne(temp, cls, &loop); + __ Bne(temp, cls.AsRegister(), &loop); break; } @@ -3376,7 +3482,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { // Walk over the class hierarchy to find a match. MipsLabel loop; __ Bind(&loop); - __ Beq(temp, cls, &done); + __ Beq(temp, cls.AsRegister(), &done); // /* HeapReference */ temp = temp->super_class_ GenerateReferenceLoadOneRegister(instruction, temp_loc, @@ -3399,7 +3505,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { maybe_temp2_loc, kWithoutReadBarrier); // Do an exact check. - __ Beq(temp, cls, &done); + __ Beq(temp, cls.AsRegister(), &done); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference */ temp = temp->component_type_ GenerateReferenceLoadOneRegister(instruction, @@ -3458,7 +3564,21 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { // Go to next interface. __ Addiu(TMP, TMP, -2); // Compare the classes and continue the loop if they do not match. - __ Bne(AT, cls, &loop); + __ Bne(AT, cls.AsRegister(), &loop); + break; + } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + maybe_temp2_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, temp); + __ Bnez(temp, slow_path->GetEntryLabel()); break; } } @@ -7401,6 +7521,8 @@ void LocationsBuilderMIPS::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; + case TypeCheckKind::kBitstringCheck: + break; } LocationSummary* locations = @@ -7409,7 +7531,13 @@ void LocationsBuilderMIPS::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } // The output does overlap inputs. // Note that TypeCheckSlowPathMIPS uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -7421,7 +7549,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = obj_loc.AsRegister(); - Register cls = locations->InAt(1).AsRegister(); + Location cls = locations->InAt(1); Location out_loc = locations->Out(); Register out = out_loc.AsRegister(); const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -7453,7 +7581,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { maybe_temp_loc, read_barrier_option); // Classes must be equal for the instanceof to succeed. - __ Xor(out, out, cls); + __ Xor(out, out, cls.AsRegister()); __ Sltiu(out, out, 1); break; } @@ -7480,7 +7608,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { read_barrier_option); // If `out` is null, we use it for the result, and jump to `done`. __ Beqz(out, &done); - __ Bne(out, cls, &loop); + __ Bne(out, cls.AsRegister(), &loop); __ LoadConst32(out, 1); break; } @@ -7498,7 +7626,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { // Walk over the class hierarchy to find a match. MipsLabel loop, success; __ Bind(&loop); - __ Beq(out, cls, &success); + __ Beq(out, cls.AsRegister(), &success); // /* HeapReference */ out = out->super_class_ GenerateReferenceLoadOneRegister(instruction, out_loc, @@ -7525,7 +7653,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { read_barrier_option); // Do an exact check. MipsLabel success; - __ Beq(out, cls, &success); + __ Beq(out, cls.AsRegister(), &success); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference */ out = out->component_type_ GenerateReferenceLoadOneRegister(instruction, @@ -7557,7 +7685,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS( instruction, /* is_fatal */ false); codegen_->AddSlowPath(slow_path); - __ Bne(out, cls, slow_path->GetEntryLabel()); + __ Bne(out, cls.AsRegister(), slow_path->GetEntryLabel()); __ LoadConst32(out, 1); break; } @@ -7589,6 +7717,20 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { __ B(slow_path->GetEntryLabel()); break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, out); + __ Sltiu(out, out, 1); + break; + } } __ Bind(&done); @@ -7712,6 +7854,14 @@ void InstructionCodeGeneratorMIPS::VisitInvokePolymorphic(HInvokePolymorphic* in 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); @@ -7725,7 +7875,7 @@ HLoadString::LoadKind CodeGeneratorMIPS::GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) { switch (desired_string_load_kind) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: - case HLoadString::LoadKind::kBootImageInternTable: + case HLoadString::LoadKind::kBootImageRelRo: case HLoadString::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -7748,7 +7898,7 @@ HLoadClass::LoadKind CodeGeneratorMIPS::GetSupportedLoadClassKind( case HLoadClass::LoadKind::kReferrersClass: break; case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: - case HLoadClass::LoadKind::kBootImageClassTable: + case HLoadClass::LoadKind::kBootImageRelRo: case HLoadClass::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -7835,6 +7985,15 @@ void CodeGeneratorMIPS::GenerateStaticOrDirectCall( case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: __ LoadConst32(temp.AsRegister(), invoke->GetMethodAddress()); break; + case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: { + uint32_t boot_image_offset = GetBootImageOffset(invoke); + PcRelativePatchInfo* info_high = NewBootImageRelRoPatch(boot_image_offset); + PcRelativePatchInfo* info_low = NewBootImageRelRoPatch(boot_image_offset, info_high); + Register temp_reg = temp.AsRegister(); + EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base_reg); + __ Lw(temp_reg, TMP, /* placeholder */ 0x5678, &info_low->label); + break; + } case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: { PcRelativePatchInfo* info_high = NewMethodBssEntryPatch( MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex())); @@ -7956,7 +8115,7 @@ void LocationsBuilderMIPS::VisitLoadClass(HLoadClass* cls) { // We need an extra register for PC-relative literals on R2. case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: case HLoadClass::LoadKind::kBootImageAddress: - case HLoadClass::LoadKind::kBootImageClassTable: + case HLoadClass::LoadKind::kBootImageRelRo: case HLoadClass::LoadKind::kBssEntry: if (isR6) { break; @@ -8008,7 +8167,7 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF // We need an extra register for PC-relative literals on R2. case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: case HLoadClass::LoadKind::kBootImageAddress: - case HLoadClass::LoadKind::kBootImageClassTable: + case HLoadClass::LoadKind::kBootImageRelRo: case HLoadClass::LoadKind::kBssEntry: base_or_current_method_reg = (isR6 || has_irreducible_loops) ? ZERO : locations->InAt(0).AsRegister(); @@ -8065,22 +8224,17 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF } break; } - case HLoadClass::LoadKind::kBootImageClassTable: { + case HLoadClass::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); + uint32_t boot_image_offset = codegen_->GetBootImageOffset(cls); CodeGeneratorMIPS::PcRelativePatchInfo* info_high = - codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex()); + codegen_->NewBootImageRelRoPatch(boot_image_offset); CodeGeneratorMIPS::PcRelativePatchInfo* info_low = - codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex(), info_high); + codegen_->NewBootImageRelRoPatch(boot_image_offset, info_high); codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, out, base_or_current_method_reg); __ Lw(out, out, /* placeholder */ 0x5678, &info_low->label); - // Extract the reference from the slot data, i.e. clear the hash bits. - int32_t masked_hash = ClassTable::TableSlot::MaskHash( - ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex()))); - if (masked_hash != 0) { - __ Addiu(out, out, -masked_hash); - } break; } case HLoadClass::LoadKind::kBssEntry: { @@ -8138,6 +8292,26 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF } } +void LocationsBuilderMIPS::VisitLoadMethodHandle(HLoadMethodHandle* load) { + InvokeRuntimeCallingConvention calling_convention; + Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, loc, loc); +} + +void InstructionCodeGeneratorMIPS::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + +void LocationsBuilderMIPS::VisitLoadMethodType(HLoadMethodType* load) { + InvokeRuntimeCallingConvention calling_convention; + Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, loc, loc); +} + +void InstructionCodeGeneratorMIPS::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + static int32_t GetExceptionTlsOffset() { return Thread::ExceptionOffset().Int32Value(); } @@ -8171,7 +8345,7 @@ void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) { // We need an extra register for PC-relative literals on R2. case HLoadString::LoadKind::kBootImageAddress: case HLoadString::LoadKind::kBootImageLinkTimePcRelative: - case HLoadString::LoadKind::kBootImageInternTable: + case HLoadString::LoadKind::kBootImageRelRo: case HLoadString::LoadKind::kBssEntry: if (isR6) { break; @@ -8223,7 +8397,7 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_ // We need an extra register for PC-relative literals on R2. case HLoadString::LoadKind::kBootImageAddress: case HLoadString::LoadKind::kBootImageLinkTimePcRelative: - case HLoadString::LoadKind::kBootImageInternTable: + case HLoadString::LoadKind::kBootImageRelRo: case HLoadString::LoadKind::kBssEntry: base_or_current_method_reg = (isR6 || has_irreducible_loops) ? ZERO : locations->InAt(0).AsRegister(); @@ -8259,12 +8433,13 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_ } return; } - case HLoadString::LoadKind::kBootImageInternTable: { + case HLoadString::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); + uint32_t boot_image_offset = codegen_->GetBootImageOffset(load); CodeGeneratorMIPS::PcRelativePatchInfo* info_high = - codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex()); + codegen_->NewBootImageRelRoPatch(boot_image_offset); CodeGeneratorMIPS::PcRelativePatchInfo* info_low = - codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex(), info_high); + codegen_->NewBootImageRelRoPatch(boot_image_offset, info_high); codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, out, base_or_current_method_reg); @@ -8526,30 +8701,13 @@ void LocationsBuilderMIPS::VisitNewInstance(HNewInstance* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary( instruction, LocationSummary::kCallOnMainOnly); InvokeRuntimeCallingConvention calling_convention; - if (instruction->IsStringAlloc()) { - locations->AddTemp(Location::RegisterLocation(kMethodRegisterArgument)); - } else { - locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); - } + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kReference)); } void InstructionCodeGeneratorMIPS::VisitNewInstance(HNewInstance* instruction) { - // Note: if heap poisoning is enabled, the entry point takes care - // of poisoning the reference. - if (instruction->IsStringAlloc()) { - // String is allocated through StringFactory. Call NewEmptyString entry point. - Register temp = instruction->GetLocations()->GetTemp(0).AsRegister(); - MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kMipsPointerSize); - __ LoadFromOffset(kLoadWord, temp, TR, QUICK_ENTRY_POINT(pNewEmptyString)); - __ LoadFromOffset(kLoadWord, T9, temp, code_offset.Int32Value()); - __ Jalr(T9); - __ NopIfNoReordering(); - codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); - } else { - codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); - CheckEntrypointTypes(); - } + codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); + CheckEntrypointTypes(); } void LocationsBuilderMIPS::VisitNot(HNot* instruction) { @@ -8779,6 +8937,501 @@ void InstructionCodeGeneratorMIPS::VisitRem(HRem* instruction) { } } +static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) { + LocationSummary* locations = new (allocator) LocationSummary(minmax); + switch (minmax->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kOutputOverlap); + break; + default: + LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType(); + } +} + +void InstructionCodeGeneratorMIPS::GenerateMinMaxInt(LocationSummary* locations, + bool is_min, + bool isR6, + DataType::Type type) { + if (isR6) { + // Some architectures, such as ARM and MIPS (prior to r6), have a + // conditional move instruction which only changes the target + // (output) register if the condition is true (MIPS prior to r6 had + // MOVF, MOVT, MOVN, and MOVZ). The SELEQZ and SELNEZ instructions + // always change the target (output) register. If the condition is + // true the output register gets the contents of the "rs" register; + // otherwise, the output register is set to zero. One consequence + // of this is that to implement something like "rd = c==0 ? rs : rt" + // MIPS64r6 needs to use a pair of SELEQZ/SELNEZ instructions. + // After executing this pair of instructions one of the output + // registers from the pair will necessarily contain zero. Then the + // code ORs the output registers from the SELEQZ/SELNEZ instructions + // to get the final result. + // + // The initial test to see if the output register is same as the + // first input register is needed to make sure that value in the + // first input register isn't clobbered before we've finished + // computing the output value. The logic in the corresponding else + // clause performs the same task but makes sure the second input + // register isn't clobbered in the event that it's the same register + // as the output register; the else clause also handles the case + // where the output register is distinct from both the first, and the + // second input registers. + if (type == DataType::Type::kInt64) { + Register a_lo = locations->InAt(0).AsRegisterPairLow(); + Register a_hi = locations->InAt(0).AsRegisterPairHigh(); + Register b_lo = locations->InAt(1).AsRegisterPairLow(); + Register b_hi = locations->InAt(1).AsRegisterPairHigh(); + Register out_lo = locations->Out().AsRegisterPairLow(); + Register out_hi = locations->Out().AsRegisterPairHigh(); + + MipsLabel compare_done; + + if (a_lo == b_lo) { + if (out_lo != a_lo) { + __ Move(out_lo, a_lo); + __ Move(out_hi, a_hi); + } + } else { + __ Slt(TMP, b_hi, a_hi); + __ Bne(b_hi, a_hi, &compare_done); + + __ Sltu(TMP, b_lo, a_lo); + + __ Bind(&compare_done); + + if (is_min) { + __ Seleqz(AT, a_lo, TMP); + __ Selnez(out_lo, b_lo, TMP); // Safe even if out_lo == a_lo/b_lo + // because at this point we're + // done using a_lo/b_lo. + } else { + __ Selnez(AT, a_lo, TMP); + __ Seleqz(out_lo, b_lo, TMP); // ditto + } + __ Or(out_lo, out_lo, AT); + if (is_min) { + __ Seleqz(AT, a_hi, TMP); + __ Selnez(out_hi, b_hi, TMP); // ditto but for out_hi & a_hi/b_hi + } else { + __ Selnez(AT, a_hi, TMP); + __ Seleqz(out_hi, b_hi, TMP); // ditto but for out_hi & a_hi/b_hi + } + __ Or(out_hi, out_hi, AT); + } + } else { + DCHECK_EQ(type, DataType::Type::kInt32); + Register a = locations->InAt(0).AsRegister(); + Register b = locations->InAt(1).AsRegister(); + Register out = locations->Out().AsRegister(); + + if (a == b) { + if (out != a) { + __ Move(out, a); + } + } else { + __ Slt(AT, b, a); + if (is_min) { + __ Seleqz(TMP, a, AT); + __ Selnez(AT, b, AT); + } else { + __ Selnez(TMP, a, AT); + __ Seleqz(AT, b, AT); + } + __ Or(out, TMP, AT); + } + } + } else { // !isR6 + if (type == DataType::Type::kInt64) { + Register a_lo = locations->InAt(0).AsRegisterPairLow(); + Register a_hi = locations->InAt(0).AsRegisterPairHigh(); + Register b_lo = locations->InAt(1).AsRegisterPairLow(); + Register b_hi = locations->InAt(1).AsRegisterPairHigh(); + Register out_lo = locations->Out().AsRegisterPairLow(); + Register out_hi = locations->Out().AsRegisterPairHigh(); + + MipsLabel compare_done; + + if (a_lo == b_lo) { + if (out_lo != a_lo) { + __ Move(out_lo, a_lo); + __ Move(out_hi, a_hi); + } + } else { + __ Slt(TMP, a_hi, b_hi); + __ Bne(a_hi, b_hi, &compare_done); + + __ Sltu(TMP, a_lo, b_lo); + + __ Bind(&compare_done); + + if (is_min) { + if (out_lo != a_lo) { + __ Movn(out_hi, a_hi, TMP); + __ Movn(out_lo, a_lo, TMP); + } + if (out_lo != b_lo) { + __ Movz(out_hi, b_hi, TMP); + __ Movz(out_lo, b_lo, TMP); + } + } else { + if (out_lo != a_lo) { + __ Movz(out_hi, a_hi, TMP); + __ Movz(out_lo, a_lo, TMP); + } + if (out_lo != b_lo) { + __ Movn(out_hi, b_hi, TMP); + __ Movn(out_lo, b_lo, TMP); + } + } + } + } else { + DCHECK_EQ(type, DataType::Type::kInt32); + Register a = locations->InAt(0).AsRegister(); + Register b = locations->InAt(1).AsRegister(); + Register out = locations->Out().AsRegister(); + + if (a == b) { + if (out != a) { + __ Move(out, a); + } + } else { + __ Slt(AT, a, b); + if (is_min) { + if (out != a) { + __ Movn(out, a, AT); + } + if (out != b) { + __ Movz(out, b, AT); + } + } else { + if (out != a) { + __ Movz(out, a, AT); + } + if (out != b) { + __ Movn(out, b, AT); + } + } + } + } + } +} + +void InstructionCodeGeneratorMIPS::GenerateMinMaxFP(LocationSummary* locations, + bool is_min, + bool isR6, + DataType::Type type) { + FRegister out = locations->Out().AsFpuRegister(); + FRegister a = locations->InAt(0).AsFpuRegister(); + FRegister b = locations->InAt(1).AsFpuRegister(); + + if (isR6) { + MipsLabel noNaNs; + MipsLabel done; + FRegister ftmp = ((out != a) && (out != b)) ? out : FTMP; + + // When Java computes min/max it prefers a NaN to a number; the + // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of + // the inputs is a NaN and the other is a valid number, the MIPS + // instruction will return the number; Java wants the NaN value + // returned. This is why there is extra logic preceding the use of + // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a + // NaN, return the NaN, otherwise return the min/max. + if (type == DataType::Type::kFloat64) { + __ CmpUnD(FTMP, a, b); + __ Bc1eqz(FTMP, &noNaNs); + + // One of the inputs is a NaN + __ CmpEqD(ftmp, a, a); + // If a == a then b is the NaN, otherwise a is the NaN. + __ SelD(ftmp, a, b); + + if (ftmp != out) { + __ MovD(out, ftmp); + } + + __ B(&done); + + __ Bind(&noNaNs); + + if (is_min) { + __ MinD(out, a, b); + } else { + __ MaxD(out, a, b); + } + } else { + DCHECK_EQ(type, DataType::Type::kFloat32); + __ CmpUnS(FTMP, a, b); + __ Bc1eqz(FTMP, &noNaNs); + + // One of the inputs is a NaN + __ CmpEqS(ftmp, a, a); + // If a == a then b is the NaN, otherwise a is the NaN. + __ SelS(ftmp, a, b); + + if (ftmp != out) { + __ MovS(out, ftmp); + } + + __ B(&done); + + __ Bind(&noNaNs); + + if (is_min) { + __ MinS(out, a, b); + } else { + __ MaxS(out, a, b); + } + } + + __ Bind(&done); + + } else { // !isR6 + MipsLabel ordered; + MipsLabel compare; + MipsLabel select; + MipsLabel done; + + if (type == DataType::Type::kFloat64) { + __ CunD(a, b); + } else { + DCHECK_EQ(type, DataType::Type::kFloat32); + __ CunS(a, b); + } + __ Bc1f(&ordered); + + // a or b (or both) is a NaN. Return one, which is a NaN. + if (type == DataType::Type::kFloat64) { + __ CeqD(b, b); + } else { + __ CeqS(b, b); + } + __ B(&select); + + __ Bind(&ordered); + + // Neither is a NaN. + // a == b? (-0.0 compares equal with +0.0) + // If equal, handle zeroes, else compare further. + if (type == DataType::Type::kFloat64) { + __ CeqD(a, b); + } else { + __ CeqS(a, b); + } + __ Bc1f(&compare); + + // a == b either bit for bit or one is -0.0 and the other is +0.0. + if (type == DataType::Type::kFloat64) { + __ MoveFromFpuHigh(TMP, a); + __ MoveFromFpuHigh(AT, b); + } else { + __ Mfc1(TMP, a); + __ Mfc1(AT, b); + } + + if (is_min) { + // -0.0 prevails over +0.0. + __ Or(TMP, TMP, AT); + } else { + // +0.0 prevails over -0.0. + __ And(TMP, TMP, AT); + } + + if (type == DataType::Type::kFloat64) { + __ Mfc1(AT, a); + __ Mtc1(AT, out); + __ MoveToFpuHigh(TMP, out); + } else { + __ Mtc1(TMP, out); + } + __ B(&done); + + __ Bind(&compare); + + if (type == DataType::Type::kFloat64) { + if (is_min) { + // return (a <= b) ? a : b; + __ ColeD(a, b); + } else { + // return (a >= b) ? a : b; + __ ColeD(b, a); // b <= a + } + } else { + if (is_min) { + // return (a <= b) ? a : b; + __ ColeS(a, b); + } else { + // return (a >= b) ? a : b; + __ ColeS(b, a); // b <= a + } + } + + __ Bind(&select); + + if (type == DataType::Type::kFloat64) { + __ MovtD(out, a); + __ MovfD(out, b); + } else { + __ MovtS(out, a); + __ MovfS(out, b); + } + + __ Bind(&done); + } +} + +void InstructionCodeGeneratorMIPS::GenerateMinMax(HBinaryOperation* minmax, bool is_min) { + bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); + DataType::Type type = minmax->GetResultType(); + switch (type) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + GenerateMinMaxInt(minmax->GetLocations(), is_min, isR6, type); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + GenerateMinMaxFP(minmax->GetLocations(), is_min, isR6, type); + break; + default: + LOG(FATAL) << "Unexpected type for HMinMax " << type; + } +} + +void LocationsBuilderMIPS::VisitMin(HMin* min) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), min); +} + +void InstructionCodeGeneratorMIPS::VisitMin(HMin* min) { + GenerateMinMax(min, /*is_min*/ true); +} + +void LocationsBuilderMIPS::VisitMax(HMax* max) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), max); +} + +void InstructionCodeGeneratorMIPS::VisitMax(HMax* max) { + GenerateMinMax(max, /*is_min*/ false); +} + +void LocationsBuilderMIPS::VisitAbs(HAbs* abs) { + LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); + break; + default: + LOG(FATAL) << "Unexpected abs type " << abs->GetResultType(); + } +} + +void InstructionCodeGeneratorMIPS::GenerateAbsFP(LocationSummary* locations, + DataType::Type type, + bool isR2OrNewer, + bool isR6) { + FRegister in = locations->InAt(0).AsFpuRegister(); + FRegister out = locations->Out().AsFpuRegister(); + + // Note, as a "quality of implementation", rather than pure "spec compliance", we require that + // Math.abs() clears the sign bit (but changes nothing else) for all numbers, including NaN + // (signaling NaN may become quiet though). + // + // The ABS.fmt instructions (abs.s and abs.d) do exactly that when NAN2008=1 (R6). For this case, + // both regular floating point numbers and NAN values are treated alike, only the sign bit is + // affected by this instruction. + // But when NAN2008=0 (R2 and before), the ABS.fmt instructions can't be used. For this case, any + // NaN operand signals invalid operation. This means that other bits (not just sign bit) might be + // changed when doing abs(NaN). Because of that, we clear sign bit in a different way. + if (isR6) { + if (type == DataType::Type::kFloat64) { + __ AbsD(out, in); + } else { + DCHECK_EQ(type, DataType::Type::kFloat32); + __ AbsS(out, in); + } + } else { + if (type == DataType::Type::kFloat64) { + if (in != out) { + __ MovD(out, in); + } + __ MoveFromFpuHigh(TMP, in); + // ins instruction is not available for R1. + if (isR2OrNewer) { + __ Ins(TMP, ZERO, 31, 1); + } else { + __ Sll(TMP, TMP, 1); + __ Srl(TMP, TMP, 1); + } + __ MoveToFpuHigh(TMP, out); + } else { + DCHECK_EQ(type, DataType::Type::kFloat32); + __ Mfc1(TMP, in); + // ins instruction is not available for R1. + if (isR2OrNewer) { + __ Ins(TMP, ZERO, 31, 1); + } else { + __ Sll(TMP, TMP, 1); + __ Srl(TMP, TMP, 1); + } + __ Mtc1(TMP, out); + } + } +} + +void InstructionCodeGeneratorMIPS::VisitAbs(HAbs* abs) { + LocationSummary* locations = abs->GetLocations(); + bool isR2OrNewer = codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2(); + bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: { + Register in = locations->InAt(0).AsRegister(); + Register out = locations->Out().AsRegister(); + __ Sra(AT, in, 31); + __ Xor(out, in, AT); + __ Subu(out, out, AT); + break; + } + case DataType::Type::kInt64: { + Register in_lo = locations->InAt(0).AsRegisterPairLow(); + Register in_hi = locations->InAt(0).AsRegisterPairHigh(); + Register out_lo = locations->Out().AsRegisterPairLow(); + Register out_hi = locations->Out().AsRegisterPairHigh(); + // The comments in this section show the analogous operations which would + // be performed if we had 64-bit registers "in", and "out". + // __ Dsra32(AT, in, 31); + __ Sra(AT, in_hi, 31); + // __ Xor(out, in, AT); + __ Xor(TMP, in_lo, AT); + __ Xor(out_hi, in_hi, AT); + // __ Dsubu(out, out, AT); + __ Subu(out_lo, TMP, AT); + __ Sltu(TMP, out_lo, TMP); + __ Addu(out_hi, out_hi, TMP); + break; + } + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + GenerateAbsFP(locations, abs->GetResultType(), isR2OrNewer, isR6); + break; + default: + LOG(FATAL) << "Unexpected abs type " << abs->GetResultType(); + } +} + void LocationsBuilderMIPS::VisitConstructorFence(HConstructorFence* constructor_fence) { constructor_fence->SetLocations(nullptr); } diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index c91cb62eda5bbb73721e1dc7b890e7d86b6e5d66..4830ac9bc6470436f601825bb2fd5a1d794ac0ed 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -237,6 +237,7 @@ class InstructionCodeGeneratorMIPS : public InstructionCodeGenerator { private: void GenerateClassInitializationCheck(SlowPathCodeMIPS* slow_path, Register class_reg); void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor); + void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, Register temp); void HandleBinaryOp(HBinaryOperation* operation); void HandleCondition(HCondition* instruction); void HandleShift(HBinaryOperation* operation); @@ -246,6 +247,11 @@ class InstructionCodeGeneratorMIPS : public InstructionCodeGenerator { bool value_can_be_null); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info, uint32_t dex_pc); + void GenerateMinMaxInt(LocationSummary* locations, bool is_min, bool isR6, DataType::Type type); + void GenerateMinMaxFP(LocationSummary* locations, bool is_min, bool isR6, DataType::Type type); + void GenerateMinMax(HBinaryOperation*, bool is_min); + void GenerateAbsFP(LocationSummary* locations, DataType::Type type, bool isR2OrNewer, bool isR6); + // Generate a heap reference load using one register `out`: // // out <- *(out + offset) @@ -364,7 +370,6 @@ class InstructionCodeGeneratorMIPS : public InstructionCodeGenerator { class CodeGeneratorMIPS : public CodeGenerator { public: CodeGeneratorMIPS(HGraph* graph, - const MipsInstructionSetFeatures& isa_features, const CompilerOptions& compiler_options, OptimizingCompilerStats* stats = nullptr); virtual ~CodeGeneratorMIPS() {} @@ -503,9 +508,7 @@ class CodeGeneratorMIPS : public CodeGenerator { InstructionSet GetInstructionSet() const OVERRIDE { return InstructionSet::kMips; } - const MipsInstructionSetFeatures& GetInstructionSetFeatures() const { - return isa_features_; - } + const MipsInstructionSetFeatures& GetInstructionSetFeatures() const; MipsLabel* GetLabelOf(HBasicBlock* block) const { return CommonGetLabelOf(block_labels_, block); @@ -615,6 +618,10 @@ class CodeGeneratorMIPS : public CodeGenerator { DISALLOW_COPY_AND_ASSIGN(PcRelativePatchInfo); }; + PcRelativePatchInfo* NewBootImageIntrinsicPatch(uint32_t intrinsic_data, + const PcRelativePatchInfo* info_high = nullptr); + PcRelativePatchInfo* NewBootImageRelRoPatch(uint32_t boot_image_offset, + const PcRelativePatchInfo* info_high = nullptr); PcRelativePatchInfo* NewBootImageMethodPatch(MethodReference target_method, const PcRelativePatchInfo* info_high = nullptr); PcRelativePatchInfo* NewMethodBssEntryPatch(MethodReference target_method, @@ -637,6 +644,9 @@ class CodeGeneratorMIPS : public CodeGenerator { Register out, Register base); + void LoadBootImageAddress(Register reg, uint32_t boot_image_reference); + void AllocateInstanceForIntrinsic(HInvokeStaticOrDirect* invoke, uint32_t boot_image_offset); + // The JitPatchInfo is used for JIT string and class loads. struct JitPatchInfo { JitPatchInfo(const DexFile& dex_file, uint64_t idx) @@ -685,11 +695,11 @@ class CodeGeneratorMIPS : public CodeGenerator { InstructionCodeGeneratorMIPS instruction_visitor_; ParallelMoveResolverMIPS move_resolver_; MipsAssembler assembler_; - const MipsInstructionSetFeatures& isa_features_; // Deduplication map for 32-bit literals, used for non-patchable boot image addresses. Uint32ToLiteralMap uint32_literals_; - // PC-relative method patch info for kBootImageLinkTimePcRelative. + // PC-relative method patch info for kBootImageLinkTimePcRelative/kBootImageRelRo. + // Also used for type/string patches for kBootImageRelRo (same linker patch as for methods). ArenaDeque boot_image_method_patches_; // PC-relative method patch info for kBssEntry. ArenaDeque method_bss_entry_patches_; @@ -697,10 +707,12 @@ class CodeGeneratorMIPS : public CodeGenerator { ArenaDeque boot_image_type_patches_; // PC-relative type patch info for kBssEntry. ArenaDeque type_bss_entry_patches_; - // PC-relative String patch info; type depends on configuration (intern table or boot image PIC). + // PC-relative String patch info for kBootImageLinkTimePcRelative. ArenaDeque boot_image_string_patches_; // PC-relative String patch info for kBssEntry. ArenaDeque string_bss_entry_patches_; + // PC-relative patch info for IntrinsicObjects. + ArenaDeque boot_image_intrinsic_patches_; // Patches for string root accesses in JIT compiled code. ArenaDeque jit_string_patches_; diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 985ac2ca554fe6ada2e733c2bd34c5024aac29fd..ffde45e95e070403f6e2523827f6c517a207dd0a 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -24,6 +24,7 @@ #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "gc/accounting/card_table.h" +#include "gc/space/image_space.h" #include "heap_poisoning.h" #include "intrinsics.h" #include "intrinsics_mips64.h" @@ -939,7 +940,6 @@ class ReadBarrierForRootSlowPathMIPS64 : public SlowPathCodeMIPS64 { }; CodeGeneratorMIPS64::CodeGeneratorMIPS64(HGraph* graph, - const Mips64InstructionSetFeatures& isa_features, const CompilerOptions& compiler_options, OptimizingCompilerStats* stats) : CodeGenerator(graph, @@ -956,8 +956,8 @@ CodeGeneratorMIPS64::CodeGeneratorMIPS64(HGraph* graph, location_builder_(graph, this), instruction_visitor_(graph, this), move_resolver_(graph->GetAllocator(), this), - assembler_(graph->GetAllocator(), &isa_features), - isa_features_(isa_features), + assembler_(graph->GetAllocator(), + compiler_options.GetInstructionSetFeatures()->AsMips64InstructionSetFeatures()), uint32_literals_(std::less(), graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), uint64_literals_(std::less(), @@ -968,6 +968,7 @@ CodeGeneratorMIPS64::CodeGeneratorMIPS64(HGraph* graph, type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), + boot_image_intrinsic_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), jit_string_patches_(StringReferenceValueComparator(), graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), jit_class_patches_(TypeReferenceValueComparator(), @@ -988,8 +989,7 @@ void CodeGeneratorMIPS64::Finalize(CodeAllocator* allocator) { // Adjust native pc offsets in stack maps. StackMapStream* stack_map_stream = GetStackMapStream(); for (size_t i = 0, num = stack_map_stream->GetNumberOfStackMaps(); i != num; ++i) { - uint32_t old_position = - stack_map_stream->GetStackMap(i).native_pc_code_offset.Uint32Value(InstructionSet::kMips64); + uint32_t old_position = stack_map_stream->GetStackMapNativePcOffset(i); uint32_t new_position = __ GetAdjustedPosition(old_position); DCHECK_GE(new_position, old_position); stack_map_stream->SetStackMapNativePcOffset(i, new_position); @@ -1509,6 +1509,15 @@ inline void CodeGeneratorMIPS64::EmitPcRelativeLinkerPatches( } } +template +linker::LinkerPatch NoDexFileAdapter(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t boot_image_offset) { + DCHECK(target_dex_file == nullptr); // Unused for these patches, should be null. + return Factory(literal_offset, pc_insn_offset, boot_image_offset); +} + void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector* linker_patches) { DCHECK(linker_patches->empty()); size_t size = @@ -1517,7 +1526,8 @@ void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector* li boot_image_type_patches_.size() + type_bss_entry_patches_.size() + boot_image_string_patches_.size() + - string_bss_entry_patches_.size(); + string_bss_entry_patches_.size() + + boot_image_intrinsic_patches_.size(); linker_patches->reserve(size); if (GetCompilerOptions().IsBootImage()) { EmitPcRelativeLinkerPatches( @@ -1526,12 +1536,14 @@ void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector* li boot_image_type_patches_, linker_patches); EmitPcRelativeLinkerPatches( boot_image_string_patches_, linker_patches); + EmitPcRelativeLinkerPatches>( + boot_image_intrinsic_patches_, linker_patches); } else { - DCHECK(boot_image_method_patches_.empty()); - EmitPcRelativeLinkerPatches( - boot_image_type_patches_, linker_patches); - EmitPcRelativeLinkerPatches( - boot_image_string_patches_, linker_patches); + EmitPcRelativeLinkerPatches>( + boot_image_method_patches_, linker_patches); + DCHECK(boot_image_type_patches_.empty()); + DCHECK(boot_image_string_patches_.empty()); + DCHECK(boot_image_intrinsic_patches_.empty()); } EmitPcRelativeLinkerPatches( method_bss_entry_patches_, linker_patches); @@ -1542,6 +1554,20 @@ void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector* li DCHECK_EQ(size, linker_patches->size()); } +CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewBootImageIntrinsicPatch( + uint32_t intrinsic_data, + const PcRelativePatchInfo* info_high) { + return NewPcRelativePatch( + /* dex_file */ nullptr, intrinsic_data, info_high, &boot_image_intrinsic_patches_); +} + +CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewBootImageRelRoPatch( + uint32_t boot_image_offset, + const PcRelativePatchInfo* info_high) { + return NewPcRelativePatch( + /* dex_file */ nullptr, boot_image_offset, info_high, &boot_image_method_patches_); +} + CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewBootImageMethodPatch( MethodReference target_method, const PcRelativePatchInfo* info_high) { @@ -1625,6 +1651,50 @@ void CodeGeneratorMIPS64::EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchIn } } +void CodeGeneratorMIPS64::LoadBootImageAddress(GpuRegister reg, uint32_t boot_image_reference) { + if (GetCompilerOptions().IsBootImage()) { + PcRelativePatchInfo* info_high = NewBootImageIntrinsicPatch(boot_image_reference); + PcRelativePatchInfo* info_low = NewBootImageIntrinsicPatch(boot_image_reference, info_high); + EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low); + __ Daddiu(reg, AT, /* placeholder */ 0x5678); + } else if (GetCompilerOptions().GetCompilePic()) { + DCHECK(Runtime::Current()->IsAotCompiler()); + PcRelativePatchInfo* info_high = NewBootImageRelRoPatch(boot_image_reference); + PcRelativePatchInfo* info_low = NewBootImageRelRoPatch(boot_image_reference, info_high); + EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low); + // Note: Boot image is in the low 4GiB and the entry is 32-bit, so emit a 32-bit load. + __ Lwu(reg, AT, /* placeholder */ 0x5678); + } else { + gc::Heap* heap = Runtime::Current()->GetHeap(); + DCHECK(!heap->GetBootImageSpaces().empty()); + uintptr_t address = + reinterpret_cast(heap->GetBootImageSpaces()[0]->Begin() + boot_image_reference); + __ LoadLiteral(reg, kLoadDoubleword, DeduplicateBootImageAddressLiteral(address)); + } +} + +void CodeGeneratorMIPS64::AllocateInstanceForIntrinsic(HInvokeStaticOrDirect* invoke, + uint32_t boot_image_offset) { + DCHECK(invoke->IsStatic()); + InvokeRuntimeCallingConvention calling_convention; + GpuRegister argument = calling_convention.GetRegisterAt(0); + if (GetCompilerOptions().IsBootImage()) { + DCHECK_EQ(boot_image_offset, IntrinsicVisitor::IntegerValueOfInfo::kInvalidReference); + // Load the class the same way as for HLoadClass::LoadKind::kBootImageLinkTimePcRelative. + MethodReference target_method = invoke->GetTargetMethod(); + dex::TypeIndex type_idx = target_method.dex_file->GetMethodId(target_method.index).class_idx_; + PcRelativePatchInfo* info_high = NewBootImageTypePatch(*target_method.dex_file, type_idx); + PcRelativePatchInfo* info_low = + NewBootImageTypePatch(*target_method.dex_file, type_idx, info_high); + EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low); + __ Daddiu(argument, AT, /* placeholder */ 0x5678); + } else { + LoadBootImageAddress(argument, boot_image_offset); + } + InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc()); + CheckEntrypointTypes(); +} + Literal* CodeGeneratorMIPS64::DeduplicateJitStringLiteral(const DexFile& dex_file, dex::StringIndex string_index, Handle handle) { @@ -1740,6 +1810,10 @@ void CodeGeneratorMIPS64::DumpFloatingPointRegister(std::ostream& stream, int re stream << FpuRegister(reg); } +const Mips64InstructionSetFeatures& CodeGeneratorMIPS64::GetInstructionSetFeatures() const { + return *GetCompilerOptions().GetInstructionSetFeatures()->AsMips64InstructionSetFeatures(); +} + void CodeGeneratorMIPS64::InvokeRuntime(QuickEntrypointEnum entrypoint, HInstruction* instruction, uint32_t dex_pc, @@ -1780,6 +1854,34 @@ void InstructionCodeGeneratorMIPS64::GenerateClassInitializationCheck(SlowPathCo __ Bind(slow_path->GetExitLabel()); } +void InstructionCodeGeneratorMIPS64::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, + GpuRegister temp) { + uint32_t path_to_root = check->GetBitstringPathToRoot(); + uint32_t mask = check->GetBitstringMask(); + DCHECK(IsPowerOfTwo(mask + 1)); + size_t mask_bits = WhichPowerOf2(mask + 1); + + if (mask_bits == 16u) { + // Load only the bitstring part of the status word. + __ LoadFromOffset( + kLoadUnsignedHalfword, temp, temp, mirror::Class::StatusOffset().Int32Value()); + // Compare the bitstring bits using XOR. + __ Xori(temp, temp, dchecked_integral_cast(path_to_root)); + } else { + // /* uint32_t */ temp = temp->status_ + __ LoadFromOffset(kLoadWord, temp, temp, mirror::Class::StatusOffset().Int32Value()); + // Compare the bitstring bits using XOR. + if (IsUint<16>(path_to_root)) { + __ Xori(temp, temp, dchecked_integral_cast(path_to_root)); + } else { + __ LoadConst32(TMP, path_to_root); + __ Xor(temp, temp, TMP); + } + // Shift out bits that do not contribute to the comparison. + __ Sll(temp, temp, 32 - mask_bits); + } +} + void InstructionCodeGeneratorMIPS64::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) { __ Sync(0); // only stype 0 is supported } @@ -2840,7 +2942,13 @@ void LocationsBuilderMIPS64::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } @@ -2849,7 +2957,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); GpuRegister obj = obj_loc.AsRegister(); - GpuRegister cls = locations->InAt(1).AsRegister(); + Location cls = locations->InAt(1); Location temp_loc = locations->GetTemp(0); GpuRegister temp = temp_loc.AsRegister(); const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); @@ -2888,7 +2996,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { kWithoutReadBarrier); // Jump to slow path for throwing the exception or doing a // more involved array check. - __ Bnec(temp, cls, slow_path->GetEntryLabel()); + __ Bnec(temp, cls.AsRegister(), slow_path->GetEntryLabel()); break; } @@ -2914,7 +3022,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { // exception. __ Beqzc(temp, slow_path->GetEntryLabel()); // Otherwise, compare the classes. - __ Bnec(temp, cls, &loop); + __ Bnec(temp, cls.AsRegister(), &loop); break; } @@ -2929,7 +3037,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { // Walk over the class hierarchy to find a match. Mips64Label loop; __ Bind(&loop); - __ Beqc(temp, cls, &done); + __ Beqc(temp, cls.AsRegister(), &done); // /* HeapReference */ temp = temp->super_class_ GenerateReferenceLoadOneRegister(instruction, temp_loc, @@ -2952,7 +3060,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { maybe_temp2_loc, kWithoutReadBarrier); // Do an exact check. - __ Beqc(temp, cls, &done); + __ Beqc(temp, cls.AsRegister(), &done); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference */ temp = temp->component_type_ GenerateReferenceLoadOneRegister(instruction, @@ -3011,7 +3119,21 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { __ Daddiu(temp, temp, 2 * kHeapReferenceSize); __ Addiu(TMP, TMP, -2); // Compare the classes and continue the loop if they do not match. - __ Bnec(AT, cls, &loop); + __ Bnec(AT, cls.AsRegister(), &loop); + break; + } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + maybe_temp2_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, temp); + __ Bnezc(temp, slow_path->GetEntryLabel()); break; } } @@ -5515,6 +5637,8 @@ void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; + case TypeCheckKind::kBitstringCheck: + break; } LocationSummary* locations = @@ -5523,7 +5647,13 @@ void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } // The output does overlap inputs. // Note that TypeCheckSlowPathMIPS64 uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -5535,7 +5665,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); GpuRegister obj = obj_loc.AsRegister(); - GpuRegister cls = locations->InAt(1).AsRegister(); + Location cls = locations->InAt(1); Location out_loc = locations->Out(); GpuRegister out = out_loc.AsRegister(); const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -5567,7 +5697,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { maybe_temp_loc, read_barrier_option); // Classes must be equal for the instanceof to succeed. - __ Xor(out, out, cls); + __ Xor(out, out, cls.AsRegister()); __ Sltiu(out, out, 1); break; } @@ -5594,7 +5724,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { read_barrier_option); // If `out` is null, we use it for the result, and jump to `done`. __ Beqzc(out, &done); - __ Bnec(out, cls, &loop); + __ Bnec(out, cls.AsRegister(), &loop); __ LoadConst32(out, 1); break; } @@ -5612,7 +5742,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { // Walk over the class hierarchy to find a match. Mips64Label loop, success; __ Bind(&loop); - __ Beqc(out, cls, &success); + __ Beqc(out, cls.AsRegister(), &success); // /* HeapReference */ out = out->super_class_ GenerateReferenceLoadOneRegister(instruction, out_loc, @@ -5639,7 +5769,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { read_barrier_option); // Do an exact check. Mips64Label success; - __ Beqc(out, cls, &success); + __ Beqc(out, cls.AsRegister(), &success); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference */ out = out->component_type_ GenerateReferenceLoadOneRegister(instruction, @@ -5671,7 +5801,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS64( instruction, /* is_fatal */ false); codegen_->AddSlowPath(slow_path); - __ Bnec(out, cls, slow_path->GetEntryLabel()); + __ Bnec(out, cls.AsRegister(), slow_path->GetEntryLabel()); __ LoadConst32(out, 1); break; } @@ -5703,6 +5833,20 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { __ Bc(slow_path->GetEntryLabel()); break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, out); + __ Sltiu(out, out, 1); + break; + } } __ Bind(&done); @@ -5825,6 +5969,14 @@ void InstructionCodeGeneratorMIPS64::VisitInvokePolymorphic(HInvokePolymorphic* 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); @@ -5839,7 +5991,7 @@ HLoadString::LoadKind CodeGeneratorMIPS64::GetSupportedLoadStringKind( bool fallback_load = false; switch (desired_string_load_kind) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: - case HLoadString::LoadKind::kBootImageInternTable: + case HLoadString::LoadKind::kBootImageRelRo: case HLoadString::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -5866,7 +6018,7 @@ HLoadClass::LoadKind CodeGeneratorMIPS64::GetSupportedLoadClassKind( case HLoadClass::LoadKind::kReferrersClass: break; case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: - case HLoadClass::LoadKind::kBootImageClassTable: + case HLoadClass::LoadKind::kBootImageRelRo: case HLoadClass::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -5926,6 +6078,15 @@ void CodeGeneratorMIPS64::GenerateStaticOrDirectCall( kLoadDoubleword, DeduplicateUint64Literal(invoke->GetMethodAddress())); break; + case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: { + uint32_t boot_image_offset = GetBootImageOffset(invoke); + PcRelativePatchInfo* info_high = NewBootImageRelRoPatch(boot_image_offset); + PcRelativePatchInfo* info_low = NewBootImageRelRoPatch(boot_image_offset, info_high); + EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low); + // Note: Boot image is in the low 4GiB and the entry is 32-bit, so emit a 32-bit load. + __ Lwu(temp.AsRegister(), AT, /* placeholder */ 0x5678); + break; + } case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: { PcRelativePatchInfo* info_high = NewMethodBssEntryPatch( MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex())); @@ -6113,20 +6274,15 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S codegen_->DeduplicateBootImageAddressLiteral(address)); break; } - case HLoadClass::LoadKind::kBootImageClassTable: { + case HLoadClass::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); + uint32_t boot_image_offset = codegen_->GetBootImageOffset(cls); CodeGeneratorMIPS64::PcRelativePatchInfo* info_high = - codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex()); + codegen_->NewBootImageRelRoPatch(boot_image_offset); CodeGeneratorMIPS64::PcRelativePatchInfo* info_low = - codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex(), info_high); + codegen_->NewBootImageRelRoPatch(boot_image_offset, info_high); codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low); __ Lwu(out, AT, /* placeholder */ 0x5678); - // Extract the reference from the slot data, i.e. clear the hash bits. - int32_t masked_hash = ClassTable::TableSlot::MaskHash( - ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex()))); - if (masked_hash != 0) { - __ Daddiu(out, out, -masked_hash); - } break; } case HLoadClass::LoadKind::kBssEntry: { @@ -6174,6 +6330,26 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S } } +void LocationsBuilderMIPS64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + InvokeRuntimeCallingConvention calling_convention; + Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, loc, loc); +} + +void InstructionCodeGeneratorMIPS64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + +void LocationsBuilderMIPS64::VisitLoadMethodType(HLoadMethodType* load) { + InvokeRuntimeCallingConvention calling_convention; + Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, loc, loc); +} + +void InstructionCodeGeneratorMIPS64::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + static int32_t GetExceptionTlsOffset() { return Thread::ExceptionOffset().Int32Value(); } @@ -6248,12 +6424,13 @@ void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) NO_THREA codegen_->DeduplicateBootImageAddressLiteral(address)); return; } - case HLoadString::LoadKind::kBootImageInternTable: { + case HLoadString::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); + uint32_t boot_image_offset = codegen_->GetBootImageOffset(load); CodeGeneratorMIPS64::PcRelativePatchInfo* info_high = - codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex()); + codegen_->NewBootImageRelRoPatch(boot_image_offset); CodeGeneratorMIPS64::PcRelativePatchInfo* info_low = - codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex(), info_high); + codegen_->NewBootImageRelRoPatch(boot_image_offset, info_high); codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low); __ Lwu(out, AT, /* placeholder */ 0x5678); return; @@ -6455,31 +6632,13 @@ void LocationsBuilderMIPS64::VisitNewInstance(HNewInstance* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary( instruction, LocationSummary::kCallOnMainOnly); InvokeRuntimeCallingConvention calling_convention; - if (instruction->IsStringAlloc()) { - locations->AddTemp(Location::RegisterLocation(kMethodRegisterArgument)); - } else { - locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); - } + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kReference)); } void InstructionCodeGeneratorMIPS64::VisitNewInstance(HNewInstance* instruction) { - // Note: if heap poisoning is enabled, the entry point takes care - // of poisoning the reference. - if (instruction->IsStringAlloc()) { - // String is allocated through StringFactory. Call NewEmptyString entry point. - GpuRegister temp = instruction->GetLocations()->GetTemp(0).AsRegister(); - MemberOffset code_offset = - ArtMethod::EntryPointFromQuickCompiledCodeOffset(kMips64PointerSize); - __ LoadFromOffset(kLoadDoubleword, temp, TR, QUICK_ENTRY_POINT(pNewEmptyString)); - __ LoadFromOffset(kLoadDoubleword, T9, temp, code_offset.Int32Value()); - __ Jalr(T9); - __ Nop(); - codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); - } else { - codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); - CheckEntrypointTypes(); - } + codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); + CheckEntrypointTypes(); } void LocationsBuilderMIPS64::VisitNot(HNot* instruction) { @@ -6665,6 +6824,236 @@ void InstructionCodeGeneratorMIPS64::VisitRem(HRem* instruction) { } } +static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) { + LocationSummary* locations = new (allocator) LocationSummary(minmax); + switch (minmax->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); + break; + default: + LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType(); + } +} + +void InstructionCodeGeneratorMIPS64::GenerateMinMaxInt(LocationSummary* locations, bool is_min) { + GpuRegister lhs = locations->InAt(0).AsRegister(); + GpuRegister rhs = locations->InAt(1).AsRegister(); + GpuRegister out = locations->Out().AsRegister(); + + if (lhs == rhs) { + if (out != lhs) { + __ Move(out, lhs); + } + } else { + // Some architectures, such as ARM and MIPS (prior to r6), have a + // conditional move instruction which only changes the target + // (output) register if the condition is true (MIPS prior to r6 had + // MOVF, MOVT, and MOVZ). The SELEQZ and SELNEZ instructions always + // change the target (output) register. If the condition is true the + // output register gets the contents of the "rs" register; otherwise, + // the output register is set to zero. One consequence of this is + // that to implement something like "rd = c==0 ? rs : rt" MIPS64r6 + // needs to use a pair of SELEQZ/SELNEZ instructions. After + // executing this pair of instructions one of the output registers + // from the pair will necessarily contain zero. Then the code ORs the + // output registers from the SELEQZ/SELNEZ instructions to get the + // final result. + // + // The initial test to see if the output register is same as the + // first input register is needed to make sure that value in the + // first input register isn't clobbered before we've finished + // computing the output value. The logic in the corresponding else + // clause performs the same task but makes sure the second input + // register isn't clobbered in the event that it's the same register + // as the output register; the else clause also handles the case + // where the output register is distinct from both the first, and the + // second input registers. + if (out == lhs) { + __ Slt(AT, rhs, lhs); + if (is_min) { + __ Seleqz(out, lhs, AT); + __ Selnez(AT, rhs, AT); + } else { + __ Selnez(out, lhs, AT); + __ Seleqz(AT, rhs, AT); + } + } else { + __ Slt(AT, lhs, rhs); + if (is_min) { + __ Seleqz(out, rhs, AT); + __ Selnez(AT, lhs, AT); + } else { + __ Selnez(out, rhs, AT); + __ Seleqz(AT, lhs, AT); + } + } + __ Or(out, out, AT); + } +} + +void InstructionCodeGeneratorMIPS64::GenerateMinMaxFP(LocationSummary* locations, + bool is_min, + DataType::Type type) { + FpuRegister a = locations->InAt(0).AsFpuRegister(); + FpuRegister b = locations->InAt(1).AsFpuRegister(); + FpuRegister out = locations->Out().AsFpuRegister(); + + Mips64Label noNaNs; + Mips64Label done; + FpuRegister ftmp = ((out != a) && (out != b)) ? out : FTMP; + + // When Java computes min/max it prefers a NaN to a number; the + // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of + // the inputs is a NaN and the other is a valid number, the MIPS + // instruction will return the number; Java wants the NaN value + // returned. This is why there is extra logic preceding the use of + // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a + // NaN, return the NaN, otherwise return the min/max. + if (type == DataType::Type::kFloat64) { + __ CmpUnD(FTMP, a, b); + __ Bc1eqz(FTMP, &noNaNs); + + // One of the inputs is a NaN + __ CmpEqD(ftmp, a, a); + // If a == a then b is the NaN, otherwise a is the NaN. + __ SelD(ftmp, a, b); + + if (ftmp != out) { + __ MovD(out, ftmp); + } + + __ Bc(&done); + + __ Bind(&noNaNs); + + if (is_min) { + __ MinD(out, a, b); + } else { + __ MaxD(out, a, b); + } + } else { + DCHECK_EQ(type, DataType::Type::kFloat32); + __ CmpUnS(FTMP, a, b); + __ Bc1eqz(FTMP, &noNaNs); + + // One of the inputs is a NaN + __ CmpEqS(ftmp, a, a); + // If a == a then b is the NaN, otherwise a is the NaN. + __ SelS(ftmp, a, b); + + if (ftmp != out) { + __ MovS(out, ftmp); + } + + __ Bc(&done); + + __ Bind(&noNaNs); + + if (is_min) { + __ MinS(out, a, b); + } else { + __ MaxS(out, a, b); + } + } + + __ Bind(&done); +} + +void InstructionCodeGeneratorMIPS64::GenerateMinMax(HBinaryOperation* minmax, bool is_min) { + DataType::Type type = minmax->GetResultType(); + switch (type) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + GenerateMinMaxInt(minmax->GetLocations(), is_min); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + GenerateMinMaxFP(minmax->GetLocations(), is_min, type); + break; + default: + LOG(FATAL) << "Unexpected type for HMinMax " << type; + } +} + +void LocationsBuilderMIPS64::VisitMin(HMin* min) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), min); +} + +void InstructionCodeGeneratorMIPS64::VisitMin(HMin* min) { + GenerateMinMax(min, /*is_min*/ true); +} + +void LocationsBuilderMIPS64::VisitMax(HMax* max) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), max); +} + +void InstructionCodeGeneratorMIPS64::VisitMax(HMax* max) { + GenerateMinMax(max, /*is_min*/ false); +} + +void LocationsBuilderMIPS64::VisitAbs(HAbs* abs) { + LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); + break; + default: + LOG(FATAL) << "Unexpected abs type " << abs->GetResultType(); + } +} + +void InstructionCodeGeneratorMIPS64::VisitAbs(HAbs* abs) { + LocationSummary* locations = abs->GetLocations(); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: { + GpuRegister in = locations->InAt(0).AsRegister(); + GpuRegister out = locations->Out().AsRegister(); + __ Sra(AT, in, 31); + __ Xor(out, in, AT); + __ Subu(out, out, AT); + break; + } + case DataType::Type::kInt64: { + GpuRegister in = locations->InAt(0).AsRegister(); + GpuRegister out = locations->Out().AsRegister(); + __ Dsra32(AT, in, 31); + __ Xor(out, in, AT); + __ Dsubu(out, out, AT); + break; + } + case DataType::Type::kFloat32: { + FpuRegister in = locations->InAt(0).AsFpuRegister(); + FpuRegister out = locations->Out().AsFpuRegister(); + __ AbsS(out, in); + break; + } + case DataType::Type::kFloat64: { + FpuRegister in = locations->InAt(0).AsFpuRegister(); + FpuRegister out = locations->Out().AsFpuRegister(); + __ AbsD(out, in); + break; + } + default: + LOG(FATAL) << "Unexpected abs type " << abs->GetResultType(); + } +} + void LocationsBuilderMIPS64::VisitConstructorFence(HConstructorFence* constructor_fence) { constructor_fence->SetLocations(nullptr); } diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index e6b69c469fd08d2895034216ef406b380cea4ff0..fc0908b2cb92344b7e4f68f2bd141dbbbe4b4a45 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -233,6 +233,7 @@ class InstructionCodeGeneratorMIPS64 : public InstructionCodeGenerator { private: void GenerateClassInitializationCheck(SlowPathCodeMIPS64* slow_path, GpuRegister class_reg); + void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, GpuRegister temp); void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor); void HandleBinaryOp(HBinaryOperation* operation); void HandleCondition(HCondition* instruction); @@ -242,6 +243,10 @@ class InstructionCodeGeneratorMIPS64 : public InstructionCodeGenerator { bool value_can_be_null); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); + void GenerateMinMaxInt(LocationSummary* locations, bool is_min); + void GenerateMinMaxFP(LocationSummary* locations, bool is_min, DataType::Type type); + void GenerateMinMax(HBinaryOperation* minmax, bool is_min); + // Generate a heap reference load using one register `out`: // // out <- *(out + offset) @@ -347,7 +352,6 @@ class InstructionCodeGeneratorMIPS64 : public InstructionCodeGenerator { class CodeGeneratorMIPS64 : public CodeGenerator { public: CodeGeneratorMIPS64(HGraph* graph, - const Mips64InstructionSetFeatures& isa_features, const CompilerOptions& compiler_options, OptimizingCompilerStats* stats = nullptr); virtual ~CodeGeneratorMIPS64() {} @@ -479,9 +483,7 @@ class CodeGeneratorMIPS64 : public CodeGenerator { InstructionSet GetInstructionSet() const OVERRIDE { return InstructionSet::kMips64; } - const Mips64InstructionSetFeatures& GetInstructionSetFeatures() const { - return isa_features_; - } + const Mips64InstructionSetFeatures& GetInstructionSetFeatures() const; Mips64Label* GetLabelOf(HBasicBlock* block) const { return CommonGetLabelOf(block_labels_, block); @@ -586,6 +588,10 @@ class CodeGeneratorMIPS64 : public CodeGenerator { DISALLOW_COPY_AND_ASSIGN(PcRelativePatchInfo); }; + PcRelativePatchInfo* NewBootImageIntrinsicPatch(uint32_t intrinsic_data, + const PcRelativePatchInfo* info_high = nullptr); + PcRelativePatchInfo* NewBootImageRelRoPatch(uint32_t boot_image_offset, + const PcRelativePatchInfo* info_high = nullptr); PcRelativePatchInfo* NewBootImageMethodPatch(MethodReference target_method, const PcRelativePatchInfo* info_high = nullptr); PcRelativePatchInfo* NewMethodBssEntryPatch(MethodReference target_method, @@ -608,6 +614,9 @@ class CodeGeneratorMIPS64 : public CodeGenerator { GpuRegister out, PcRelativePatchInfo* info_low = nullptr); + void LoadBootImageAddress(GpuRegister reg, uint32_t boot_image_reference); + void AllocateInstanceForIntrinsic(HInvokeStaticOrDirect* invoke, uint32_t boot_image_offset); + void PatchJitRootUse(uint8_t* code, const uint8_t* roots_data, const Literal* literal, @@ -648,14 +657,14 @@ class CodeGeneratorMIPS64 : public CodeGenerator { InstructionCodeGeneratorMIPS64 instruction_visitor_; ParallelMoveResolverMIPS64 move_resolver_; Mips64Assembler assembler_; - const Mips64InstructionSetFeatures& isa_features_; // Deduplication map for 32-bit literals, used for non-patchable boot image addresses. Uint32ToLiteralMap uint32_literals_; // Deduplication map for 64-bit literals, used for non-patchable method address or method code // address. Uint64ToLiteralMap uint64_literals_; - // PC-relative method patch info for kBootImageLinkTimePcRelative. + // PC-relative method patch info for kBootImageLinkTimePcRelative/kBootImageRelRo. + // Also used for type/string patches for kBootImageRelRo (same linker patch as for methods). ArenaDeque boot_image_method_patches_; // PC-relative method patch info for kBssEntry. ArenaDeque method_bss_entry_patches_; @@ -663,10 +672,12 @@ class CodeGeneratorMIPS64 : public CodeGenerator { ArenaDeque boot_image_type_patches_; // PC-relative type patch info for kBssEntry. ArenaDeque type_bss_entry_patches_; - // PC-relative String patch info; type depends on configuration (intern table or boot image PIC). + // PC-relative String patch info for kBootImageLinkTimePcRelative. ArenaDeque boot_image_string_patches_; // PC-relative type patch info for kBssEntry. ArenaDeque string_bss_entry_patches_; + // PC-relative patch info for IntrinsicObjects. + ArenaDeque boot_image_intrinsic_patches_; // Patches for string root accesses in JIT compiled code. StringToLiteralMap jit_string_patches_; diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64.cc index 174efdf115536faece4b494ca141334f38803cda..6d135a9bfb7248dd81451863abb3d20f0570b6e7 100644 --- a/compiler/optimizing/code_generator_vector_arm64.cc +++ b/compiler/optimizing/code_generator_vector_arm64.cc @@ -29,7 +29,7 @@ using helpers::Arm64CanEncodeConstantAsImmediate; using helpers::DRegisterFrom; using helpers::HeapOperand; using helpers::InputRegisterAt; -using helpers::Int64ConstantFrom; +using helpers::Int64FromLocation; using helpers::OutputRegister; using helpers::VRegisterFrom; using helpers::WRegisterFrom; @@ -63,7 +63,7 @@ void LocationsBuilderARM64::VisitVecReplicateScalar(HVecReplicateScalar* instruc } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -78,7 +78,7 @@ void InstructionCodeGeneratorARM64::VisitVecReplicateScalar(HVecReplicateScalar* case DataType::Type::kInt8: DCHECK_EQ(16u, instruction->GetVectorLength()); if (src_loc.IsConstant()) { - __ Movi(dst.V16B(), Int64ConstantFrom(src_loc)); + __ Movi(dst.V16B(), Int64FromLocation(src_loc)); } else { __ Dup(dst.V16B(), InputRegisterAt(instruction, 0)); } @@ -87,7 +87,7 @@ void InstructionCodeGeneratorARM64::VisitVecReplicateScalar(HVecReplicateScalar* case DataType::Type::kInt16: DCHECK_EQ(8u, instruction->GetVectorLength()); if (src_loc.IsConstant()) { - __ Movi(dst.V8H(), Int64ConstantFrom(src_loc)); + __ Movi(dst.V8H(), Int64FromLocation(src_loc)); } else { __ Dup(dst.V8H(), InputRegisterAt(instruction, 0)); } @@ -95,7 +95,7 @@ void InstructionCodeGeneratorARM64::VisitVecReplicateScalar(HVecReplicateScalar* case DataType::Type::kInt32: DCHECK_EQ(4u, instruction->GetVectorLength()); if (src_loc.IsConstant()) { - __ Movi(dst.V4S(), Int64ConstantFrom(src_loc)); + __ Movi(dst.V4S(), Int64FromLocation(src_loc)); } else { __ Dup(dst.V4S(), InputRegisterAt(instruction, 0)); } @@ -103,7 +103,7 @@ void InstructionCodeGeneratorARM64::VisitVecReplicateScalar(HVecReplicateScalar* case DataType::Type::kInt64: DCHECK_EQ(2u, instruction->GetVectorLength()); if (src_loc.IsConstant()) { - __ Movi(dst.V2D(), Int64ConstantFrom(src_loc)); + __ Movi(dst.V2D(), Int64FromLocation(src_loc)); } else { __ Dup(dst.V2D(), XRegisterFrom(src_loc)); } @@ -125,7 +125,7 @@ void InstructionCodeGeneratorARM64::VisitVecReplicateScalar(HVecReplicateScalar* } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -149,7 +149,7 @@ void LocationsBuilderARM64::VisitVecExtractScalar(HVecExtractScalar* instruction locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -173,7 +173,7 @@ void InstructionCodeGeneratorARM64::VisitVecExtractScalar(HVecExtractScalar* ins DCHECK(locations->InAt(0).Equals(locations->Out())); // no code required break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -200,7 +200,7 @@ static void CreateVecUnOpLocations(ArenaAllocator* allocator, HVecUnaryOperation locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -240,7 +240,7 @@ void InstructionCodeGeneratorARM64::VisitVecReduce(HVecReduce* instruction) { } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -259,7 +259,7 @@ void InstructionCodeGeneratorARM64::VisitVecCnv(HVecCnv* instruction) { DCHECK_EQ(4u, instruction->GetVectorLength()); __ Scvtf(dst.V4S(), src.V4S()); } else { - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); } } @@ -299,7 +299,7 @@ void InstructionCodeGeneratorARM64::VisitVecNeg(HVecNeg* instruction) { __ Fneg(dst.V2D(), src.V2D()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -338,7 +338,7 @@ void InstructionCodeGeneratorARM64::VisitVecAbs(HVecAbs* instruction) { __ Fabs(dst.V2D(), src.V2D()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -366,7 +366,7 @@ void InstructionCodeGeneratorARM64::VisitVecNot(HVecNot* instruction) { __ Not(dst.V16B(), src.V16B()); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -389,7 +389,7 @@ static void CreateVecBinOpLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -431,7 +431,39 @@ void InstructionCodeGeneratorARM64::VisitVecAdd(HVecAdd* instruction) { __ Fadd(dst.V2D(), lhs.V2D(), rhs.V2D()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); + UNREACHABLE(); + } +} + +void LocationsBuilderARM64::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorARM64::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + LocationSummary* locations = instruction->GetLocations(); + VRegister lhs = VRegisterFrom(locations->InAt(0)); + VRegister rhs = VRegisterFrom(locations->InAt(1)); + VRegister dst = VRegisterFrom(locations->Out()); + switch (instruction->GetPackedType()) { + case DataType::Type::kUint8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ Uqadd(dst.V16B(), lhs.V16B(), rhs.V16B()); + break; + case DataType::Type::kInt8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ Sqadd(dst.V16B(), lhs.V16B(), rhs.V16B()); + break; + case DataType::Type::kUint16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ Uqadd(dst.V8H(), lhs.V8H(), rhs.V8H()); + break; + case DataType::Type::kInt16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ Sqadd(dst.V8H(), lhs.V8H(), rhs.V8H()); + break; + default: + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -471,7 +503,7 @@ void InstructionCodeGeneratorARM64::VisitVecHalvingAdd(HVecHalvingAdd* instructi : __ Shadd(dst.V8H(), lhs.V8H(), rhs.V8H()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -513,7 +545,39 @@ void InstructionCodeGeneratorARM64::VisitVecSub(HVecSub* instruction) { __ Fsub(dst.V2D(), lhs.V2D(), rhs.V2D()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); + UNREACHABLE(); + } +} + +void LocationsBuilderARM64::VisitVecSaturationSub(HVecSaturationSub* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorARM64::VisitVecSaturationSub(HVecSaturationSub* instruction) { + LocationSummary* locations = instruction->GetLocations(); + VRegister lhs = VRegisterFrom(locations->InAt(0)); + VRegister rhs = VRegisterFrom(locations->InAt(1)); + VRegister dst = VRegisterFrom(locations->Out()); + switch (instruction->GetPackedType()) { + case DataType::Type::kUint8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ Uqsub(dst.V16B(), lhs.V16B(), rhs.V16B()); + break; + case DataType::Type::kInt8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ Sqsub(dst.V16B(), lhs.V16B(), rhs.V16B()); + break; + case DataType::Type::kUint16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ Uqsub(dst.V8H(), lhs.V8H(), rhs.V8H()); + break; + case DataType::Type::kInt16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ Sqsub(dst.V8H(), lhs.V8H(), rhs.V8H()); + break; + default: + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -551,7 +615,7 @@ void InstructionCodeGeneratorARM64::VisitVecMul(HVecMul* instruction) { __ Fmul(dst.V2D(), lhs.V2D(), rhs.V2D()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -575,7 +639,7 @@ void InstructionCodeGeneratorARM64::VisitVecDiv(HVecDiv* instruction) { __ Fdiv(dst.V2D(), lhs.V2D(), rhs.V2D()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -623,7 +687,7 @@ void InstructionCodeGeneratorARM64::VisitVecMin(HVecMin* instruction) { __ Fmin(dst.V2D(), lhs.V2D(), rhs.V2D()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -671,7 +735,7 @@ void InstructionCodeGeneratorARM64::VisitVecMax(HVecMax* instruction) { __ Fmax(dst.V2D(), lhs.V2D(), rhs.V2D()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -699,7 +763,7 @@ void InstructionCodeGeneratorARM64::VisitVecAnd(HVecAnd* instruction) { __ And(dst.V16B(), lhs.V16B(), rhs.V16B()); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -735,7 +799,7 @@ void InstructionCodeGeneratorARM64::VisitVecOr(HVecOr* instruction) { __ Orr(dst.V16B(), lhs.V16B(), rhs.V16B()); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -762,7 +826,7 @@ void InstructionCodeGeneratorARM64::VisitVecXor(HVecXor* instruction) { __ Eor(dst.V16B(), lhs.V16B(), rhs.V16B()); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -782,7 +846,7 @@ static void CreateVecShiftLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -816,7 +880,7 @@ void InstructionCodeGeneratorARM64::VisitVecShl(HVecShl* instruction) { __ Shl(dst.V2D(), lhs.V2D(), value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -850,7 +914,7 @@ void InstructionCodeGeneratorARM64::VisitVecShr(HVecShr* instruction) { __ Sshr(dst.V2D(), lhs.V2D(), value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -884,7 +948,7 @@ void InstructionCodeGeneratorARM64::VisitVecUShr(HVecUShr* instruction) { __ Ushr(dst.V2D(), lhs.V2D(), value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -916,7 +980,7 @@ void LocationsBuilderARM64::VisitVecSetScalars(HVecSetScalars* instruction) { locations->SetOut(Location::RequiresFpuRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -957,7 +1021,7 @@ void InstructionCodeGeneratorARM64::VisitVecSetScalars(HVecSetScalars* instructi __ Mov(dst.V2D(), 0, InputRegisterAt(instruction, 0)); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -978,7 +1042,7 @@ static void CreateVecAccumLocations(ArenaAllocator* allocator, HVecOperation* in locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1026,7 +1090,7 @@ void InstructionCodeGeneratorARM64::VisitVecMultiplyAccumulate(HVecMultiplyAccum } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1139,7 +1203,7 @@ void InstructionCodeGeneratorARM64::VisitVecSADAccumulate(HVecSADAccumulate* ins break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; @@ -1167,7 +1231,7 @@ void InstructionCodeGeneratorARM64::VisitVecSADAccumulate(HVecSADAccumulate* ins break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; @@ -1188,7 +1252,7 @@ void InstructionCodeGeneratorARM64::VisitVecSADAccumulate(HVecSADAccumulate* ins __ Sabal2(acc.V2D(), left.V4S(), right.V4S()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; @@ -1204,12 +1268,12 @@ void InstructionCodeGeneratorARM64::VisitVecSADAccumulate(HVecSADAccumulate* ins break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); } } @@ -1237,7 +1301,7 @@ static void CreateVecMemLocations(ArenaAllocator* allocator, } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1269,7 +1333,7 @@ MemOperand InstructionCodeGeneratorARM64::VecAddress( DCHECK(!instruction->InputAt(0)->IsIntermediateAddress()); if (index.IsConstant()) { - offset += Int64ConstantFrom(index) << shift; + offset += Int64FromLocation(index) << shift; return HeapOperand(base, offset); } else { *scratch = temps_scope->AcquireSameSizeAs(base); @@ -1331,7 +1395,7 @@ void InstructionCodeGeneratorARM64::VisitVecLoad(HVecLoad* instruction) { __ Ldr(reg, VecAddress(instruction, &temps, size, instruction->IsStringCharAt(), &scratch)); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1362,7 +1426,7 @@ void InstructionCodeGeneratorARM64::VisitVecStore(HVecStore* instruction) { __ Str(reg, VecAddress(instruction, &temps, size, /*is_string_char_at*/ false, &scratch)); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } diff --git a/compiler/optimizing/code_generator_vector_arm_vixl.cc b/compiler/optimizing/code_generator_vector_arm_vixl.cc index 7c3155ab73ba1e2480eb1b26db3051f93007b52a..7b66b1798397cd5d54158b738fa5a5f5eba498ee 100644 --- a/compiler/optimizing/code_generator_vector_arm_vixl.cc +++ b/compiler/optimizing/code_generator_vector_arm_vixl.cc @@ -46,7 +46,7 @@ void LocationsBuilderARMVIXL::VisitVecReplicateScalar(HVecReplicateScalar* instr locations->SetOut(Location::RequiresFpuRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -71,7 +71,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecReplicateScalar(HVecReplicateScala __ Vdup(Untyped32, dst, InputRegisterAt(instruction, 0)); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -84,7 +84,7 @@ void LocationsBuilderARMVIXL::VisitVecExtractScalar(HVecExtractScalar* instructi locations->SetOut(Location::RequiresRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -98,7 +98,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecExtractScalar(HVecExtractScalar* i __ Vmov(OutputRegister(instruction), DRegisterLane(src, 0)); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -122,7 +122,7 @@ static void CreateVecUnOpLocations(ArenaAllocator* allocator, HVecUnaryOperation locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -151,7 +151,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecReduce(HVecReduce* instruction) { } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -188,7 +188,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecNeg(HVecNeg* instruction) { __ Vneg(DataTypeValue::S32, dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -215,7 +215,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecAbs(HVecAbs* instruction) { __ Vabs(DataTypeValue::S32, dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -242,7 +242,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecNot(HVecNot* instruction) { __ Vmvn(I8, dst, src); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -262,7 +262,7 @@ static void CreateVecBinOpLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -292,7 +292,39 @@ void InstructionCodeGeneratorARMVIXL::VisitVecAdd(HVecAdd* instruction) { __ Vadd(I32, dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); + UNREACHABLE(); + } +} + +void LocationsBuilderARMVIXL::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorARMVIXL::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + LocationSummary* locations = instruction->GetLocations(); + vixl32::DRegister lhs = DRegisterFrom(locations->InAt(0)); + vixl32::DRegister rhs = DRegisterFrom(locations->InAt(1)); + vixl32::DRegister dst = DRegisterFrom(locations->Out()); + switch (instruction->GetPackedType()) { + case DataType::Type::kUint8: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ Vqadd(DataTypeValue::U8, dst, lhs, rhs); + break; + case DataType::Type::kInt8: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ Vqadd(DataTypeValue::S8, dst, lhs, rhs); + break; + case DataType::Type::kUint16: + DCHECK_EQ(4u, instruction->GetVectorLength()); + __ Vqadd(DataTypeValue::U16, dst, lhs, rhs); + break; + case DataType::Type::kInt16: + DCHECK_EQ(4u, instruction->GetVectorLength()); + __ Vqadd(DataTypeValue::S16, dst, lhs, rhs); + break; + default: + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -332,7 +364,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecHalvingAdd(HVecHalvingAdd* instruc : __ Vhadd(DataTypeValue::S16, dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -362,7 +394,39 @@ void InstructionCodeGeneratorARMVIXL::VisitVecSub(HVecSub* instruction) { __ Vsub(I32, dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); + UNREACHABLE(); + } +} + +void LocationsBuilderARMVIXL::VisitVecSaturationSub(HVecSaturationSub* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorARMVIXL::VisitVecSaturationSub(HVecSaturationSub* instruction) { + LocationSummary* locations = instruction->GetLocations(); + vixl32::DRegister lhs = DRegisterFrom(locations->InAt(0)); + vixl32::DRegister rhs = DRegisterFrom(locations->InAt(1)); + vixl32::DRegister dst = DRegisterFrom(locations->Out()); + switch (instruction->GetPackedType()) { + case DataType::Type::kUint8: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ Vqsub(DataTypeValue::U8, dst, lhs, rhs); + break; + case DataType::Type::kInt8: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ Vqsub(DataTypeValue::S8, dst, lhs, rhs); + break; + case DataType::Type::kUint16: + DCHECK_EQ(4u, instruction->GetVectorLength()); + __ Vqsub(DataTypeValue::U16, dst, lhs, rhs); + break; + case DataType::Type::kInt16: + DCHECK_EQ(4u, instruction->GetVectorLength()); + __ Vqsub(DataTypeValue::S16, dst, lhs, rhs); + break; + default: + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -392,7 +456,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecMul(HVecMul* instruction) { __ Vmul(I32, dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -440,7 +504,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecMin(HVecMin* instruction) { __ Vmin(DataTypeValue::S32, dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -480,7 +544,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecMax(HVecMax* instruction) { __ Vmax(DataTypeValue::S32, dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -505,7 +569,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecAnd(HVecAnd* instruction) { __ Vand(I8, dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -537,7 +601,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecOr(HVecOr* instruction) { __ Vorr(I8, dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -561,7 +625,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecXor(HVecXor* instruction) { __ Veor(I8, dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -580,7 +644,7 @@ static void CreateVecShiftLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -610,7 +674,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecShl(HVecShl* instruction) { __ Vshl(I32, dst, lhs, value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -640,7 +704,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecShr(HVecShr* instruction) { __ Vshr(DataTypeValue::S32, dst, lhs, value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -670,7 +734,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecUShr(HVecUShr* instruction) { __ Vshr(DataTypeValue::U32, dst, lhs, value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -690,7 +754,7 @@ void LocationsBuilderARMVIXL::VisitVecSetScalars(HVecSetScalars* instruction) { locations->SetOut(Location::RequiresFpuRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -716,7 +780,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecSetScalars(HVecSetScalars* instruc __ Vmov(Untyped32, DRegisterLane(dst, 0), InputRegisterAt(instruction, 0)); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -737,7 +801,7 @@ static void CreateVecAccumLocations(ArenaAllocator* allocator, HVecOperation* in locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -780,12 +844,12 @@ void InstructionCodeGeneratorARMVIXL::VisitVecSADAccumulate(HVecSADAccumulate* i break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -817,7 +881,7 @@ static void CreateVecMemLocations(ArenaAllocator* allocator, } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -923,7 +987,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecLoad(HVecLoad* instruction) { } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -971,7 +1035,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecStore(HVecStore* instruction) { } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } diff --git a/compiler/optimizing/code_generator_vector_mips.cc b/compiler/optimizing/code_generator_vector_mips.cc index ed9de964965da79fe70636374c95b9a777d8e693..df0e1485d69202722ea12a0ede99e21ce524230d 100644 --- a/compiler/optimizing/code_generator_vector_mips.cc +++ b/compiler/optimizing/code_generator_vector_mips.cc @@ -42,7 +42,7 @@ void LocationsBuilderMIPS::VisitVecReplicateScalar(HVecReplicateScalar* instruct locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -89,7 +89,7 @@ void InstructionCodeGeneratorMIPS::VisitVecReplicateScalar(HVecReplicateScalar* /* is_double */ true); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -113,7 +113,7 @@ void LocationsBuilderMIPS::VisitVecExtractScalar(HVecExtractScalar* instruction) locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -138,7 +138,7 @@ void InstructionCodeGeneratorMIPS::VisitVecExtractScalar(HVecExtractScalar* inst DCHECK(locations->InAt(0).Equals(locations->Out())); // no code required break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -170,7 +170,7 @@ static void CreateVecUnOpLocations(ArenaAllocator* allocator, HVecUnaryOperation : Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -225,7 +225,7 @@ void InstructionCodeGeneratorMIPS::VisitVecReduce(HVecReduce* instruction) { } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -244,7 +244,7 @@ void InstructionCodeGeneratorMIPS::VisitVecCnv(HVecCnv* instruction) { DCHECK_EQ(4u, instruction->GetVectorLength()); __ Ffint_sW(dst, src); } else { - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); } } @@ -290,7 +290,7 @@ void InstructionCodeGeneratorMIPS::VisitVecNeg(HVecNeg* instruction) { __ FsubD(dst, dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -337,7 +337,7 @@ void InstructionCodeGeneratorMIPS::VisitVecAbs(HVecAbs* instruction) { __ AndV(dst, dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -369,7 +369,7 @@ void InstructionCodeGeneratorMIPS::VisitVecNot(HVecNot* instruction) { __ NorV(dst, src, src); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -392,7 +392,7 @@ static void CreateVecBinOpLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -434,11 +434,19 @@ void InstructionCodeGeneratorMIPS::VisitVecAdd(HVecAdd* instruction) { __ FaddD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } +void LocationsBuilderMIPS::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorMIPS::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + LOG(FATAL) << "Unsupported SIMD " << instruction->GetId(); +} + void LocationsBuilderMIPS::VisitVecHalvingAdd(HVecHalvingAdd* instruction) { CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); } @@ -474,7 +482,7 @@ void InstructionCodeGeneratorMIPS::VisitVecHalvingAdd(HVecHalvingAdd* instructio : __ Ave_sH(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -516,11 +524,19 @@ void InstructionCodeGeneratorMIPS::VisitVecSub(HVecSub* instruction) { __ FsubD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } +void LocationsBuilderMIPS::VisitVecSaturationSub(HVecSaturationSub* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorMIPS::VisitVecSaturationSub(HVecSaturationSub* instruction) { + LOG(FATAL) << "Unsupported SIMD " << instruction->GetId(); +} + void LocationsBuilderMIPS::VisitVecMul(HVecMul* instruction) { CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); } @@ -558,7 +574,7 @@ void InstructionCodeGeneratorMIPS::VisitVecMul(HVecMul* instruction) { __ FmulD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -582,7 +598,7 @@ void InstructionCodeGeneratorMIPS::VisitVecDiv(HVecDiv* instruction) { __ FdivD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -640,7 +656,7 @@ void InstructionCodeGeneratorMIPS::VisitVecMin(HVecMin* instruction) { __ FminD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -698,7 +714,7 @@ void InstructionCodeGeneratorMIPS::VisitVecMax(HVecMax* instruction) { __ FmaxD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -727,7 +743,7 @@ void InstructionCodeGeneratorMIPS::VisitVecAnd(HVecAnd* instruction) { __ AndV(dst, lhs, rhs); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -764,7 +780,7 @@ void InstructionCodeGeneratorMIPS::VisitVecOr(HVecOr* instruction) { __ OrV(dst, lhs, rhs); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -793,7 +809,7 @@ void InstructionCodeGeneratorMIPS::VisitVecXor(HVecXor* instruction) { __ XorV(dst, lhs, rhs); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -813,7 +829,7 @@ static void CreateVecShiftLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -847,7 +863,7 @@ void InstructionCodeGeneratorMIPS::VisitVecShl(HVecShl* instruction) { __ SlliD(dst, lhs, value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -881,7 +897,7 @@ void InstructionCodeGeneratorMIPS::VisitVecShr(HVecShr* instruction) { __ SraiD(dst, lhs, value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -915,7 +931,7 @@ void InstructionCodeGeneratorMIPS::VisitVecUShr(HVecUShr* instruction) { __ SrliD(dst, lhs, value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -947,7 +963,7 @@ void LocationsBuilderMIPS::VisitVecSetScalars(HVecSetScalars* instruction) { locations->SetOut(Location::RequiresFpuRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -989,7 +1005,7 @@ void InstructionCodeGeneratorMIPS::VisitVecSetScalars(HVecSetScalars* instructio __ InsertW(dst, locations->InAt(0).AsRegisterPairHigh(), 1); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1010,7 +1026,7 @@ static void CreateVecAccumLocations(ArenaAllocator* allocator, HVecOperation* in locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1060,7 +1076,7 @@ void InstructionCodeGeneratorMIPS::VisitVecMultiplyAccumulate(HVecMultiplyAccumu } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1162,7 +1178,7 @@ void InstructionCodeGeneratorMIPS::VisitVecSADAccumulate(HVecSADAccumulate* inst break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; @@ -1201,7 +1217,7 @@ void InstructionCodeGeneratorMIPS::VisitVecSADAccumulate(HVecSADAccumulate* inst break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; @@ -1231,7 +1247,7 @@ void InstructionCodeGeneratorMIPS::VisitVecSADAccumulate(HVecSADAccumulate* inst break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; @@ -1247,13 +1263,13 @@ void InstructionCodeGeneratorMIPS::VisitVecSADAccumulate(HVecSADAccumulate* inst break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1282,7 +1298,7 @@ static void CreateVecMemLocations(ArenaAllocator* allocator, } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1357,7 +1373,7 @@ void InstructionCodeGeneratorMIPS::VisitVecLoad(HVecLoad* instruction) { __ LdD(reg, base, offset); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1395,7 +1411,7 @@ void InstructionCodeGeneratorMIPS::VisitVecStore(HVecStore* instruction) { __ StD(reg, base, offset); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } diff --git a/compiler/optimizing/code_generator_vector_mips64.cc b/compiler/optimizing/code_generator_vector_mips64.cc index 9ea55ec8d79d03379ed1d3a5e839fb8342f936bf..de354b63a1568fe5451f0204bb083cef576d9c7f 100644 --- a/compiler/optimizing/code_generator_vector_mips64.cc +++ b/compiler/optimizing/code_generator_vector_mips64.cc @@ -47,7 +47,7 @@ void LocationsBuilderMIPS64::VisitVecReplicateScalar(HVecReplicateScalar* instru locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -88,7 +88,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecReplicateScalar(HVecReplicateScalar /* is_double */ true); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -112,7 +112,7 @@ void LocationsBuilderMIPS64::VisitVecExtractScalar(HVecExtractScalar* instructio locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -136,7 +136,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecExtractScalar(HVecExtractScalar* in DCHECK(locations->InAt(0).Equals(locations->Out())); // no code required break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -168,7 +168,7 @@ static void CreateVecUnOpLocations(ArenaAllocator* allocator, HVecUnaryOperation : Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -223,7 +223,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecReduce(HVecReduce* instruction) { } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -242,7 +242,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecCnv(HVecCnv* instruction) { DCHECK_EQ(4u, instruction->GetVectorLength()); __ Ffint_sW(dst, src); } else { - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -289,7 +289,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecNeg(HVecNeg* instruction) { __ FsubD(dst, dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -336,7 +336,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecAbs(HVecAbs* instruction) { __ AndV(dst, dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -368,7 +368,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecNot(HVecNot* instruction) { __ NorV(dst, src, src); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -391,7 +391,7 @@ static void CreateVecBinOpLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -433,11 +433,19 @@ void InstructionCodeGeneratorMIPS64::VisitVecAdd(HVecAdd* instruction) { __ FaddD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } +void LocationsBuilderMIPS64::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorMIPS64::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + LOG(FATAL) << "Unsupported SIMD " << instruction->GetId(); +} + void LocationsBuilderMIPS64::VisitVecHalvingAdd(HVecHalvingAdd* instruction) { CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); } @@ -473,7 +481,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecHalvingAdd(HVecHalvingAdd* instruct : __ Ave_sH(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -515,11 +523,19 @@ void InstructionCodeGeneratorMIPS64::VisitVecSub(HVecSub* instruction) { __ FsubD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } +void LocationsBuilderMIPS64::VisitVecSaturationSub(HVecSaturationSub* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorMIPS64::VisitVecSaturationSub(HVecSaturationSub* instruction) { + LOG(FATAL) << "Unsupported SIMD " << instruction->GetId(); +} + void LocationsBuilderMIPS64::VisitVecMul(HVecMul* instruction) { CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); } @@ -557,7 +573,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecMul(HVecMul* instruction) { __ FmulD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -581,7 +597,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecDiv(HVecDiv* instruction) { __ FdivD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -639,7 +655,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecMin(HVecMin* instruction) { __ FminD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -697,7 +713,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecMax(HVecMax* instruction) { __ FmaxD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -726,7 +742,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecAnd(HVecAnd* instruction) { __ AndV(dst, lhs, rhs); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -763,7 +779,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecOr(HVecOr* instruction) { __ OrV(dst, lhs, rhs); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -792,7 +808,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecXor(HVecXor* instruction) { __ XorV(dst, lhs, rhs); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -812,7 +828,7 @@ static void CreateVecShiftLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -846,7 +862,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecShl(HVecShl* instruction) { __ SlliD(dst, lhs, value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -880,7 +896,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecShr(HVecShr* instruction) { __ SraiD(dst, lhs, value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -914,7 +930,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecUShr(HVecUShr* instruction) { __ SrliD(dst, lhs, value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -946,7 +962,7 @@ void LocationsBuilderMIPS64::VisitVecSetScalars(HVecSetScalars* instruction) { locations->SetOut(Location::RequiresFpuRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -987,7 +1003,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecSetScalars(HVecSetScalars* instruct __ InsertD(dst, locations->InAt(0).AsRegister(), 0); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1008,7 +1024,7 @@ static void CreateVecAccumLocations(ArenaAllocator* allocator, HVecOperation* in locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1058,7 +1074,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecMultiplyAccumulate(HVecMultiplyAccu } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1160,7 +1176,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecSADAccumulate(HVecSADAccumulate* in break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; @@ -1199,7 +1215,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecSADAccumulate(HVecSADAccumulate* in break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; @@ -1229,7 +1245,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecSADAccumulate(HVecSADAccumulate* in break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; @@ -1245,13 +1261,13 @@ void InstructionCodeGeneratorMIPS64::VisitVecSADAccumulate(HVecSADAccumulate* in break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1280,7 +1296,7 @@ static void CreateVecMemLocations(ArenaAllocator* allocator, } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1355,7 +1371,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecLoad(HVecLoad* instruction) { __ LdD(reg, base, offset); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1393,7 +1409,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecStore(HVecStore* instruction) { __ StD(reg, base, offset); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } diff --git a/compiler/optimizing/code_generator_vector_x86.cc b/compiler/optimizing/code_generator_vector_x86.cc index f2ffccc8879b5f46c33161fe27c6dc0f87dd38d0..086ae07a0644f74c4f2d09298bd554a665e2dc39 100644 --- a/compiler/optimizing/code_generator_vector_x86.cc +++ b/compiler/optimizing/code_generator_vector_x86.cc @@ -54,7 +54,7 @@ void LocationsBuilderX86::VisitVecReplicateScalar(HVecReplicateScalar* instructi : Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -111,7 +111,7 @@ void InstructionCodeGeneratorX86::VisitVecReplicateScalar(HVecReplicateScalar* i __ shufpd(dst, dst, Immediate(0)); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -138,7 +138,7 @@ void LocationsBuilderX86::VisitVecExtractScalar(HVecExtractScalar* instruction) locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -152,7 +152,7 @@ void InstructionCodeGeneratorX86::VisitVecExtractScalar(HVecExtractScalar* instr case DataType::Type::kInt8: case DataType::Type::kUint16: case DataType::Type::kInt16: // TODO: up to here, and? - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); case DataType::Type::kInt32: DCHECK_LE(4u, instruction->GetVectorLength()); @@ -174,7 +174,7 @@ void InstructionCodeGeneratorX86::VisitVecExtractScalar(HVecExtractScalar* instr DCHECK(locations->InAt(0).Equals(locations->Out())); // no code required break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -196,7 +196,7 @@ static void CreateVecUnOpLocations(ArenaAllocator* allocator, HVecUnaryOperation locations->SetOut(Location::RequiresFpuRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -258,12 +258,12 @@ void InstructionCodeGeneratorX86::VisitVecReduce(HVecReduce* instruction) { break; case HVecReduce::kMin: case HVecReduce::kMax: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); } break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -282,7 +282,7 @@ void InstructionCodeGeneratorX86::VisitVecCnv(HVecCnv* instruction) { DCHECK_EQ(4u, instruction->GetVectorLength()); __ cvtdq2ps(dst, src); } else { - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); } } @@ -328,7 +328,7 @@ void InstructionCodeGeneratorX86::VisitVecNeg(HVecNeg* instruction) { __ subpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -369,7 +369,7 @@ void InstructionCodeGeneratorX86::VisitVecAbs(HVecAbs* instruction) { __ andpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -418,7 +418,7 @@ void InstructionCodeGeneratorX86::VisitVecNot(HVecNot* instruction) { __ xorpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -441,7 +441,7 @@ static void CreateVecBinOpLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -483,7 +483,39 @@ void InstructionCodeGeneratorX86::VisitVecAdd(HVecAdd* instruction) { __ addpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); + UNREACHABLE(); + } +} + +void LocationsBuilderX86::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorX86::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + LocationSummary* locations = instruction->GetLocations(); + DCHECK(locations->InAt(0).Equals(locations->Out())); + XmmRegister src = locations->InAt(1).AsFpuRegister(); + XmmRegister dst = locations->Out().AsFpuRegister(); + switch (instruction->GetPackedType()) { + case DataType::Type::kUint8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ paddusb(dst, src); + break; + case DataType::Type::kInt8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ paddsb(dst, src); + break; + case DataType::Type::kUint16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ paddusw(dst, src); + break; + case DataType::Type::kInt16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ paddsw(dst, src); + break; + default: + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -503,14 +535,14 @@ void InstructionCodeGeneratorX86::VisitVecHalvingAdd(HVecHalvingAdd* instruction switch (instruction->GetPackedType()) { case DataType::Type::kUint8: DCHECK_EQ(16u, instruction->GetVectorLength()); - __ pavgb(dst, src); - return; + __ pavgb(dst, src); + break; case DataType::Type::kUint16: DCHECK_EQ(8u, instruction->GetVectorLength()); __ pavgw(dst, src); - return; + break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -552,7 +584,39 @@ void InstructionCodeGeneratorX86::VisitVecSub(HVecSub* instruction) { __ subpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); + UNREACHABLE(); + } +} + +void LocationsBuilderX86::VisitVecSaturationSub(HVecSaturationSub* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorX86::VisitVecSaturationSub(HVecSaturationSub* instruction) { + LocationSummary* locations = instruction->GetLocations(); + DCHECK(locations->InAt(0).Equals(locations->Out())); + XmmRegister src = locations->InAt(1).AsFpuRegister(); + XmmRegister dst = locations->Out().AsFpuRegister(); + switch (instruction->GetPackedType()) { + case DataType::Type::kUint8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ psubusb(dst, src); + break; + case DataType::Type::kInt8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ psubsb(dst, src); + break; + case DataType::Type::kUint16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ psubusw(dst, src); + break; + case DataType::Type::kInt16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ psubsw(dst, src); + break; + default: + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -585,7 +649,7 @@ void InstructionCodeGeneratorX86::VisitVecMul(HVecMul* instruction) { __ mulpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -609,7 +673,7 @@ void InstructionCodeGeneratorX86::VisitVecDiv(HVecDiv* instruction) { __ divpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -658,7 +722,7 @@ void InstructionCodeGeneratorX86::VisitVecMin(HVecMin* instruction) { __ minpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -707,7 +771,7 @@ void InstructionCodeGeneratorX86::VisitVecMax(HVecMax* instruction) { __ maxpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -742,7 +806,7 @@ void InstructionCodeGeneratorX86::VisitVecAnd(HVecAnd* instruction) { __ andpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -777,7 +841,7 @@ void InstructionCodeGeneratorX86::VisitVecAndNot(HVecAndNot* instruction) { __ andnpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -812,7 +876,7 @@ void InstructionCodeGeneratorX86::VisitVecOr(HVecOr* instruction) { __ orpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -847,7 +911,7 @@ void InstructionCodeGeneratorX86::VisitVecXor(HVecXor* instruction) { __ xorpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -865,7 +929,7 @@ static void CreateVecShiftLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -894,7 +958,7 @@ void InstructionCodeGeneratorX86::VisitVecShl(HVecShl* instruction) { __ psllq(dst, Immediate(static_cast(value))); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -919,7 +983,7 @@ void InstructionCodeGeneratorX86::VisitVecShr(HVecShr* instruction) { __ psrad(dst, Immediate(static_cast(value))); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -948,7 +1012,7 @@ void InstructionCodeGeneratorX86::VisitVecUShr(HVecUShr* instruction) { __ psrlq(dst, Immediate(static_cast(value))); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -985,7 +1049,7 @@ void LocationsBuilderX86::VisitVecSetScalars(HVecSetScalars* instruction) { locations->SetOut(Location::RequiresFpuRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1011,7 +1075,7 @@ void InstructionCodeGeneratorX86::VisitVecSetScalars(HVecSetScalars* instruction case DataType::Type::kInt8: case DataType::Type::kUint16: case DataType::Type::kInt16: // TODO: up to here, and? - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); case DataType::Type::kInt32: DCHECK_EQ(4u, instruction->GetVectorLength()); @@ -1035,7 +1099,7 @@ void InstructionCodeGeneratorX86::VisitVecSetScalars(HVecSetScalars* instruction __ movsd(dst, locations->InAt(1).AsFpuRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1056,7 +1120,7 @@ static void CreateVecAccumLocations(ArenaAllocator* allocator, HVecOperation* in locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1103,7 +1167,7 @@ static void CreateVecMemLocations(ArenaAllocator* allocator, } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1184,7 +1248,7 @@ void InstructionCodeGeneratorX86::VisitVecLoad(HVecLoad* instruction) { is_aligned16 ? __ movapd(reg, address) : __ movupd(reg, address); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1220,7 +1284,7 @@ void InstructionCodeGeneratorX86::VisitVecStore(HVecStore* instruction) { is_aligned16 ? __ movapd(address, reg) : __ movupd(address, reg); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } diff --git a/compiler/optimizing/code_generator_vector_x86_64.cc b/compiler/optimizing/code_generator_vector_x86_64.cc index e2b0485f890b68960c8ec8e37015f27bb9738903..4d31ab68d11878741f76abcd9f3b2c08f6c68e9e 100644 --- a/compiler/optimizing/code_generator_vector_x86_64.cc +++ b/compiler/optimizing/code_generator_vector_x86_64.cc @@ -49,7 +49,7 @@ void LocationsBuilderX86_64::VisitVecReplicateScalar(HVecReplicateScalar* instru : Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -102,7 +102,7 @@ void InstructionCodeGeneratorX86_64::VisitVecReplicateScalar(HVecReplicateScalar __ shufpd(dst, dst, Immediate(0)); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -126,7 +126,7 @@ void LocationsBuilderX86_64::VisitVecExtractScalar(HVecExtractScalar* instructio locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -140,7 +140,7 @@ void InstructionCodeGeneratorX86_64::VisitVecExtractScalar(HVecExtractScalar* in case DataType::Type::kInt8: case DataType::Type::kUint16: case DataType::Type::kInt16: // TODO: up to here, and? - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); case DataType::Type::kInt32: DCHECK_EQ(4u, instruction->GetVectorLength()); @@ -157,7 +157,7 @@ void InstructionCodeGeneratorX86_64::VisitVecExtractScalar(HVecExtractScalar* in DCHECK(locations->InAt(0).Equals(locations->Out())); // no code required break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -179,7 +179,7 @@ static void CreateVecUnOpLocations(ArenaAllocator* allocator, HVecUnaryOperation locations->SetOut(Location::RequiresFpuRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -241,12 +241,12 @@ void InstructionCodeGeneratorX86_64::VisitVecReduce(HVecReduce* instruction) { break; case HVecReduce::kMin: case HVecReduce::kMax: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); } break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -265,7 +265,7 @@ void InstructionCodeGeneratorX86_64::VisitVecCnv(HVecCnv* instruction) { DCHECK_EQ(4u, instruction->GetVectorLength()); __ cvtdq2ps(dst, src); } else { - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); } } @@ -311,7 +311,7 @@ void InstructionCodeGeneratorX86_64::VisitVecNeg(HVecNeg* instruction) { __ subpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -352,7 +352,7 @@ void InstructionCodeGeneratorX86_64::VisitVecAbs(HVecAbs* instruction) { __ andpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -401,7 +401,7 @@ void InstructionCodeGeneratorX86_64::VisitVecNot(HVecNot* instruction) { __ xorpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -424,7 +424,7 @@ static void CreateVecBinOpLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -466,7 +466,39 @@ void InstructionCodeGeneratorX86_64::VisitVecAdd(HVecAdd* instruction) { __ addpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); + UNREACHABLE(); + } +} + +void LocationsBuilderX86_64::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorX86_64::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + LocationSummary* locations = instruction->GetLocations(); + DCHECK(locations->InAt(0).Equals(locations->Out())); + XmmRegister src = locations->InAt(1).AsFpuRegister(); + XmmRegister dst = locations->Out().AsFpuRegister(); + switch (instruction->GetPackedType()) { + case DataType::Type::kUint8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ paddusb(dst, src); + break; + case DataType::Type::kInt8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ paddsb(dst, src); + break; + case DataType::Type::kUint16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ paddusw(dst, src); + break; + case DataType::Type::kInt16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ paddsw(dst, src); + break; + default: + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -486,14 +518,14 @@ void InstructionCodeGeneratorX86_64::VisitVecHalvingAdd(HVecHalvingAdd* instruct switch (instruction->GetPackedType()) { case DataType::Type::kUint8: DCHECK_EQ(16u, instruction->GetVectorLength()); - __ pavgb(dst, src); - return; + __ pavgb(dst, src); + break; case DataType::Type::kUint16: DCHECK_EQ(8u, instruction->GetVectorLength()); __ pavgw(dst, src); - return; + break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -535,7 +567,39 @@ void InstructionCodeGeneratorX86_64::VisitVecSub(HVecSub* instruction) { __ subpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); + UNREACHABLE(); + } +} + +void LocationsBuilderX86_64::VisitVecSaturationSub(HVecSaturationSub* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorX86_64::VisitVecSaturationSub(HVecSaturationSub* instruction) { + LocationSummary* locations = instruction->GetLocations(); + DCHECK(locations->InAt(0).Equals(locations->Out())); + XmmRegister src = locations->InAt(1).AsFpuRegister(); + XmmRegister dst = locations->Out().AsFpuRegister(); + switch (instruction->GetPackedType()) { + case DataType::Type::kUint8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ psubusb(dst, src); + break; + case DataType::Type::kInt8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ psubsb(dst, src); + break; + case DataType::Type::kUint16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ psubusw(dst, src); + break; + case DataType::Type::kInt16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ psubsw(dst, src); + break; + default: + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -568,7 +632,7 @@ void InstructionCodeGeneratorX86_64::VisitVecMul(HVecMul* instruction) { __ mulpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -592,7 +656,7 @@ void InstructionCodeGeneratorX86_64::VisitVecDiv(HVecDiv* instruction) { __ divpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -641,7 +705,7 @@ void InstructionCodeGeneratorX86_64::VisitVecMin(HVecMin* instruction) { __ minpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -690,7 +754,7 @@ void InstructionCodeGeneratorX86_64::VisitVecMax(HVecMax* instruction) { __ maxpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -725,7 +789,7 @@ void InstructionCodeGeneratorX86_64::VisitVecAnd(HVecAnd* instruction) { __ andpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -760,7 +824,7 @@ void InstructionCodeGeneratorX86_64::VisitVecAndNot(HVecAndNot* instruction) { __ andnpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -795,7 +859,7 @@ void InstructionCodeGeneratorX86_64::VisitVecOr(HVecOr* instruction) { __ orpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -830,7 +894,7 @@ void InstructionCodeGeneratorX86_64::VisitVecXor(HVecXor* instruction) { __ xorpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -848,7 +912,7 @@ static void CreateVecShiftLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -877,7 +941,7 @@ void InstructionCodeGeneratorX86_64::VisitVecShl(HVecShl* instruction) { __ psllq(dst, Immediate(static_cast(value))); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -902,7 +966,7 @@ void InstructionCodeGeneratorX86_64::VisitVecShr(HVecShr* instruction) { __ psrad(dst, Immediate(static_cast(value))); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -931,7 +995,7 @@ void InstructionCodeGeneratorX86_64::VisitVecUShr(HVecUShr* instruction) { __ psrlq(dst, Immediate(static_cast(value))); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -963,7 +1027,7 @@ void LocationsBuilderX86_64::VisitVecSetScalars(HVecSetScalars* instruction) { locations->SetOut(Location::RequiresFpuRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -989,7 +1053,7 @@ void InstructionCodeGeneratorX86_64::VisitVecSetScalars(HVecSetScalars* instruct case DataType::Type::kInt8: case DataType::Type::kUint16: case DataType::Type::kInt16: // TODO: up to here, and? - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); case DataType::Type::kInt32: DCHECK_EQ(4u, instruction->GetVectorLength()); @@ -1008,7 +1072,7 @@ void InstructionCodeGeneratorX86_64::VisitVecSetScalars(HVecSetScalars* instruct __ movsd(dst, locations->InAt(0).AsFpuRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1029,7 +1093,7 @@ static void CreateVecAccumLocations(ArenaAllocator* allocator, HVecOperation* in locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1076,7 +1140,7 @@ static void CreateVecMemLocations(ArenaAllocator* allocator, } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1157,7 +1221,7 @@ void InstructionCodeGeneratorX86_64::VisitVecLoad(HVecLoad* instruction) { is_aligned16 ? __ movapd(reg, address) : __ movupd(reg, address); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1193,7 +1257,7 @@ void InstructionCodeGeneratorX86_64::VisitVecStore(HVecStore* instruction) { is_aligned16 ? __ movapd(address, reg) : __ movupd(address, reg); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 6bf045885d6f5b33680a0f1ed241059c8786f650..1c0d283ef6b9338ea4f982dbd8013921c61ef1b6 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -23,6 +23,7 @@ #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "gc/accounting/card_table.h" +#include "gc/space/image_space.h" #include "heap_poisoning.h" #include "intrinsics.h" #include "intrinsics_x86.h" @@ -51,6 +52,9 @@ static constexpr int kC2ConditionMask = 0x400; static constexpr int kFakeReturnRegister = Register(8); +static constexpr int64_t kDoubleNaN = INT64_C(0x7FF8000000000000); +static constexpr int32_t kFloatNaN = INT32_C(0x7FC00000); + // NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy. #define __ down_cast(codegen->GetAssembler())-> // NOLINT #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kX86PointerSize, x).Int32Value() @@ -954,6 +958,10 @@ void CodeGeneratorX86::DumpFloatingPointRegister(std::ostream& stream, int reg) stream << XmmRegister(reg); } +const X86InstructionSetFeatures& CodeGeneratorX86::GetInstructionSetFeatures() const { + return *GetCompilerOptions().GetInstructionSetFeatures()->AsX86InstructionSetFeatures(); +} + size_t CodeGeneratorX86::SaveCoreRegister(size_t stack_index, uint32_t reg_id) { __ movl(Address(ESP, stack_index), static_cast(reg_id)); return kX86WordSize; @@ -1005,7 +1013,6 @@ void CodeGeneratorX86::GenerateInvokeRuntime(int32_t entry_point_offset) { } CodeGeneratorX86::CodeGeneratorX86(HGraph* graph, - const X86InstructionSetFeatures& isa_features, const CompilerOptions& compiler_options, OptimizingCompilerStats* stats) : CodeGenerator(graph, @@ -1023,13 +1030,13 @@ CodeGeneratorX86::CodeGeneratorX86(HGraph* graph, instruction_visitor_(graph, this), move_resolver_(graph->GetAllocator(), this), assembler_(graph->GetAllocator()), - isa_features_(isa_features), boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), + boot_image_intrinsic_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), jit_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), jit_class_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), constant_area_start_(-1), @@ -2185,7 +2192,9 @@ void LocationsBuilderX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invok IntrinsicLocationsBuilderX86 intrinsic(codegen_); if (intrinsic.TryDispatch(invoke)) { - if (invoke->GetLocations()->CanCall() && invoke->HasPcRelativeMethodLoadKind()) { + if (invoke->GetLocations()->CanCall() && + invoke->HasPcRelativeMethodLoadKind() && + invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()).IsInvalid()) { invoke->GetLocations()->SetInAt(invoke->GetSpecialInputIndex(), Location::Any()); } return; @@ -2308,6 +2317,14 @@ void InstructionCodeGeneratorX86::VisitInvokePolymorphic(HInvokePolymorphic* inv 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); @@ -3802,6 +3819,301 @@ void InstructionCodeGeneratorX86::VisitRem(HRem* rem) { } } +static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) { + LocationSummary* locations = new (allocator) LocationSummary(minmax); + switch (minmax->GetResultType()) { + case DataType::Type::kInt32: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + // Register to use to perform a long subtract to set cc. + locations->AddTemp(Location::RequiresRegister()); + break; + case DataType::Type::kFloat32: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); + locations->AddTemp(Location::RequiresRegister()); + break; + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + default: + LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType(); + } +} + +void InstructionCodeGeneratorX86::GenerateMinMaxInt(LocationSummary* locations, + bool is_min, + DataType::Type type) { + Location op1_loc = locations->InAt(0); + Location op2_loc = locations->InAt(1); + + // Shortcut for same input locations. + if (op1_loc.Equals(op2_loc)) { + // Can return immediately, as op1_loc == out_loc. + // Note: if we ever support separate registers, e.g., output into memory, we need to check for + // a copy here. + DCHECK(locations->Out().Equals(op1_loc)); + return; + } + + if (type == DataType::Type::kInt64) { + // Need to perform a subtract to get the sign right. + // op1 is already in the same location as the output. + Location output = locations->Out(); + Register output_lo = output.AsRegisterPairLow(); + Register output_hi = output.AsRegisterPairHigh(); + + Register op2_lo = op2_loc.AsRegisterPairLow(); + Register op2_hi = op2_loc.AsRegisterPairHigh(); + + // The comparison is performed by subtracting the second operand from + // the first operand and then setting the status flags in the same + // manner as the SUB instruction." + __ cmpl(output_lo, op2_lo); + + // Now use a temp and the borrow to finish the subtraction of op2_hi. + Register temp = locations->GetTemp(0).AsRegister(); + __ movl(temp, output_hi); + __ sbbl(temp, op2_hi); + + // Now the condition code is correct. + Condition cond = is_min ? Condition::kGreaterEqual : Condition::kLess; + __ cmovl(cond, output_lo, op2_lo); + __ cmovl(cond, output_hi, op2_hi); + } else { + DCHECK_EQ(type, DataType::Type::kInt32); + Register out = locations->Out().AsRegister(); + Register op2 = op2_loc.AsRegister(); + + // (out := op1) + // out <=? op2 + // if out is min jmp done + // out := op2 + // done: + + __ cmpl(out, op2); + Condition cond = is_min ? Condition::kGreater : Condition::kLess; + __ cmovl(cond, out, op2); + } +} + +void InstructionCodeGeneratorX86::GenerateMinMaxFP(LocationSummary* locations, + bool is_min, + DataType::Type type) { + Location op1_loc = locations->InAt(0); + Location op2_loc = locations->InAt(1); + Location out_loc = locations->Out(); + XmmRegister out = out_loc.AsFpuRegister(); + + // Shortcut for same input locations. + if (op1_loc.Equals(op2_loc)) { + DCHECK(out_loc.Equals(op1_loc)); + return; + } + + // (out := op1) + // out <=? op2 + // if Nan jmp Nan_label + // if out is min jmp done + // if op2 is min jmp op2_label + // handle -0/+0 + // jmp done + // Nan_label: + // out := NaN + // op2_label: + // out := op2 + // done: + // + // This removes one jmp, but needs to copy one input (op1) to out. + // + // TODO: This is straight from Quick (except literal pool). Make NaN an out-of-line slowpath? + + XmmRegister op2 = op2_loc.AsFpuRegister(); + + NearLabel nan, done, op2_label; + if (type == DataType::Type::kFloat64) { + __ ucomisd(out, op2); + } else { + DCHECK_EQ(type, DataType::Type::kFloat32); + __ ucomiss(out, op2); + } + + __ j(Condition::kParityEven, &nan); + + __ j(is_min ? Condition::kAbove : Condition::kBelow, &op2_label); + __ j(is_min ? Condition::kBelow : Condition::kAbove, &done); + + // Handle 0.0/-0.0. + if (is_min) { + if (type == DataType::Type::kFloat64) { + __ orpd(out, op2); + } else { + __ orps(out, op2); + } + } else { + if (type == DataType::Type::kFloat64) { + __ andpd(out, op2); + } else { + __ andps(out, op2); + } + } + __ jmp(&done); + + // NaN handling. + __ Bind(&nan); + if (type == DataType::Type::kFloat64) { + // TODO: Use a constant from the constant table (requires extra input). + __ LoadLongConstant(out, kDoubleNaN); + } else { + Register constant = locations->GetTemp(0).AsRegister(); + __ movl(constant, Immediate(kFloatNaN)); + __ movd(out, constant); + } + __ jmp(&done); + + // out := op2; + __ Bind(&op2_label); + if (type == DataType::Type::kFloat64) { + __ movsd(out, op2); + } else { + __ movss(out, op2); + } + + // Done. + __ Bind(&done); +} + +void InstructionCodeGeneratorX86::GenerateMinMax(HBinaryOperation* minmax, bool is_min) { + DataType::Type type = minmax->GetResultType(); + switch (type) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + GenerateMinMaxInt(minmax->GetLocations(), is_min, type); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + GenerateMinMaxFP(minmax->GetLocations(), is_min, type); + break; + default: + LOG(FATAL) << "Unexpected type for HMinMax " << type; + } +} + +void LocationsBuilderX86::VisitMin(HMin* min) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), min); +} + +void InstructionCodeGeneratorX86::VisitMin(HMin* min) { + GenerateMinMax(min, /*is_min*/ true); +} + +void LocationsBuilderX86::VisitMax(HMax* max) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), max); +} + +void InstructionCodeGeneratorX86::VisitMax(HMax* max) { + GenerateMinMax(max, /*is_min*/ false); +} + +void LocationsBuilderX86::VisitAbs(HAbs* abs) { + LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: + locations->SetInAt(0, Location::RegisterLocation(EAX)); + locations->SetOut(Location::SameAsFirstInput()); + locations->AddTemp(Location::RegisterLocation(EDX)); + break; + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); + locations->AddTemp(Location::RequiresRegister()); + break; + case DataType::Type::kFloat32: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); + locations->AddTemp(Location::RequiresFpuRegister()); + locations->AddTemp(Location::RequiresRegister()); + break; + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); + locations->AddTemp(Location::RequiresFpuRegister()); + break; + default: + LOG(FATAL) << "Unexpected type for HAbs " << abs->GetResultType(); + } +} + +void InstructionCodeGeneratorX86::VisitAbs(HAbs* abs) { + LocationSummary* locations = abs->GetLocations(); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: { + Register out = locations->Out().AsRegister(); + DCHECK_EQ(out, EAX); + Register temp = locations->GetTemp(0).AsRegister(); + DCHECK_EQ(temp, EDX); + // Sign extend EAX into EDX. + __ cdq(); + // XOR EAX with sign. + __ xorl(EAX, EDX); + // Subtract out sign to correct. + __ subl(EAX, EDX); + // The result is in EAX. + break; + } + case DataType::Type::kInt64: { + Location input = locations->InAt(0); + Register input_lo = input.AsRegisterPairLow(); + Register input_hi = input.AsRegisterPairHigh(); + Location output = locations->Out(); + Register output_lo = output.AsRegisterPairLow(); + Register output_hi = output.AsRegisterPairHigh(); + Register temp = locations->GetTemp(0).AsRegister(); + // Compute the sign into the temporary. + __ movl(temp, input_hi); + __ sarl(temp, Immediate(31)); + // Store the sign into the output. + __ movl(output_lo, temp); + __ movl(output_hi, temp); + // XOR the input to the output. + __ xorl(output_lo, input_lo); + __ xorl(output_hi, input_hi); + // Subtract the sign. + __ subl(output_lo, temp); + __ sbbl(output_hi, temp); + break; + } + case DataType::Type::kFloat32: { + XmmRegister out = locations->Out().AsFpuRegister(); + XmmRegister temp = locations->GetTemp(0).AsFpuRegister(); + Register constant = locations->GetTemp(1).AsRegister(); + __ movl(constant, Immediate(INT32_C(0x7FFFFFFF))); + __ movd(temp, constant); + __ andps(out, temp); + break; + } + case DataType::Type::kFloat64: { + XmmRegister out = locations->Out().AsFpuRegister(); + XmmRegister temp = locations->GetTemp(0).AsFpuRegister(); + // TODO: Use a constant from the constant table (requires extra input). + __ LoadLongConstant(temp, INT64_C(0x7FFFFFFFFFFFFFFF)); + __ andpd(out, temp); + break; + } + default: + LOG(FATAL) << "Unexpected type for HAbs " << abs->GetResultType(); + } +} + void LocationsBuilderX86::VisitDivZeroCheck(HDivZeroCheck* instruction) { LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction); switch (instruction->GetType()) { @@ -4184,29 +4496,14 @@ void LocationsBuilderX86::VisitNewInstance(HNewInstance* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary( instruction, LocationSummary::kCallOnMainOnly); locations->SetOut(Location::RegisterLocation(EAX)); - if (instruction->IsStringAlloc()) { - locations->AddTemp(Location::RegisterLocation(kMethodRegisterArgument)); - } else { - InvokeRuntimeCallingConvention calling_convention; - locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); - } + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); } void InstructionCodeGeneratorX86::VisitNewInstance(HNewInstance* instruction) { - // Note: if heap poisoning is enabled, the entry point takes cares - // of poisoning the reference. - if (instruction->IsStringAlloc()) { - // String is allocated through StringFactory. Call NewEmptyString entry point. - Register temp = instruction->GetLocations()->GetTemp(0).AsRegister(); - MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86PointerSize); - __ fs()->movl(temp, Address::Absolute(QUICK_ENTRY_POINT(pNewEmptyString))); - __ call(Address(temp, code_offset.Int32Value())); - codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); - } else { - codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); - CheckEntrypointTypes(); - DCHECK(!codegen_->IsLeafMethod()); - } + codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); + CheckEntrypointTypes(); + DCHECK(!codegen_->IsLeafMethod()); } void LocationsBuilderX86::VisitNewArray(HNewArray* instruction) { @@ -4534,6 +4831,15 @@ void CodeGeneratorX86::GenerateStaticOrDirectCall( case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: __ movl(temp.AsRegister(), Immediate(invoke->GetMethodAddress())); break; + case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: { + Register base_reg = GetInvokeStaticOrDirectExtraParameter(invoke, + temp.AsRegister()); + __ movl(temp.AsRegister(), Address(base_reg, kDummy32BitOffset)); + RecordBootImageRelRoPatch( + invoke->InputAt(invoke->GetSpecialInputIndex())->AsX86ComputeBaseMethodAddress(), + GetBootImageOffset(invoke)); + break; + } case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: { Register base_reg = GetInvokeStaticOrDirectExtraParameter(invoke, temp.AsRegister()); @@ -4595,6 +4901,20 @@ void CodeGeneratorX86::GenerateVirtualCall( RecordPcInfo(invoke, invoke->GetDexPc(), slow_path); } +void CodeGeneratorX86::RecordBootImageIntrinsicPatch(HX86ComputeBaseMethodAddress* method_address, + uint32_t intrinsic_data) { + boot_image_intrinsic_patches_.emplace_back( + method_address, /* target_dex_file */ nullptr, intrinsic_data); + __ Bind(&boot_image_intrinsic_patches_.back().label); +} + +void CodeGeneratorX86::RecordBootImageRelRoPatch(HX86ComputeBaseMethodAddress* method_address, + uint32_t boot_image_offset) { + boot_image_method_patches_.emplace_back( + method_address, /* target_dex_file */ nullptr, boot_image_offset); + __ Bind(&boot_image_method_patches_.back().label); +} + void CodeGeneratorX86::RecordBootImageMethodPatch(HInvokeStaticOrDirect* invoke) { DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u); HX86ComputeBaseMethodAddress* method_address = @@ -4647,6 +4967,62 @@ Label* CodeGeneratorX86::NewStringBssEntryPatch(HLoadString* load_string) { return &string_bss_entry_patches_.back().label; } +void CodeGeneratorX86::LoadBootImageAddress(Register reg, + uint32_t boot_image_reference, + HInvokeStaticOrDirect* invoke) { + if (GetCompilerOptions().IsBootImage()) { + DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u); + HX86ComputeBaseMethodAddress* method_address = + invoke->InputAt(invoke->GetSpecialInputIndex())->AsX86ComputeBaseMethodAddress(); + DCHECK(method_address != nullptr); + Register method_address_reg = + invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()).AsRegister(); + __ leal(reg, Address(method_address_reg, CodeGeneratorX86::kDummy32BitOffset)); + RecordBootImageIntrinsicPatch(method_address, boot_image_reference); + } else if (GetCompilerOptions().GetCompilePic()) { + DCHECK(Runtime::Current()->IsAotCompiler()); + DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u); + HX86ComputeBaseMethodAddress* method_address = + invoke->InputAt(invoke->GetSpecialInputIndex())->AsX86ComputeBaseMethodAddress(); + DCHECK(method_address != nullptr); + Register method_address_reg = + invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()).AsRegister(); + __ movl(reg, Address(method_address_reg, CodeGeneratorX86::kDummy32BitOffset)); + RecordBootImageRelRoPatch(method_address, boot_image_reference); + } else { + gc::Heap* heap = Runtime::Current()->GetHeap(); + DCHECK(!heap->GetBootImageSpaces().empty()); + const uint8_t* address = heap->GetBootImageSpaces()[0]->Begin() + boot_image_reference; + __ movl(reg, Immediate(dchecked_integral_cast(reinterpret_cast(address)))); + } +} + +void CodeGeneratorX86::AllocateInstanceForIntrinsic(HInvokeStaticOrDirect* invoke, + uint32_t boot_image_offset) { + DCHECK(invoke->IsStatic()); + InvokeRuntimeCallingConvention calling_convention; + Register argument = calling_convention.GetRegisterAt(0); + if (GetCompilerOptions().IsBootImage()) { + DCHECK_EQ(boot_image_offset, IntrinsicVisitor::IntegerValueOfInfo::kInvalidReference); + // Load the class the same way as for HLoadClass::LoadKind::kBootImageLinkTimePcRelative. + DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u); + HX86ComputeBaseMethodAddress* method_address = + invoke->InputAt(invoke->GetSpecialInputIndex())->AsX86ComputeBaseMethodAddress(); + DCHECK(method_address != nullptr); + Register method_address_reg = + invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()).AsRegister(); + __ leal(argument, Address(method_address_reg, CodeGeneratorX86::kDummy32BitOffset)); + MethodReference target_method = invoke->GetTargetMethod(); + dex::TypeIndex type_idx = target_method.dex_file->GetMethodId(target_method.index).class_idx_; + boot_image_type_patches_.emplace_back(method_address, target_method.dex_file, type_idx.index_); + __ Bind(&boot_image_type_patches_.back().label); + } else { + LoadBootImageAddress(argument, boot_image_offset, invoke); + } + InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc()); + CheckEntrypointTypes(); +} + // The label points to the end of the "movl" or another instruction but the literal offset // for method patch needs to point to the embedded constant which occupies the last 4 bytes. constexpr uint32_t kLabelPositionToLiteralOffsetAdjustment = 4u; @@ -4664,6 +5040,15 @@ inline void CodeGeneratorX86::EmitPcRelativeLinkerPatches( } } +template +linker::LinkerPatch NoDexFileAdapter(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t boot_image_offset) { + DCHECK(target_dex_file == nullptr); // Unused for these patches, should be null. + return Factory(literal_offset, pc_insn_offset, boot_image_offset); +} + void CodeGeneratorX86::EmitLinkerPatches(ArenaVector* linker_patches) { DCHECK(linker_patches->empty()); size_t size = @@ -4672,7 +5057,8 @@ void CodeGeneratorX86::EmitLinkerPatches(ArenaVector* linke boot_image_type_patches_.size() + type_bss_entry_patches_.size() + boot_image_string_patches_.size() + - string_bss_entry_patches_.size(); + string_bss_entry_patches_.size() + + boot_image_intrinsic_patches_.size(); linker_patches->reserve(size); if (GetCompilerOptions().IsBootImage()) { EmitPcRelativeLinkerPatches( @@ -4681,12 +5067,14 @@ void CodeGeneratorX86::EmitLinkerPatches(ArenaVector* linke boot_image_type_patches_, linker_patches); EmitPcRelativeLinkerPatches( boot_image_string_patches_, linker_patches); + EmitPcRelativeLinkerPatches>( + boot_image_intrinsic_patches_, linker_patches); } else { - DCHECK(boot_image_method_patches_.empty()); - EmitPcRelativeLinkerPatches( - boot_image_type_patches_, linker_patches); - EmitPcRelativeLinkerPatches( - boot_image_string_patches_, linker_patches); + EmitPcRelativeLinkerPatches>( + boot_image_method_patches_, linker_patches); + DCHECK(boot_image_type_patches_.empty()); + DCHECK(boot_image_string_patches_.empty()); + DCHECK(boot_image_intrinsic_patches_.empty()); } EmitPcRelativeLinkerPatches( method_bss_entry_patches_, linker_patches); @@ -6055,7 +6443,7 @@ HLoadClass::LoadKind CodeGeneratorX86::GetSupportedLoadClassKind( case HLoadClass::LoadKind::kReferrersClass: break; case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: - case HLoadClass::LoadKind::kBootImageClassTable: + case HLoadClass::LoadKind::kBootImageRelRo: case HLoadClass::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -6093,7 +6481,7 @@ void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) { if (load_kind == HLoadClass::LoadKind::kReferrersClass || load_kind == HLoadClass::LoadKind::kBootImageLinkTimePcRelative || - load_kind == HLoadClass::LoadKind::kBootImageClassTable || + load_kind == HLoadClass::LoadKind::kBootImageRelRo || load_kind == HLoadClass::LoadKind::kBssEntry) { locations->SetInAt(0, Location::RequiresRegister()); } @@ -6169,17 +6557,12 @@ void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFE __ movl(out, Immediate(address)); break; } - case HLoadClass::LoadKind::kBootImageClassTable: { + case HLoadClass::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); Register method_address = locations->InAt(0).AsRegister(); __ movl(out, Address(method_address, CodeGeneratorX86::kDummy32BitOffset)); - codegen_->RecordBootImageTypePatch(cls); - // Extract the reference from the slot data, i.e. clear the hash bits. - int32_t masked_hash = ClassTable::TableSlot::MaskHash( - ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex()))); - if (masked_hash != 0) { - __ subl(out, Immediate(masked_hash)); - } + codegen_->RecordBootImageRelRoPatch(cls->InputAt(0)->AsX86ComputeBaseMethodAddress(), + codegen_->GetBootImageOffset(cls)); break; } case HLoadClass::LoadKind::kBssEntry: { @@ -6223,6 +6606,26 @@ void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFE } } +void LocationsBuilderX86::VisitLoadMethodHandle(HLoadMethodHandle* load) { + InvokeRuntimeCallingConvention calling_convention; + Location location = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorX86::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + +void LocationsBuilderX86::VisitLoadMethodType(HLoadMethodType* load) { + InvokeRuntimeCallingConvention calling_convention; + Location location = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorX86::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + void LocationsBuilderX86::VisitClinitCheck(HClinitCheck* check) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath); @@ -6255,11 +6658,31 @@ void InstructionCodeGeneratorX86::GenerateClassInitializationCheck( // No need for memory fence, thanks to the X86 memory model. } +void InstructionCodeGeneratorX86::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, + Register temp) { + uint32_t path_to_root = check->GetBitstringPathToRoot(); + uint32_t mask = check->GetBitstringMask(); + DCHECK(IsPowerOfTwo(mask + 1)); + size_t mask_bits = WhichPowerOf2(mask + 1); + + if (mask_bits == 16u) { + // Compare the bitstring in memory. + __ cmpw(Address(temp, mirror::Class::StatusOffset()), Immediate(path_to_root)); + } else { + // /* uint32_t */ temp = temp->status_ + __ movl(temp, Address(temp, mirror::Class::StatusOffset())); + // Compare the bitstring bits using SUB. + __ subl(temp, Immediate(path_to_root)); + // Shift out bits that do not contribute to the comparison. + __ shll(temp, Immediate(32u - mask_bits)); + } +} + HLoadString::LoadKind CodeGeneratorX86::GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) { switch (desired_string_load_kind) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: - case HLoadString::LoadKind::kBootImageInternTable: + case HLoadString::LoadKind::kBootImageRelRo: case HLoadString::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -6278,7 +6701,7 @@ void LocationsBuilderX86::VisitLoadString(HLoadString* load) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(load, call_kind); HLoadString::LoadKind load_kind = load->GetLoadKind(); if (load_kind == HLoadString::LoadKind::kBootImageLinkTimePcRelative || - load_kind == HLoadString::LoadKind::kBootImageInternTable || + load_kind == HLoadString::LoadKind::kBootImageRelRo || load_kind == HLoadString::LoadKind::kBssEntry) { locations->SetInAt(0, Location::RequiresRegister()); } @@ -6332,11 +6755,12 @@ void InstructionCodeGeneratorX86::VisitLoadString(HLoadString* load) NO_THREAD_S __ movl(out, Immediate(address)); return; } - case HLoadString::LoadKind::kBootImageInternTable: { + case HLoadString::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); Register method_address = locations->InAt(0).AsRegister(); __ movl(out, Address(method_address, CodeGeneratorX86::kDummy32BitOffset)); - codegen_->RecordBootImageStringPatch(load); + codegen_->RecordBootImageRelRoPatch(load->InputAt(0)->AsX86ComputeBaseMethodAddress(), + codegen_->GetBootImageOffset(load)); return; } case HLoadString::LoadKind::kBssEntry: { @@ -6418,8 +6842,8 @@ static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) { return 0; } -// Interface case has 3 temps, one for holding the number of interfaces, one for the current -// interface pointer, one for loading the current interface. +// Interface case has 2 temps, one for holding the number of interfaces, one for the current +// interface pointer, the current interface is compared in memory. // The other checks have one temp for loading the object's class. static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) { if (type_check_kind == TypeCheckKind::kInterfaceCheck) { @@ -6447,6 +6871,8 @@ void LocationsBuilderX86::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; + case TypeCheckKind::kBitstringCheck: + break; } LocationSummary* locations = @@ -6455,7 +6881,13 @@ void LocationsBuilderX86::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::Any()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::Any()); + } // Note that TypeCheckSlowPathX86 uses this "out" register too. locations->SetOut(Location::RequiresRegister()); // When read barriers are enabled, we need a temporary register for some cases. @@ -6676,6 +7108,21 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { } break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, out); + __ j(kNotEqual, &zero); + __ movl(out, Immediate(1)); + __ jmp(&done); + break; + } } if (zero.IsLinked()) { @@ -6702,12 +7149,14 @@ void LocationsBuilderX86::VisitCheckCast(HCheckCast* instruction) { // Require a register for the interface check since there is a loop that compares the class to // a memory address. locations->SetInAt(1, Location::RequiresRegister()); + } else if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); } else { locations->SetInAt(1, Location::Any()); } - // Note that TypeCheckSlowPathX86 uses this "temp" register too. - locations->AddTemp(Location::RequiresRegister()); - // When read barriers are enabled, we need an additional temporary register for some cases. + // Add temps for read barriers and other uses. One is used by TypeCheckSlowPathX86. locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } @@ -6921,6 +7370,19 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { __ MaybeUnpoisonHeapReference(cls.AsRegister()); break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, temp); + __ j(kNotEqual, type_check_slow_path->GetEntryLabel()); + break; + } } __ Bind(&done); diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 51e5bca00b6989675b312999e0bc3e334b206c5b..cb58e920ea1c35e0a5b7a5544c426a974d92e41b 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -211,6 +211,7 @@ class InstructionCodeGeneratorX86 : public InstructionCodeGenerator { // the suspend call. void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor); void GenerateClassInitializationCheck(SlowPathCode* slow_path, Register class_reg); + void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, Register temp); void HandleBitwiseOperation(HBinaryOperation* instruction); void GenerateDivRemIntegral(HBinaryOperation* instruction); void DivRemOneOrMinusOne(HBinaryOperation* instruction); @@ -225,6 +226,9 @@ class InstructionCodeGeneratorX86 : public InstructionCodeGenerator { void GenerateShlLong(const Location& loc, int shift); void GenerateShrLong(const Location& loc, int shift); void GenerateUShrLong(const Location& loc, int shift); + void GenerateMinMaxInt(LocationSummary* locations, bool is_min, DataType::Type type); + void GenerateMinMaxFP(LocationSummary* locations, bool is_min, DataType::Type type); + void GenerateMinMax(HBinaryOperation* minmax, bool is_min); void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info, @@ -312,7 +316,6 @@ class JumpTableRIPFixup; class CodeGeneratorX86 : public CodeGenerator { public: CodeGeneratorX86(HGraph* graph, - const X86InstructionSetFeatures& isa_features, const CompilerOptions& compiler_options, OptimizingCompilerStats* stats = nullptr); virtual ~CodeGeneratorX86() {} @@ -386,6 +389,8 @@ class CodeGeneratorX86 : public CodeGenerator { return InstructionSet::kX86; } + const X86InstructionSetFeatures& GetInstructionSetFeatures() const; + // Helper method to move a 32bits value between two locations. void Move32(Location destination, Location source); // Helper method to move a 64bits value between two locations. @@ -414,12 +419,22 @@ class CodeGeneratorX86 : public CodeGenerator { void GenerateVirtualCall( HInvokeVirtual* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE; + void RecordBootImageIntrinsicPatch(HX86ComputeBaseMethodAddress* method_address, + uint32_t intrinsic_data); + void RecordBootImageRelRoPatch(HX86ComputeBaseMethodAddress* method_address, + uint32_t boot_image_offset); void RecordBootImageMethodPatch(HInvokeStaticOrDirect* invoke); void RecordMethodBssEntryPatch(HInvokeStaticOrDirect* invoke); void RecordBootImageTypePatch(HLoadClass* load_class); Label* NewTypeBssEntryPatch(HLoadClass* load_class); void RecordBootImageStringPatch(HLoadString* load_string); Label* NewStringBssEntryPatch(HLoadString* load_string); + + void LoadBootImageAddress(Register reg, + uint32_t boot_image_reference, + HInvokeStaticOrDirect* invoke); + void AllocateInstanceForIntrinsic(HInvokeStaticOrDirect* invoke, uint32_t boot_image_offset); + Label* NewJitRootStringPatch(const DexFile& dex_file, dex::StringIndex string_index, Handle handle); @@ -463,10 +478,6 @@ class CodeGeneratorX86 : public CodeGenerator { Label* GetFrameEntryLabel() { return &frame_entry_label_; } - const X86InstructionSetFeatures& GetInstructionSetFeatures() const { - return isa_features_; - } - void AddMethodAddressOffset(HX86ComputeBaseMethodAddress* method_base, int32_t offset) { method_address_offset_.Put(method_base->GetId(), offset); } @@ -629,20 +640,22 @@ class CodeGeneratorX86 : public CodeGenerator { InstructionCodeGeneratorX86 instruction_visitor_; ParallelMoveResolverX86 move_resolver_; X86Assembler assembler_; - const X86InstructionSetFeatures& isa_features_; - // PC-relative method patch info for kBootImageLinkTimePcRelative. + // PC-relative method patch info for kBootImageLinkTimePcRelative/kBootImageRelRo. + // Also used for type/string patches for kBootImageRelRo (same linker patch as for methods). ArenaDeque boot_image_method_patches_; // PC-relative method patch info for kBssEntry. ArenaDeque method_bss_entry_patches_; // PC-relative type patch info for kBootImageLinkTimePcRelative. ArenaDeque boot_image_type_patches_; - // Type patch locations for kBssEntry. + // PC-relative type patch info for kBssEntry. ArenaDeque type_bss_entry_patches_; - // String patch locations; type depends on configuration (intern table or boot image PIC). + // PC-relative String patch info for kBootImageLinkTimePcRelative. ArenaDeque boot_image_string_patches_; - // String patch locations for kBssEntry. + // PC-relative String patch info for kBssEntry. ArenaDeque string_bss_entry_patches_; + // PC-relative patch info for IntrinsicObjects. + ArenaDeque boot_image_intrinsic_patches_; // Patches for string root accesses in JIT compiled code. ArenaDeque> jit_string_patches_; diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 7be360536b287a3934e401507133db2c59c79aae..3073be6ca7303571b8691e6bc65e55c6b3fff898 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -22,6 +22,7 @@ #include "compiled_method.h" #include "entrypoints/quick/quick_entrypoints.h" #include "gc/accounting/card_table.h" +#include "gc/space/image_space.h" #include "heap_poisoning.h" #include "intrinsics.h" #include "intrinsics_x86_64.h" @@ -998,6 +999,13 @@ void CodeGeneratorX86_64::GenerateStaticOrDirectCall( case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: Load64BitValue(temp.AsRegister(), invoke->GetMethodAddress()); break; + case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: { + // Note: Boot image is in the low 4GiB and the entry is 32-bit, so emit a 32-bit load. + __ movl(temp.AsRegister(), + Address::Absolute(kDummy32BitOffset, /* no_rip */ false)); + RecordBootImageRelRoPatch(GetBootImageOffset(invoke)); + break; + } case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: { __ movq(temp.AsRegister(), Address::Absolute(kDummy32BitOffset, /* no_rip */ false)); @@ -1059,6 +1067,16 @@ void CodeGeneratorX86_64::GenerateVirtualCall( RecordPcInfo(invoke, invoke->GetDexPc(), slow_path); } +void CodeGeneratorX86_64::RecordBootImageIntrinsicPatch(uint32_t intrinsic_data) { + boot_image_intrinsic_patches_.emplace_back(/* target_dex_file */ nullptr, intrinsic_data); + __ Bind(&boot_image_intrinsic_patches_.back().label); +} + +void CodeGeneratorX86_64::RecordBootImageRelRoPatch(uint32_t boot_image_offset) { + boot_image_method_patches_.emplace_back(/* target_dex_file */ nullptr, boot_image_offset); + __ Bind(&boot_image_method_patches_.back().label); +} + void CodeGeneratorX86_64::RecordBootImageMethodPatch(HInvokeStaticOrDirect* invoke) { boot_image_method_patches_.emplace_back( invoke->GetTargetMethod().dex_file, invoke->GetTargetMethod().index); @@ -1095,6 +1113,43 @@ Label* CodeGeneratorX86_64::NewStringBssEntryPatch(HLoadString* load_string) { return &string_bss_entry_patches_.back().label; } +void CodeGeneratorX86_64::LoadBootImageAddress(CpuRegister reg, uint32_t boot_image_reference) { + if (GetCompilerOptions().IsBootImage()) { + __ leal(reg, Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false)); + RecordBootImageIntrinsicPatch(boot_image_reference); + } else if (GetCompilerOptions().GetCompilePic()) { + DCHECK(Runtime::Current()->IsAotCompiler()); + __ movl(reg, Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false)); + RecordBootImageRelRoPatch(boot_image_reference); + } else { + gc::Heap* heap = Runtime::Current()->GetHeap(); + DCHECK(!heap->GetBootImageSpaces().empty()); + const uint8_t* address = heap->GetBootImageSpaces()[0]->Begin() + boot_image_reference; + __ movl(reg, Immediate(dchecked_integral_cast(reinterpret_cast(address)))); + } +} + +void CodeGeneratorX86_64::AllocateInstanceForIntrinsic(HInvokeStaticOrDirect* invoke, + uint32_t boot_image_offset) { + DCHECK(invoke->IsStatic()); + InvokeRuntimeCallingConvention calling_convention; + CpuRegister argument = CpuRegister(calling_convention.GetRegisterAt(0)); + if (GetCompilerOptions().IsBootImage()) { + DCHECK_EQ(boot_image_offset, IntrinsicVisitor::IntegerValueOfInfo::kInvalidReference); + // Load the class the same way as for HLoadClass::LoadKind::kBootImageLinkTimePcRelative. + __ leal(argument, + Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false)); + MethodReference target_method = invoke->GetTargetMethod(); + dex::TypeIndex type_idx = target_method.dex_file->GetMethodId(target_method.index).class_idx_; + boot_image_type_patches_.emplace_back(target_method.dex_file, type_idx.index_); + __ Bind(&boot_image_type_patches_.back().label); + } else { + LoadBootImageAddress(argument, boot_image_offset); + } + InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc()); + CheckEntrypointTypes(); +} + // The label points to the end of the "movl" or another instruction but the literal offset // for method patch needs to point to the embedded constant which occupies the last 4 bytes. constexpr uint32_t kLabelPositionToLiteralOffsetAdjustment = 4u; @@ -1110,6 +1165,15 @@ inline void CodeGeneratorX86_64::EmitPcRelativeLinkerPatches( } } +template +linker::LinkerPatch NoDexFileAdapter(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t boot_image_offset) { + DCHECK(target_dex_file == nullptr); // Unused for these patches, should be null. + return Factory(literal_offset, pc_insn_offset, boot_image_offset); +} + void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector* linker_patches) { DCHECK(linker_patches->empty()); size_t size = @@ -1118,7 +1182,8 @@ void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector* li boot_image_type_patches_.size() + type_bss_entry_patches_.size() + boot_image_string_patches_.size() + - string_bss_entry_patches_.size(); + string_bss_entry_patches_.size() + + boot_image_intrinsic_patches_.size(); linker_patches->reserve(size); if (GetCompilerOptions().IsBootImage()) { EmitPcRelativeLinkerPatches( @@ -1127,12 +1192,14 @@ void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector* li boot_image_type_patches_, linker_patches); EmitPcRelativeLinkerPatches( boot_image_string_patches_, linker_patches); + EmitPcRelativeLinkerPatches>( + boot_image_intrinsic_patches_, linker_patches); } else { - DCHECK(boot_image_method_patches_.empty()); - EmitPcRelativeLinkerPatches( - boot_image_type_patches_, linker_patches); - EmitPcRelativeLinkerPatches( - boot_image_string_patches_, linker_patches); + EmitPcRelativeLinkerPatches>( + boot_image_method_patches_, linker_patches); + DCHECK(boot_image_type_patches_.empty()); + DCHECK(boot_image_string_patches_.empty()); + DCHECK(boot_image_intrinsic_patches_.empty()); } EmitPcRelativeLinkerPatches( method_bss_entry_patches_, linker_patches); @@ -1151,6 +1218,10 @@ void CodeGeneratorX86_64::DumpFloatingPointRegister(std::ostream& stream, int re stream << FloatRegister(reg); } +const X86_64InstructionSetFeatures& CodeGeneratorX86_64::GetInstructionSetFeatures() const { + return *GetCompilerOptions().GetInstructionSetFeatures()->AsX86_64InstructionSetFeatures(); +} + size_t CodeGeneratorX86_64::SaveCoreRegister(size_t stack_index, uint32_t reg_id) { __ movq(Address(CpuRegister(RSP), stack_index), CpuRegister(reg_id)); return kX86_64WordSize; @@ -1205,7 +1276,6 @@ static constexpr int kNumberOfCpuRegisterPairs = 0; // Use a fake return address register to mimic Quick. static constexpr Register kFakeReturnRegister = Register(kLastCpuRegister + 1); CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph, - const X86_64InstructionSetFeatures& isa_features, const CompilerOptions& compiler_options, OptimizingCompilerStats* stats) : CodeGenerator(graph, @@ -1224,7 +1294,6 @@ CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph, instruction_visitor_(graph, this), move_resolver_(graph->GetAllocator(), this), assembler_(graph->GetAllocator()), - isa_features_(isa_features), constant_area_start_(0), boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), @@ -1232,6 +1301,7 @@ CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph, type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), + boot_image_intrinsic_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), jit_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), jit_class_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), fixups_to_jump_tables_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)) { @@ -2482,6 +2552,14 @@ void InstructionCodeGeneratorX86_64::VisitInvokePolymorphic(HInvokePolymorphic* 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); @@ -3821,6 +3899,241 @@ void InstructionCodeGeneratorX86_64::VisitRem(HRem* rem) { } } +static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) { + LocationSummary* locations = new (allocator) LocationSummary(minmax); + switch (minmax->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + // The following is sub-optimal, but all we can do for now. It would be fine to also accept + // the second input to be the output (we can simply swap inputs). + locations->SetOut(Location::SameAsFirstInput()); + break; + default: + LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType(); + } +} + +void InstructionCodeGeneratorX86_64::GenerateMinMaxInt(LocationSummary* locations, + bool is_min, + DataType::Type type) { + Location op1_loc = locations->InAt(0); + Location op2_loc = locations->InAt(1); + + // Shortcut for same input locations. + if (op1_loc.Equals(op2_loc)) { + // Can return immediately, as op1_loc == out_loc. + // Note: if we ever support separate registers, e.g., output into memory, we need to check for + // a copy here. + DCHECK(locations->Out().Equals(op1_loc)); + return; + } + + CpuRegister out = locations->Out().AsRegister(); + CpuRegister op2 = op2_loc.AsRegister(); + + // (out := op1) + // out <=? op2 + // if out is min jmp done + // out := op2 + // done: + + if (type == DataType::Type::kInt64) { + __ cmpq(out, op2); + __ cmov(is_min ? Condition::kGreater : Condition::kLess, out, op2, /*is64bit*/ true); + } else { + DCHECK_EQ(type, DataType::Type::kInt32); + __ cmpl(out, op2); + __ cmov(is_min ? Condition::kGreater : Condition::kLess, out, op2, /*is64bit*/ false); + } +} + +void InstructionCodeGeneratorX86_64::GenerateMinMaxFP(LocationSummary* locations, + bool is_min, + DataType::Type type) { + Location op1_loc = locations->InAt(0); + Location op2_loc = locations->InAt(1); + Location out_loc = locations->Out(); + XmmRegister out = out_loc.AsFpuRegister(); + + // Shortcut for same input locations. + if (op1_loc.Equals(op2_loc)) { + DCHECK(out_loc.Equals(op1_loc)); + return; + } + + // (out := op1) + // out <=? op2 + // if Nan jmp Nan_label + // if out is min jmp done + // if op2 is min jmp op2_label + // handle -0/+0 + // jmp done + // Nan_label: + // out := NaN + // op2_label: + // out := op2 + // done: + // + // This removes one jmp, but needs to copy one input (op1) to out. + // + // TODO: This is straight from Quick. Make NaN an out-of-line slowpath? + + XmmRegister op2 = op2_loc.AsFpuRegister(); + + NearLabel nan, done, op2_label; + if (type == DataType::Type::kFloat64) { + __ ucomisd(out, op2); + } else { + DCHECK_EQ(type, DataType::Type::kFloat32); + __ ucomiss(out, op2); + } + + __ j(Condition::kParityEven, &nan); + + __ j(is_min ? Condition::kAbove : Condition::kBelow, &op2_label); + __ j(is_min ? Condition::kBelow : Condition::kAbove, &done); + + // Handle 0.0/-0.0. + if (is_min) { + if (type == DataType::Type::kFloat64) { + __ orpd(out, op2); + } else { + __ orps(out, op2); + } + } else { + if (type == DataType::Type::kFloat64) { + __ andpd(out, op2); + } else { + __ andps(out, op2); + } + } + __ jmp(&done); + + // NaN handling. + __ Bind(&nan); + if (type == DataType::Type::kFloat64) { + __ movsd(out, codegen_->LiteralInt64Address(INT64_C(0x7FF8000000000000))); + } else { + __ movss(out, codegen_->LiteralInt32Address(INT32_C(0x7FC00000))); + } + __ jmp(&done); + + // out := op2; + __ Bind(&op2_label); + if (type == DataType::Type::kFloat64) { + __ movsd(out, op2); + } else { + __ movss(out, op2); + } + + // Done. + __ Bind(&done); +} + +void InstructionCodeGeneratorX86_64::GenerateMinMax(HBinaryOperation* minmax, bool is_min) { + DataType::Type type = minmax->GetResultType(); + switch (type) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + GenerateMinMaxInt(minmax->GetLocations(), is_min, type); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + GenerateMinMaxFP(minmax->GetLocations(), is_min, type); + break; + default: + LOG(FATAL) << "Unexpected type for HMinMax " << type; + } +} + +void LocationsBuilderX86_64::VisitMin(HMin* min) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), min); +} + +void InstructionCodeGeneratorX86_64::VisitMin(HMin* min) { + GenerateMinMax(min, /*is_min*/ true); +} + +void LocationsBuilderX86_64::VisitMax(HMax* max) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), max); +} + +void InstructionCodeGeneratorX86_64::VisitMax(HMax* max) { + GenerateMinMax(max, /*is_min*/ false); +} + +void LocationsBuilderX86_64::VisitAbs(HAbs* abs) { + LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + locations->AddTemp(Location::RequiresRegister()); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); + locations->AddTemp(Location::RequiresFpuRegister()); + break; + default: + LOG(FATAL) << "Unexpected type for HAbs " << abs->GetResultType(); + } +} + +void InstructionCodeGeneratorX86_64::VisitAbs(HAbs* abs) { + LocationSummary* locations = abs->GetLocations(); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: { + CpuRegister out = locations->Out().AsRegister(); + CpuRegister mask = locations->GetTemp(0).AsRegister(); + // Create mask. + __ movl(mask, out); + __ sarl(mask, Immediate(31)); + // Add mask. + __ addl(out, mask); + __ xorl(out, mask); + break; + } + case DataType::Type::kInt64: { + CpuRegister out = locations->Out().AsRegister(); + CpuRegister mask = locations->GetTemp(0).AsRegister(); + // Create mask. + __ movq(mask, out); + __ sarq(mask, Immediate(63)); + // Add mask. + __ addq(out, mask); + __ xorq(out, mask); + break; + } + case DataType::Type::kFloat32: { + XmmRegister out = locations->Out().AsFpuRegister(); + XmmRegister mask = locations->GetTemp(0).AsFpuRegister(); + __ movss(mask, codegen_->LiteralInt32Address(INT32_C(0x7FFFFFFF))); + __ andps(out, mask); + break; + } + case DataType::Type::kFloat64: { + XmmRegister out = locations->Out().AsFpuRegister(); + XmmRegister mask = locations->GetTemp(0).AsFpuRegister(); + __ movsd(mask, codegen_->LiteralInt64Address(INT64_C(0x7FFFFFFFFFFFFFFF))); + __ andpd(out, mask); + break; + } + default: + LOG(FATAL) << "Unexpected type for HAbs " << abs->GetResultType(); + } +} + void LocationsBuilderX86_64::VisitDivZeroCheck(HDivZeroCheck* instruction) { LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction); locations->SetInAt(0, Location::Any()); @@ -4030,29 +4343,14 @@ void LocationsBuilderX86_64::VisitNewInstance(HNewInstance* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary( instruction, LocationSummary::kCallOnMainOnly); InvokeRuntimeCallingConvention calling_convention; - if (instruction->IsStringAlloc()) { - locations->AddTemp(Location::RegisterLocation(kMethodRegisterArgument)); - } else { - locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); - } + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); locations->SetOut(Location::RegisterLocation(RAX)); } void InstructionCodeGeneratorX86_64::VisitNewInstance(HNewInstance* instruction) { - // Note: if heap poisoning is enabled, the entry point takes cares - // of poisoning the reference. - if (instruction->IsStringAlloc()) { - // String is allocated through StringFactory. Call NewEmptyString entry point. - CpuRegister temp = instruction->GetLocations()->GetTemp(0).AsRegister(); - MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86_64PointerSize); - __ gs()->movq(temp, Address::Absolute(QUICK_ENTRY_POINT(pNewEmptyString), /* no_rip */ true)); - __ call(Address(temp, code_offset.SizeValue())); - codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); - } else { - codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); - CheckEntrypointTypes(); - DCHECK(!codegen_->IsLeafMethod()); - } + codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); + CheckEntrypointTypes(); + DCHECK(!codegen_->IsLeafMethod()); } void LocationsBuilderX86_64::VisitNewArray(HNewArray* instruction) { @@ -5462,6 +5760,26 @@ void InstructionCodeGeneratorX86_64::GenerateClassInitializationCheck( // No need for memory fence, thanks to the x86-64 memory model. } +void InstructionCodeGeneratorX86_64::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, + CpuRegister temp) { + uint32_t path_to_root = check->GetBitstringPathToRoot(); + uint32_t mask = check->GetBitstringMask(); + DCHECK(IsPowerOfTwo(mask + 1)); + size_t mask_bits = WhichPowerOf2(mask + 1); + + if (mask_bits == 16u) { + // Compare the bitstring in memory. + __ cmpw(Address(temp, mirror::Class::StatusOffset()), Immediate(path_to_root)); + } else { + // /* uint32_t */ temp = temp->status_ + __ movl(temp, Address(temp, mirror::Class::StatusOffset())); + // Compare the bitstring bits using SUB. + __ subl(temp, Immediate(path_to_root)); + // Shift out bits that do not contribute to the comparison. + __ shll(temp, Immediate(32u - mask_bits)); + } +} + HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind( HLoadClass::LoadKind desired_class_load_kind) { switch (desired_class_load_kind) { @@ -5471,7 +5789,7 @@ HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind( case HLoadClass::LoadKind::kReferrersClass: break; case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: - case HLoadClass::LoadKind::kBootImageClassTable: + case HLoadClass::LoadKind::kBootImageRelRo: case HLoadClass::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -5579,16 +5897,10 @@ void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S __ movl(out, Immediate(static_cast(address))); // Zero-extended. break; } - case HLoadClass::LoadKind::kBootImageClassTable: { + case HLoadClass::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); __ movl(out, Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false)); - codegen_->RecordBootImageTypePatch(cls); - // Extract the reference from the slot data, i.e. clear the hash bits. - int32_t masked_hash = ClassTable::TableSlot::MaskHash( - ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex()))); - if (masked_hash != 0) { - __ subl(out, Immediate(masked_hash)); - } + codegen_->RecordBootImageRelRoPatch(codegen_->GetBootImageOffset(cls)); break; } case HLoadClass::LoadKind::kBssEntry: { @@ -5640,6 +5952,26 @@ void LocationsBuilderX86_64::VisitClinitCheck(HClinitCheck* check) { } } +void LocationsBuilderX86_64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + // Custom calling convention: RAX serves as both input and output. + Location location = Location::RegisterLocation(RAX); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorX86_64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + +void LocationsBuilderX86_64::VisitLoadMethodType(HLoadMethodType* load) { + // Custom calling convention: RAX serves as both input and output. + Location location = Location::RegisterLocation(RAX); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorX86_64::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + void InstructionCodeGeneratorX86_64::VisitClinitCheck(HClinitCheck* check) { // We assume the class to not be null. SlowPathCode* slow_path = new (codegen_->GetScopedAllocator()) LoadClassSlowPathX86_64( @@ -5653,7 +5985,7 @@ HLoadString::LoadKind CodeGeneratorX86_64::GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) { switch (desired_string_load_kind) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: - case HLoadString::LoadKind::kBootImageInternTable: + case HLoadString::LoadKind::kBootImageRelRo: case HLoadString::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -5719,10 +6051,10 @@ void InstructionCodeGeneratorX86_64::VisitLoadString(HLoadString* load) NO_THREA __ movl(out, Immediate(static_cast(address))); // Zero-extended. return; } - case HLoadString::LoadKind::kBootImageInternTable: { + case HLoadString::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); __ movl(out, Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false)); - codegen_->RecordBootImageStringPatch(load); + codegen_->RecordBootImageRelRoPatch(codegen_->GetBootImageOffset(load)); return; } case HLoadString::LoadKind::kBssEntry: { @@ -5795,24 +6127,26 @@ void InstructionCodeGeneratorX86_64::VisitThrow(HThrow* instruction) { CheckEntrypointTypes(); } -static bool CheckCastTypeCheckNeedsATemporary(TypeCheckKind type_check_kind) { - if (type_check_kind == TypeCheckKind::kInterfaceCheck) { - // We need a temporary for holding the iftable length. - return true; - } - return kEmitCompilerReadBarrier && +// Temp is used for read barrier. +static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) { + if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier && (type_check_kind == TypeCheckKind::kAbstractClassCheck || type_check_kind == TypeCheckKind::kClassHierarchyCheck || - type_check_kind == TypeCheckKind::kArrayObjectCheck); + type_check_kind == TypeCheckKind::kArrayObjectCheck)) { + return 1; + } + return 0; } -static bool InstanceOfTypeCheckNeedsATemporary(TypeCheckKind type_check_kind) { - return kEmitCompilerReadBarrier && - !kUseBakerReadBarrier && - (type_check_kind == TypeCheckKind::kAbstractClassCheck || - type_check_kind == TypeCheckKind::kClassHierarchyCheck || - type_check_kind == TypeCheckKind::kArrayObjectCheck); +// Interface case has 2 temps, one for holding the number of interfaces, one for the current +// interface pointer, the current interface is compared in memory. +// The other checks have one temp for loading the object's class. +static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) { + if (type_check_kind == TypeCheckKind::kInterfaceCheck) { + return 2; + } + return 1 + NumberOfInstanceOfTemps(type_check_kind); } void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) { @@ -5834,6 +6168,8 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; + case TypeCheckKind::kBitstringCheck: + break; } LocationSummary* locations = @@ -5842,14 +6178,16 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::Any()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::Any()); + } // Note that TypeCheckSlowPathX86_64 uses this "out" register too. locations->SetOut(Location::RequiresRegister()); - // When read barriers are enabled, we need a temporary register for - // some cases. - if (InstanceOfTypeCheckNeedsATemporary(type_check_kind)) { - locations->AddTemp(Location::RequiresRegister()); - } + locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind)); } void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { @@ -5860,9 +6198,9 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { Location cls = locations->InAt(1); Location out_loc = locations->Out(); CpuRegister out = out_loc.AsRegister(); - Location maybe_temp_loc = InstanceOfTypeCheckNeedsATemporary(type_check_kind) ? - locations->GetTemp(0) : - Location::NoLocation(); + const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); + DCHECK_LE(num_temps, 1u); + Location maybe_temp_loc = (num_temps >= 1u) ? locations->GetTemp(0) : Location::NoLocation(); uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); @@ -6071,6 +6409,27 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { } break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, out); + if (zero.IsLinked()) { + __ j(kNotEqual, &zero); + __ movl(out, Immediate(1)); + __ jmp(&done); + } else { + __ setcc(kEqual, out); + // setcc only sets the low byte. + __ andl(out, Immediate(1)); + } + break; + } } if (zero.IsLinked()) { @@ -6097,17 +6456,15 @@ void LocationsBuilderX86_64::VisitCheckCast(HCheckCast* instruction) { // Require a register for the interface check since there is a loop that compares the class to // a memory address. locations->SetInAt(1, Location::RequiresRegister()); + } else if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); } else { locations->SetInAt(1, Location::Any()); } - - // Note that TypeCheckSlowPathX86_64 uses this "temp" register too. - locations->AddTemp(Location::RequiresRegister()); - // When read barriers are enabled, we need an additional temporary - // register for some cases. - if (CheckCastTypeCheckNeedsATemporary(type_check_kind)) { - locations->AddTemp(Location::RequiresRegister()); - } + // Add temps for read barriers and other uses. One is used by TypeCheckSlowPathX86. + locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { @@ -6118,9 +6475,10 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { Location cls = locations->InAt(1); Location temp_loc = locations->GetTemp(0); CpuRegister temp = temp_loc.AsRegister(); - Location maybe_temp2_loc = CheckCastTypeCheckNeedsATemporary(type_check_kind) ? - locations->GetTemp(1) : - Location::NoLocation(); + const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); + DCHECK_GE(num_temps, 1u); + DCHECK_LE(num_temps, 2u); + Location maybe_temp2_loc = (num_temps >= 2u) ? locations->GetTemp(1) : Location::NoLocation(); const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); @@ -6283,7 +6641,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { break; } - case TypeCheckKind::kInterfaceCheck: + case TypeCheckKind::kInterfaceCheck: { // Fast path for the interface check. Try to avoid read barriers to improve the fast path. // We can not get false positives by doing this. // /* HeapReference */ temp = obj->klass_ @@ -6319,6 +6677,20 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { // If `cls` was poisoned above, unpoison it. __ MaybeUnpoisonHeapReference(cls.AsRegister()); break; + } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, temp); + __ j(kNotEqual, type_check_slow_path->GetEntryLabel()); + break; + } } if (done.IsLinked()) { diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 1079e94dfc2b8bb51f6beb469d96f780c1240ed0..5ba7f9cb714edbe2b4fd4d9787ac1b90e0316df7 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -208,6 +208,7 @@ class InstructionCodeGeneratorX86_64 : public InstructionCodeGenerator { // the suspend call. void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor); void GenerateClassInitializationCheck(SlowPathCode* slow_path, CpuRegister class_reg); + void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, CpuRegister temp); void HandleBitwiseOperation(HBinaryOperation* operation); void GenerateRemFP(HRem* rem); void DivRemOneOrMinusOne(HBinaryOperation* instruction); @@ -222,6 +223,10 @@ class InstructionCodeGeneratorX86_64 : public InstructionCodeGenerator { bool value_can_be_null); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); + void GenerateMinMaxInt(LocationSummary* locations, bool is_min, DataType::Type type); + void GenerateMinMaxFP(LocationSummary* locations, bool is_min, DataType::Type type); + void GenerateMinMax(HBinaryOperation* minmax, bool is_min); + // Generate a heap reference load using one register `out`: // // out <- *(out + offset) @@ -291,7 +296,6 @@ class JumpTableRIPFixup; class CodeGeneratorX86_64 : public CodeGenerator { public: CodeGeneratorX86_64(HGraph* graph, - const X86_64InstructionSetFeatures& isa_features, const CompilerOptions& compiler_options, OptimizingCompilerStats* stats = nullptr); virtual ~CodeGeneratorX86_64() {} @@ -365,6 +369,8 @@ class CodeGeneratorX86_64 : public CodeGenerator { return InstructionSet::kX86_64; } + const X86_64InstructionSetFeatures& GetInstructionSetFeatures() const; + // Emit a write barrier. void MarkGCCard(CpuRegister temp, CpuRegister card, @@ -410,6 +416,8 @@ class CodeGeneratorX86_64 : public CodeGenerator { void GenerateVirtualCall( HInvokeVirtual* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE; + void RecordBootImageIntrinsicPatch(uint32_t intrinsic_data); + void RecordBootImageRelRoPatch(uint32_t boot_image_offset); void RecordBootImageMethodPatch(HInvokeStaticOrDirect* invoke); void RecordMethodBssEntryPatch(HInvokeStaticOrDirect* invoke); void RecordBootImageTypePatch(HLoadClass* load_class); @@ -423,7 +431,8 @@ class CodeGeneratorX86_64 : public CodeGenerator { dex::TypeIndex type_index, Handle handle); - void MoveFromReturnRegister(Location trg, DataType::Type type) OVERRIDE; + void LoadBootImageAddress(CpuRegister reg, uint32_t boot_image_reference); + void AllocateInstanceForIntrinsic(HInvokeStaticOrDirect* invoke, uint32_t boot_image_offset); void EmitLinkerPatches(ArenaVector* linker_patches) OVERRIDE; @@ -434,10 +443,6 @@ class CodeGeneratorX86_64 : public CodeGenerator { void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; - const X86_64InstructionSetFeatures& GetInstructionSetFeatures() const { - return isa_features_; - } - // Fast path implementation of ReadBarrier::Barrier for a heap // reference field load when Baker's read barriers are used. void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, @@ -560,6 +565,8 @@ class CodeGeneratorX86_64 : public CodeGenerator { // Store a 64 bit value into a DoubleStackSlot in the most efficient manner. void Store64BitValueToStack(Location dest, int64_t value); + void MoveFromReturnRegister(Location trg, DataType::Type type) OVERRIDE; + // Assign a 64 bit constant to an address. void MoveInt64ToAddress(const Address& addr_low, const Address& addr_high, @@ -598,24 +605,26 @@ class CodeGeneratorX86_64 : public CodeGenerator { InstructionCodeGeneratorX86_64 instruction_visitor_; ParallelMoveResolverX86_64 move_resolver_; X86_64Assembler assembler_; - const X86_64InstructionSetFeatures& isa_features_; // Offset to the start of the constant area in the assembled code. // Used for fixups to the constant area. int constant_area_start_; - // PC-relative method patch info for kBootImageLinkTimePcRelative. + // PC-relative method patch info for kBootImageLinkTimePcRelative/kBootImageRelRo. + // Also used for type/string patches for kBootImageRelRo (same linker patch as for methods). ArenaDeque> boot_image_method_patches_; // PC-relative method patch info for kBssEntry. ArenaDeque> method_bss_entry_patches_; // PC-relative type patch info for kBootImageLinkTimePcRelative. ArenaDeque> boot_image_type_patches_; - // Type patch locations for kBssEntry. + // PC-relative type patch info for kBssEntry. ArenaDeque> type_bss_entry_patches_; - // String patch locations; type depends on configuration (intern table or boot image PIC). + // PC-relative String patch info for kBootImageLinkTimePcRelative. ArenaDeque> boot_image_string_patches_; - // String patch locations for kBssEntry. + // PC-relative String patch info for kBssEntry. ArenaDeque> string_bss_entry_patches_; + // PC-relative patch info for IntrinsicObjects. + ArenaDeque> boot_image_intrinsic_patches_; // Patches for string literals in JIT compiled code. ArenaDeque> jit_string_patches_; diff --git a/compiler/optimizing/code_sinking.cc b/compiler/optimizing/code_sinking.cc index 2e31d35584db9ad6c75fc9982f33fac1b4a52cf8..d6c97552dc7bff14a8267ae73d2c3fea084f900d 100644 --- a/compiler/optimizing/code_sinking.cc +++ b/compiler/optimizing/code_sinking.cc @@ -25,11 +25,11 @@ namespace art { -void CodeSinking::Run() { +bool CodeSinking::Run() { HBasicBlock* exit = graph_->GetExitBlock(); if (exit == nullptr) { // Infinite loop, just bail. - return; + return false; } // TODO(ngeoffray): we do not profile branches yet, so use throw instructions // as an indicator of an uncommon branch. @@ -40,6 +40,7 @@ void CodeSinking::Run() { SinkCodeToUncommonBranch(exit_predecessor); } } + return true; } static bool IsInterestingInstruction(HInstruction* instruction) { diff --git a/compiler/optimizing/code_sinking.h b/compiler/optimizing/code_sinking.h index 836d9d4f67c05f80ab8dbfcc8da76a2de2d439f7..5db0b6dcc55347c02b095002158e18a43ff34f85 100644 --- a/compiler/optimizing/code_sinking.h +++ b/compiler/optimizing/code_sinking.h @@ -33,7 +33,7 @@ class CodeSinking : public HOptimization { const char* name = kCodeSinkingPassName) : HOptimization(graph, name, stats) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kCodeSinkingPassName = "code_sinking"; diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc index a0fd5ffcb177a9c54e1004a0ee2f5472e6a94b99..86687e60a99ef77776a14e694d944f049b1c4d4d 100644 --- a/compiler/optimizing/codegen_test.cc +++ b/compiler/optimizing/codegen_test.cc @@ -89,7 +89,8 @@ void CodegenTest::TestCode(const std::vector& data, bool has_result, i HGraph* graph = CreateCFG(data); // Remove suspend checks, they cannot be executed in this context. RemoveSuspendChecks(graph); - RunCode(target_config, graph, [](HGraph*) {}, has_result, expected); + OverrideInstructionSetFeatures(target_config.GetInstructionSet(), "default"); + RunCode(target_config, *compiler_options_, graph, [](HGraph*) {}, has_result, expected); } } @@ -100,7 +101,8 @@ void CodegenTest::TestCodeLong(const std::vector& data, HGraph* graph = CreateCFG(data, DataType::Type::kInt64); // Remove suspend checks, they cannot be executed in this context. RemoveSuspendChecks(graph); - RunCode(target_config, graph, [](HGraph*) {}, has_result, expected); + OverrideInstructionSetFeatures(target_config.GetInstructionSet(), "default"); + RunCode(target_config, *compiler_options_, graph, [](HGraph*) {}, has_result, expected); } } @@ -460,7 +462,8 @@ TEST_F(CodegenTest, NonMaterializedCondition) { block->InsertInstructionBefore(move, block->GetLastInstruction()); }; - RunCode(target_config, graph, hook_before_codegen, true, 0); + OverrideInstructionSetFeatures(target_config.GetInstructionSet(), "default"); + RunCode(target_config, *compiler_options_, graph, hook_before_codegen, true, 0); } } @@ -506,7 +509,8 @@ TEST_F(CodegenTest, MaterializedCondition1) { new (graph_in->GetAllocator()) HParallelMove(graph_in->GetAllocator()); block->InsertInstructionBefore(move, block->GetLastInstruction()); }; - RunCode(target_config, graph, hook_before_codegen, true, lhs[i] < rhs[i]); + OverrideInstructionSetFeatures(target_config.GetInstructionSet(), "default"); + RunCode(target_config, *compiler_options_, graph, hook_before_codegen, true, lhs[i] < rhs[i]); } } } @@ -573,7 +577,8 @@ TEST_F(CodegenTest, MaterializedCondition2) { new (graph_in->GetAllocator()) HParallelMove(graph_in->GetAllocator()); block->InsertInstructionBefore(move, block->GetLastInstruction()); }; - RunCode(target_config, graph, hook_before_codegen, true, lhs[i] < rhs[i]); + OverrideInstructionSetFeatures(target_config.GetInstructionSet(), "default"); + RunCode(target_config, *compiler_options_, graph, hook_before_codegen, true, lhs[i] < rhs[i]); } } } @@ -682,7 +687,8 @@ void CodegenTest::TestComparison(IfCondition condition, block->AddInstruction(new (GetAllocator()) HReturn(comparison)); graph->BuildDominatorTree(); - RunCode(target_config, graph, [](HGraph*) {}, true, expected_result); + OverrideInstructionSetFeatures(target_config.GetInstructionSet(), "default"); + RunCode(target_config, *compiler_options_, graph, [](HGraph*) {}, true, expected_result); } TEST_F(CodegenTest, ComparisonsInt) { @@ -713,10 +719,9 @@ TEST_F(CodegenTest, ComparisonsLong) { #ifdef ART_ENABLE_CODEGEN_arm TEST_F(CodegenTest, ARMVIXLParallelMoveResolver) { - std::unique_ptr features( - ArmInstructionSetFeatures::FromCppDefines()); + OverrideInstructionSetFeatures(InstructionSet::kThumb2, "default"); HGraph* graph = CreateGraph(); - arm::CodeGeneratorARMVIXL codegen(graph, *features.get(), CompilerOptions()); + arm::CodeGeneratorARMVIXL codegen(graph, *compiler_options_); codegen.Initialize(); @@ -737,10 +742,9 @@ TEST_F(CodegenTest, ARMVIXLParallelMoveResolver) { #ifdef ART_ENABLE_CODEGEN_arm64 // Regression test for b/34760542. TEST_F(CodegenTest, ARM64ParallelMoveResolverB34760542) { - std::unique_ptr features( - Arm64InstructionSetFeatures::FromCppDefines()); + OverrideInstructionSetFeatures(InstructionSet::kArm64, "default"); HGraph* graph = CreateGraph(); - arm64::CodeGeneratorARM64 codegen(graph, *features.get(), CompilerOptions()); + arm64::CodeGeneratorARM64 codegen(graph, *compiler_options_); codegen.Initialize(); @@ -787,10 +791,9 @@ TEST_F(CodegenTest, ARM64ParallelMoveResolverB34760542) { // Check that ParallelMoveResolver works fine for ARM64 for both cases when SIMD is on and off. TEST_F(CodegenTest, ARM64ParallelMoveResolverSIMD) { - std::unique_ptr features( - Arm64InstructionSetFeatures::FromCppDefines()); + OverrideInstructionSetFeatures(InstructionSet::kArm64, "default"); HGraph* graph = CreateGraph(); - arm64::CodeGeneratorARM64 codegen(graph, *features.get(), CompilerOptions()); + arm64::CodeGeneratorARM64 codegen(graph, *compiler_options_); codegen.Initialize(); @@ -824,9 +827,9 @@ TEST_F(CodegenTest, ARM64ParallelMoveResolverSIMD) { #ifdef ART_ENABLE_CODEGEN_mips TEST_F(CodegenTest, MipsClobberRA) { - std::unique_ptr features_mips( - MipsInstructionSetFeatures::FromCppDefines()); - if (!CanExecute(InstructionSet::kMips) || features_mips->IsR6()) { + OverrideInstructionSetFeatures(InstructionSet::kMips, "mips32r"); + CHECK(!instruction_set_features_->AsMipsInstructionSetFeatures()->IsR6()); + if (!CanExecute(InstructionSet::kMips)) { // HMipsComputeBaseMethodAddress and the NAL instruction behind it // should only be generated on non-R6. return; @@ -860,7 +863,7 @@ TEST_F(CodegenTest, MipsClobberRA) { graph->BuildDominatorTree(); - mips::CodeGeneratorMIPS codegenMIPS(graph, *features_mips.get(), CompilerOptions()); + mips::CodeGeneratorMIPS codegenMIPS(graph, *compiler_options_); // Since there isn't HLoadClass or HLoadString, we need to manually indicate // that RA is clobbered and the method entry code should generate a stack frame // and preserve RA in it. And this is what we're testing here. diff --git a/compiler/optimizing/codegen_test_utils.h b/compiler/optimizing/codegen_test_utils.h index c41c290c8b4e43bc36e339303b2311ce04dd1d2c..91811262de6c79372b67cbb69b0ed7f1a28b059c 100644 --- a/compiler/optimizing/codegen_test_utils.h +++ b/compiler/optimizing/codegen_test_utils.h @@ -17,17 +17,11 @@ #ifndef ART_COMPILER_OPTIMIZING_CODEGEN_TEST_UTILS_H_ #define ART_COMPILER_OPTIMIZING_CODEGEN_TEST_UTILS_H_ -#include "arch/arm/instruction_set_features_arm.h" #include "arch/arm/registers_arm.h" -#include "arch/arm64/instruction_set_features_arm64.h" #include "arch/instruction_set.h" -#include "arch/mips/instruction_set_features_mips.h" #include "arch/mips/registers_mips.h" -#include "arch/mips64/instruction_set_features_mips64.h" #include "arch/mips64/registers_mips64.h" -#include "arch/x86/instruction_set_features_x86.h" #include "arch/x86/registers_x86.h" -#include "arch/x86_64/instruction_set_features_x86_64.h" #include "code_simulator.h" #include "code_simulator_container.h" #include "common_compiler_test.h" @@ -101,10 +95,8 @@ class CodegenTargetConfig { // to just overwrite the code generator. class TestCodeGeneratorARMVIXL : public arm::CodeGeneratorARMVIXL { public: - TestCodeGeneratorARMVIXL(HGraph* graph, - const ArmInstructionSetFeatures& isa_features, - const CompilerOptions& compiler_options) - : arm::CodeGeneratorARMVIXL(graph, isa_features, compiler_options) { + TestCodeGeneratorARMVIXL(HGraph* graph, const CompilerOptions& compiler_options) + : arm::CodeGeneratorARMVIXL(graph, compiler_options) { AddAllocatedRegister(Location::RegisterLocation(arm::R6)); AddAllocatedRegister(Location::RegisterLocation(arm::R7)); } @@ -145,10 +137,8 @@ class TestCodeGeneratorARMVIXL : public arm::CodeGeneratorARMVIXL { // function. class TestCodeGeneratorARM64 : public arm64::CodeGeneratorARM64 { public: - TestCodeGeneratorARM64(HGraph* graph, - const Arm64InstructionSetFeatures& isa_features, - const CompilerOptions& compiler_options) - : arm64::CodeGeneratorARM64(graph, isa_features, compiler_options) {} + TestCodeGeneratorARM64(HGraph* graph, const CompilerOptions& compiler_options) + : arm64::CodeGeneratorARM64(graph, compiler_options) {} void MaybeGenerateMarkingRegisterCheck(int codem ATTRIBUTE_UNUSED, Location temp_loc ATTRIBUTE_UNUSED) OVERRIDE { @@ -165,10 +155,8 @@ class TestCodeGeneratorARM64 : public arm64::CodeGeneratorARM64 { #ifdef ART_ENABLE_CODEGEN_x86 class TestCodeGeneratorX86 : public x86::CodeGeneratorX86 { public: - TestCodeGeneratorX86(HGraph* graph, - const X86InstructionSetFeatures& isa_features, - const CompilerOptions& compiler_options) - : x86::CodeGeneratorX86(graph, isa_features, compiler_options) { + TestCodeGeneratorX86(HGraph* graph, const CompilerOptions& compiler_options) + : x86::CodeGeneratorX86(graph, compiler_options) { // Save edi, we need it for getting enough registers for long multiplication. AddAllocatedRegister(Location::RegisterLocation(x86::EDI)); } @@ -195,7 +183,9 @@ class InternalCodeAllocator : public CodeAllocator { } size_t GetSize() const { return size_; } - uint8_t* GetMemory() const { return memory_.get(); } + ArrayRef GetMemory() const OVERRIDE { + return ArrayRef(memory_.get(), size_); + } private: size_t size_; @@ -269,8 +259,8 @@ static void Run(const InternalCodeAllocator& allocator, InstructionSet target_isa = codegen.GetInstructionSet(); typedef Expected (*fptr)(); - CommonCompilerTest::MakeExecutable(allocator.GetMemory(), allocator.GetSize()); - fptr f = reinterpret_cast(allocator.GetMemory()); + CommonCompilerTest::MakeExecutable(allocator.GetMemory().data(), allocator.GetMemory().size()); + fptr f = reinterpret_cast(reinterpret_cast(allocator.GetMemory().data())); if (target_isa == InstructionSet::kThumb2) { // For thumb we need the bottom bit set. f = reinterpret_cast(reinterpret_cast(f) + 1); @@ -322,11 +312,11 @@ static void RunCode(CodeGenerator* codegen, template static void RunCode(CodegenTargetConfig target_config, + const CompilerOptions& compiler_options, HGraph* graph, std::function hook_before_codegen, bool has_result, Expected expected) { - CompilerOptions compiler_options; std::unique_ptr codegen(target_config.CreateCodeGenerator(graph, compiler_options)); RunCode(codegen.get(), graph, hook_before_codegen, has_result, expected); @@ -334,55 +324,37 @@ static void RunCode(CodegenTargetConfig target_config, #ifdef ART_ENABLE_CODEGEN_arm CodeGenerator* create_codegen_arm_vixl32(HGraph* graph, const CompilerOptions& compiler_options) { - std::unique_ptr features_arm( - ArmInstructionSetFeatures::FromCppDefines()); - return new (graph->GetAllocator()) - TestCodeGeneratorARMVIXL(graph, *features_arm.get(), compiler_options); + return new (graph->GetAllocator()) TestCodeGeneratorARMVIXL(graph, compiler_options); } #endif #ifdef ART_ENABLE_CODEGEN_arm64 CodeGenerator* create_codegen_arm64(HGraph* graph, const CompilerOptions& compiler_options) { - std::unique_ptr features_arm64( - Arm64InstructionSetFeatures::FromCppDefines()); - return new (graph->GetAllocator()) - TestCodeGeneratorARM64(graph, *features_arm64.get(), compiler_options); + return new (graph->GetAllocator()) TestCodeGeneratorARM64(graph, compiler_options); } #endif #ifdef ART_ENABLE_CODEGEN_x86 CodeGenerator* create_codegen_x86(HGraph* graph, const CompilerOptions& compiler_options) { - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - return new (graph->GetAllocator()) TestCodeGeneratorX86( - graph, *features_x86.get(), compiler_options); + return new (graph->GetAllocator()) TestCodeGeneratorX86(graph, compiler_options); } #endif #ifdef ART_ENABLE_CODEGEN_x86_64 CodeGenerator* create_codegen_x86_64(HGraph* graph, const CompilerOptions& compiler_options) { - std::unique_ptr features_x86_64( - X86_64InstructionSetFeatures::FromCppDefines()); - return new (graph->GetAllocator()) - x86_64::CodeGeneratorX86_64(graph, *features_x86_64.get(), compiler_options); + return new (graph->GetAllocator()) x86_64::CodeGeneratorX86_64(graph, compiler_options); } #endif #ifdef ART_ENABLE_CODEGEN_mips CodeGenerator* create_codegen_mips(HGraph* graph, const CompilerOptions& compiler_options) { - std::unique_ptr features_mips( - MipsInstructionSetFeatures::FromCppDefines()); - return new (graph->GetAllocator()) - mips::CodeGeneratorMIPS(graph, *features_mips.get(), compiler_options); + return new (graph->GetAllocator()) mips::CodeGeneratorMIPS(graph, compiler_options); } #endif #ifdef ART_ENABLE_CODEGEN_mips64 CodeGenerator* create_codegen_mips64(HGraph* graph, const CompilerOptions& compiler_options) { - std::unique_ptr features_mips64( - Mips64InstructionSetFeatures::FromCppDefines()); - return new (graph->GetAllocator()) - mips64::CodeGeneratorMIPS64(graph, *features_mips64.get(), compiler_options); + return new (graph->GetAllocator()) mips64::CodeGeneratorMIPS64(graph, compiler_options); } #endif diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h index ed2f8e995d00b700785f4ef7f5c92bb903fe13d0..5556f16740fb8c715cc2fd8f8e110858b472a1a5 100644 --- a/compiler/optimizing/common_arm64.h +++ b/compiler/optimizing/common_arm64.h @@ -151,23 +151,15 @@ inline vixl::aarch64::CPURegister InputCPURegisterOrZeroRegAt(HInstruction* inst return InputCPURegisterAt(instr, index); } -inline int64_t Int64ConstantFrom(Location location) { - HConstant* instr = location.GetConstant(); - if (instr->IsIntConstant()) { - return instr->AsIntConstant()->GetValue(); - } else if (instr->IsNullConstant()) { - return 0; - } else { - DCHECK(instr->IsLongConstant()) << instr->DebugName(); - return instr->AsLongConstant()->GetValue(); - } +inline int64_t Int64FromLocation(Location location) { + return Int64FromConstant(location.GetConstant()); } inline vixl::aarch64::Operand OperandFrom(Location location, DataType::Type type) { if (location.IsRegister()) { return vixl::aarch64::Operand(RegisterFrom(location, type)); } else { - return vixl::aarch64::Operand(Int64ConstantFrom(location)); + return vixl::aarch64::Operand(Int64FromLocation(location)); } } @@ -234,6 +226,13 @@ inline vixl::aarch64::Operand OperandFromMemOperand( } } +inline bool AddSubCanEncodeAsImmediate(int64_t value) { + // If `value` does not fit but `-value` does, VIXL will automatically use + // the 'opposite' instruction. + return vixl::aarch64::Assembler::IsImmAddSub(value) + || vixl::aarch64::Assembler::IsImmAddSub(-value); +} + inline bool Arm64CanEncodeConstantAsImmediate(HConstant* constant, HInstruction* instr) { int64_t value = CodeGenerator::GetInt64ValueOf(constant); @@ -249,6 +248,20 @@ inline bool Arm64CanEncodeConstantAsImmediate(HConstant* constant, HInstruction* return IsUint<8>(value); } + // Code generation for Min/Max: + // Cmp left_op, right_op + // Csel dst, left_op, right_op, cond + if (instr->IsMin() || instr->IsMax()) { + if (constant->GetUses().HasExactlyOneElement()) { + // If value can be encoded as immediate for the Cmp, then let VIXL handle + // the constant generation for the Csel. + return AddSubCanEncodeAsImmediate(value); + } + // These values are encodable as immediates for Cmp and VIXL will use csinc and csinv + // with the zr register as right_op, hence no constant generation is required. + return constant->IsZeroBitPattern() || constant->IsOne() || constant->IsMinusOne(); + } + // For single uses we let VIXL handle the constant generation since it will // use registers that are not managed by the register allocator (wip0, wip1). if (constant->GetUses().HasExactlyOneElement()) { @@ -275,10 +288,7 @@ inline bool Arm64CanEncodeConstantAsImmediate(HConstant* constant, HInstruction* instr->IsSub()) << instr->DebugName(); // Uses aliases of ADD/SUB instructions. - // If `value` does not fit but `-value` does, VIXL will automatically use - // the 'opposite' instruction. - return vixl::aarch64::Assembler::IsImmAddSub(value) - || vixl::aarch64::Assembler::IsImmAddSub(-value); + return AddSubCanEncodeAsImmediate(value); } } diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc index 6f11e628eef22ff6487ee6742322dfacc817e0e4..bb78c2357ee3bf5a518ab0b2e9472d6bf26d19c7 100644 --- a/compiler/optimizing/constant_folding.cc +++ b/compiler/optimizing/constant_folding.cc @@ -68,13 +68,14 @@ class InstructionWithAbsorbingInputSimplifier : public HGraphVisitor { }; -void HConstantFolding::Run() { +bool HConstantFolding::Run() { HConstantFoldingVisitor visitor(graph_); // Process basic blocks in reverse post-order in the dominator tree, // so that an instruction turned into a constant, used as input of // another instruction, may possibly be used to turn that second // instruction into a constant as well. visitor.VisitReversePostOrder(); + return true; } diff --git a/compiler/optimizing/constant_folding.h b/compiler/optimizing/constant_folding.h index 05c6df4a93d7dafaedce161738e7506b74ecbd34..f4dbc805c4e1253ee38d28bc8627a99cc158abcc 100644 --- a/compiler/optimizing/constant_folding.h +++ b/compiler/optimizing/constant_folding.h @@ -41,7 +41,7 @@ class HConstantFolding : public HOptimization { public: HConstantFolding(HGraph* graph, const char* name) : HOptimization(graph, name) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kConstantFoldingPassName = "constant_folding"; diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc index d27104752bfa3ae17afcd711fe4863aa8fdd8e0c..b1436f863c2ab7f9fbd6567e17ec78eb003df293 100644 --- a/compiler/optimizing/constant_folding_test.cc +++ b/compiler/optimizing/constant_folding_test.cc @@ -16,8 +16,6 @@ #include -#include "arch/x86/instruction_set_features_x86.h" -#include "code_generator_x86.h" #include "constant_folding.h" #include "dead_code_elimination.h" #include "driver/compiler_options.h" @@ -60,9 +58,6 @@ class ConstantFoldingTest : public OptimizingUnitTest { std::string actual_before = printer_before.str(); EXPECT_EQ(expected_before, actual_before); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegenX86(graph_, *features_x86.get(), CompilerOptions()); HConstantFolding(graph_, "constant_folding").Run(); GraphChecker graph_checker_cf(graph_); graph_checker_cf.Run(); diff --git a/compiler/optimizing/constructor_fence_redundancy_elimination.cc b/compiler/optimizing/constructor_fence_redundancy_elimination.cc index 4a66cd2265dd76263ea8172693ab8773b13c57cc..54bff22e98cbcfe0cf801c5aa3703e1f3bc87eb9 100644 --- a/compiler/optimizing/constructor_fence_redundancy_elimination.cc +++ b/compiler/optimizing/constructor_fence_redundancy_elimination.cc @@ -47,7 +47,7 @@ class CFREVisitor : public HGraphVisitor { candidate_fences_.push_back(constructor_fence); for (size_t input_idx = 0; input_idx < constructor_fence->InputCount(); ++input_idx) { - candidate_fence_targets_.Insert(constructor_fence->InputAt(input_idx)); + candidate_fence_targets_.insert(constructor_fence->InputAt(input_idx)); } } @@ -208,13 +208,13 @@ class CFREVisitor : public HGraphVisitor { // there is no benefit to this extra complexity unless we also reordered // the stores to come later. candidate_fences_.clear(); - candidate_fence_targets_.Clear(); + candidate_fence_targets_.clear(); } // A publishing 'store' is only interesting if the value being stored // is one of the fence `targets` in `candidate_fences`. bool IsInterestingPublishTarget(HInstruction* store_input) const { - return candidate_fence_targets_.Find(store_input) != candidate_fence_targets_.end(); + return candidate_fence_targets_.find(store_input) != candidate_fence_targets_.end(); } void MaybeMerge(HConstructorFence* target, HConstructorFence* src) { @@ -250,13 +250,14 @@ class CFREVisitor : public HGraphVisitor { DISALLOW_COPY_AND_ASSIGN(CFREVisitor); }; -void ConstructorFenceRedundancyElimination::Run() { +bool ConstructorFenceRedundancyElimination::Run() { CFREVisitor cfre_visitor(graph_, stats_); // Arbitrarily visit in reverse-post order. // The exact block visit order does not matter, as the algorithm // only operates on a single block at a time. cfre_visitor.VisitReversePostOrder(); + return true; } } // namespace art diff --git a/compiler/optimizing/constructor_fence_redundancy_elimination.h b/compiler/optimizing/constructor_fence_redundancy_elimination.h index f4b06d5544cd1be9ae83d5d70145fc67fddf35a3..367d9f21a0a6c243555163cef623984a91176b3a 100644 --- a/compiler/optimizing/constructor_fence_redundancy_elimination.h +++ b/compiler/optimizing/constructor_fence_redundancy_elimination.h @@ -52,7 +52,7 @@ class ConstructorFenceRedundancyElimination : public HOptimization { const char* name = kCFREPassName) : HOptimization(graph, name, stats) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kCFREPassName = "constructor_fence_redundancy_elimination"; diff --git a/compiler/optimizing/data_type.h b/compiler/optimizing/data_type.h index 4a6c91459fc1f2398a491df4eea0d9313ac81f8c..5ac6e460031652ee672a9d2e25e82b2d31d09e7c 100644 --- a/compiler/optimizing/data_type.h +++ b/compiler/optimizing/data_type.h @@ -210,6 +210,27 @@ class DataType { static bool IsTypeConversionImplicit(Type input_type, Type result_type); static bool IsTypeConversionImplicit(int64_t value, Type result_type); + static bool IsZeroExtension(Type input_type, Type result_type) { + return IsIntOrLongType(result_type) && + IsUnsignedType(input_type) && + Size(result_type) > Size(input_type); + } + + static Type ToSigned(Type type) { + switch (type) { + case Type::kUint8: + return Type::kInt8; + case Type::kUint16: + return Type::kInt16; + case Type::kUint32: + return Type::kInt32; + case Type::kUint64: + return Type::kInt64; + default: + return type; + } + } + static const char* PrettyDescriptor(Type type); private: diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc index 9fa0f72e80e5b631ece56d8b374d6bc30cc1730a..1dc10948ccfc6e6341fa97d1101b9595ce18b79d 100644 --- a/compiler/optimizing/dead_code_elimination.cc +++ b/compiler/optimizing/dead_code_elimination.cc @@ -508,7 +508,7 @@ void HDeadCodeElimination::RemoveDeadInstructions() { } } -void HDeadCodeElimination::Run() { +bool HDeadCodeElimination::Run() { // Do not eliminate dead blocks if the graph has irreducible loops. We could // support it, but that would require changes in our loop representation to handle // multiple entry points. We decided it was not worth the complexity. @@ -526,6 +526,7 @@ void HDeadCodeElimination::Run() { } SsaRedundantPhiElimination(graph_).Run(); RemoveDeadInstructions(); + return true; } } // namespace art diff --git a/compiler/optimizing/dead_code_elimination.h b/compiler/optimizing/dead_code_elimination.h index 92a7f562e12b497975467f7aa204559d468c1f0c..90caa53764b43a67fe90c49700c85b5434c52fbf 100644 --- a/compiler/optimizing/dead_code_elimination.h +++ b/compiler/optimizing/dead_code_elimination.h @@ -32,7 +32,8 @@ class HDeadCodeElimination : public HOptimization { HDeadCodeElimination(HGraph* graph, OptimizingCompilerStats* stats, const char* name) : HOptimization(graph, name, stats) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; + static constexpr const char* kDeadCodeEliminationPassName = "dead_code_elimination"; private: diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc index adb6ce1187c83efbd639200a8a860697c03a7b99..277453545a66127963be2c373419d22b904cd1a8 100644 --- a/compiler/optimizing/dead_code_elimination_test.cc +++ b/compiler/optimizing/dead_code_elimination_test.cc @@ -16,8 +16,6 @@ #include "dead_code_elimination.h" -#include "arch/x86/instruction_set_features_x86.h" -#include "code_generator_x86.h" #include "driver/compiler_options.h" #include "graph_checker.h" #include "optimizing_unit_test.h" @@ -45,9 +43,6 @@ void DeadCodeEliminationTest::TestCode(const std::vector& data, std::string actual_before = printer_before.str(); ASSERT_EQ(actual_before, expected_before); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegenX86(graph, *features_x86.get(), CompilerOptions()); HDeadCodeElimination(graph, nullptr /* stats */, "dead_code_elimination").Run(); GraphChecker graph_checker(graph); graph_checker.Run(); diff --git a/compiler/optimizing/emit_swap_mips_test.cc b/compiler/optimizing/emit_swap_mips_test.cc index b63914faf7f747ba3bc1514ea62e0fe6b2832365..293c1ab3f3f9639a5debfa7b48b1b83dabe2a8d2 100644 --- a/compiler/optimizing/emit_swap_mips_test.cc +++ b/compiler/optimizing/emit_swap_mips_test.cc @@ -28,11 +28,12 @@ namespace art { class EmitSwapMipsTest : public OptimizingUnitTest { public: void SetUp() OVERRIDE { + instruction_set_ = InstructionSet::kMips; + instruction_set_features_ = MipsInstructionSetFeatures::FromCppDefines(); + OptimizingUnitTest::SetUp(); graph_ = CreateGraph(); - isa_features_ = MipsInstructionSetFeatures::FromCppDefines(); - codegen_ = new (graph_->GetAllocator()) mips::CodeGeneratorMIPS(graph_, - *isa_features_.get(), - CompilerOptions()); + codegen_.reset( + new (graph_->GetAllocator()) mips::CodeGeneratorMIPS(graph_, *compiler_options_)); moves_ = new (GetAllocator()) HParallelMove(GetAllocator()); test_helper_.reset( new AssemblerTestInfrastructure(GetArchitectureString(), @@ -47,8 +48,10 @@ class EmitSwapMipsTest : public OptimizingUnitTest { void TearDown() OVERRIDE { test_helper_.reset(); - isa_features_.reset(); + codegen_.reset(); + graph_ = nullptr; ResetPoolAndAllocator(); + OptimizingUnitTest::TearDown(); } // Get the typically used name for this architecture. @@ -106,10 +109,9 @@ class EmitSwapMipsTest : public OptimizingUnitTest { protected: HGraph* graph_; HParallelMove* moves_; - mips::CodeGeneratorMIPS* codegen_; + std::unique_ptr codegen_; mips::MipsAssembler* assembler_; std::unique_ptr test_helper_; - std::unique_ptr isa_features_; }; TEST_F(EmitSwapMipsTest, TwoRegisters) { diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index c88baa8610f5caf5b8e1626a8fab61cc0ac4df3f..a689f35e0fdd07ebabb55db7821c87a9b1c8cf81 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -25,6 +25,11 @@ #include "base/bit_vector-inl.h" #include "base/scoped_arena_allocator.h" #include "base/scoped_arena_containers.h" +#include "handle.h" +#include "mirror/class.h" +#include "obj_ptr-inl.h" +#include "scoped_thread_state_change-inl.h" +#include "subtype_check.h" namespace art { @@ -53,6 +58,30 @@ static bool IsExitTryBoundaryIntoExitBlock(HBasicBlock* block) { !boundary->IsEntry(); } + +size_t GraphChecker::Run(bool pass_change, size_t last_size) { + size_t current_size = GetGraph()->GetReversePostOrder().size(); + if (!pass_change) { + // Nothing changed for certain. Do a quick sanity check on that assertion + // for anything other than the first call (when last size was still 0). + if (last_size != 0) { + if (current_size != last_size) { + AddError(StringPrintf("Incorrect no-change assertion, " + "last graph size %zu vs current graph size %zu", + last_size, current_size)); + } + } + // TODO: if we would trust the "false" value of the flag completely, we + // could skip checking the graph at this point. + } + + // VisitReversePostOrder is used instead of VisitInsertionOrder, + // as the latter might visit dead blocks removed by the dominator + // computation. + VisitReversePostOrder(); + return current_size; +} + void GraphChecker::VisitBasicBlock(HBasicBlock* block) { current_block_ = block; @@ -548,30 +577,85 @@ void GraphChecker::VisitReturnVoid(HReturnVoid* ret) { } } -void GraphChecker::VisitCheckCast(HCheckCast* check) { - VisitInstruction(check); - HInstruction* input = check->InputAt(1); - if (!input->IsLoadClass()) { - AddError(StringPrintf("%s:%d expects a HLoadClass as second input, not %s:%d.", +void GraphChecker::CheckTypeCheckBitstringInput(HTypeCheckInstruction* check, + size_t input_pos, + bool check_value, + uint32_t expected_value, + const char* name) { + if (!check->InputAt(input_pos)->IsIntConstant()) { + AddError(StringPrintf("%s:%d (bitstring) expects a HIntConstant input %zu (%s), not %s:%d.", check->DebugName(), check->GetId(), - input->DebugName(), - input->GetId())); + input_pos, + name, + check->InputAt(2)->DebugName(), + check->InputAt(2)->GetId())); + } else if (check_value) { + uint32_t actual_value = + static_cast(check->InputAt(input_pos)->AsIntConstant()->GetValue()); + if (actual_value != expected_value) { + AddError(StringPrintf("%s:%d (bitstring) has %s 0x%x, not 0x%x as expected.", + check->DebugName(), + check->GetId(), + name, + actual_value, + expected_value)); + } } } -void GraphChecker::VisitInstanceOf(HInstanceOf* instruction) { - VisitInstruction(instruction); - HInstruction* input = instruction->InputAt(1); - if (!input->IsLoadClass()) { - AddError(StringPrintf("%s:%d expects a HLoadClass as second input, not %s:%d.", - instruction->DebugName(), - instruction->GetId(), - input->DebugName(), - input->GetId())); +void GraphChecker::HandleTypeCheckInstruction(HTypeCheckInstruction* check) { + VisitInstruction(check); + HInstruction* input = check->InputAt(1); + if (check->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { + if (!input->IsNullConstant()) { + AddError(StringPrintf("%s:%d (bitstring) expects a HNullConstant as second input, not %s:%d.", + check->DebugName(), + check->GetId(), + input->DebugName(), + input->GetId())); + } + bool check_values = false; + BitString::StorageType expected_path_to_root = 0u; + BitString::StorageType expected_mask = 0u; + { + ScopedObjectAccess soa(Thread::Current()); + ObjPtr klass = check->GetClass().Get(); + MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); + SubtypeCheckInfo::State state = SubtypeCheck>::GetState(klass); + if (state == SubtypeCheckInfo::kAssigned) { + expected_path_to_root = + SubtypeCheck>::GetEncodedPathToRootForTarget(klass); + expected_mask = SubtypeCheck>::GetEncodedPathToRootMask(klass); + check_values = true; + } else { + AddError(StringPrintf("%s:%d (bitstring) references a class with unassigned bitstring.", + check->DebugName(), + check->GetId())); + } + } + CheckTypeCheckBitstringInput( + check, /* input_pos */ 2, check_values, expected_path_to_root, "path_to_root"); + CheckTypeCheckBitstringInput(check, /* input_pos */ 3, check_values, expected_mask, "mask"); + } else { + if (!input->IsLoadClass()) { + AddError(StringPrintf("%s:%d (classic) expects a HLoadClass as second input, not %s:%d.", + check->DebugName(), + check->GetId(), + input->DebugName(), + input->GetId())); + } } } +void GraphChecker::VisitCheckCast(HCheckCast* check) { + HandleTypeCheckInstruction(check); +} + +void GraphChecker::VisitInstanceOf(HInstanceOf* instruction) { + HandleTypeCheckInstruction(instruction); +} + void GraphChecker::HandleLoop(HBasicBlock* loop_header) { int id = loop_header->GetBlockId(); HLoopInformation* loop_information = loop_header->GetLoopInformation(); diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h index 0f0b49d240a8e64790bbad4699ee2a84c142283b..3a2bb7a00c41ab05cb608a17cd7791f5fc3b9783 100644 --- a/compiler/optimizing/graph_checker.h +++ b/compiler/optimizing/graph_checker.h @@ -38,13 +38,11 @@ class GraphChecker : public HGraphDelegateVisitor { seen_ids_.ClearAllBits(); } - // Check the whole graph (in reverse post-order). - void Run() { - // VisitReversePostOrder is used instead of VisitInsertionOrder, - // as the latter might visit dead blocks removed by the dominator - // computation. - VisitReversePostOrder(); - } + // Check the whole graph. The pass_change parameter indicates whether changes + // may have occurred during the just executed pass. The default value is + // conservatively "true" (something may have changed). The last_size parameter + // and return value pass along the observed graph sizes. + size_t Run(bool pass_change = true, size_t last_size = 0); void VisitBasicBlock(HBasicBlock* block) OVERRIDE; @@ -71,6 +69,12 @@ class GraphChecker : public HGraphDelegateVisitor { void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE; void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE; + void CheckTypeCheckBitstringInput(HTypeCheckInstruction* check, + size_t input_pos, + bool check_value, + uint32_t expected_value, + const char* name); + void HandleTypeCheckInstruction(HTypeCheckInstruction* instruction); void HandleLoop(HBasicBlock* loop_header); void HandleBooleanInput(HInstruction* instruction, size_t input_index); diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 5ff31cead589b7b90367c9ec63edfcf7527faa8a..d65ad40565176715d8bf2c4c280283325f3df4f0 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -386,20 +386,39 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { << load_class->NeedsAccessCheck() << std::noboolalpha; } + void VisitLoadMethodHandle(HLoadMethodHandle* load_method_handle) OVERRIDE { + StartAttributeStream("load_kind") << "RuntimeCall"; + StartAttributeStream("method_handle_index") << load_method_handle->GetMethodHandleIndex(); + } + + void VisitLoadMethodType(HLoadMethodType* load_method_type) OVERRIDE { + StartAttributeStream("load_kind") << "RuntimeCall"; + const DexFile& dex_file = load_method_type->GetDexFile(); + const DexFile::ProtoId& proto_id = dex_file.GetProtoId(load_method_type->GetProtoIndex()); + StartAttributeStream("method_type") << dex_file.GetProtoSignature(proto_id); + } + void VisitLoadString(HLoadString* load_string) OVERRIDE { StartAttributeStream("load_kind") << load_string->GetLoadKind(); } - void VisitCheckCast(HCheckCast* check_cast) OVERRIDE { - StartAttributeStream("check_kind") << check_cast->GetTypeCheckKind(); + void HandleTypeCheckInstruction(HTypeCheckInstruction* check) { + StartAttributeStream("check_kind") << check->GetTypeCheckKind(); StartAttributeStream("must_do_null_check") << std::boolalpha - << check_cast->MustDoNullCheck() << std::noboolalpha; + << check->MustDoNullCheck() << std::noboolalpha; + if (check->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { + StartAttributeStream("path_to_root") << std::hex + << "0x" << check->GetBitstringPathToRoot() << std::dec; + StartAttributeStream("mask") << std::hex << "0x" << check->GetBitstringMask() << std::dec; + } + } + + void VisitCheckCast(HCheckCast* check_cast) OVERRIDE { + HandleTypeCheckInstruction(check_cast); } void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE { - StartAttributeStream("check_kind") << instance_of->GetTypeCheckKind(); - StartAttributeStream("must_do_null_check") << std::boolalpha - << instance_of->MustDoNullCheck() << std::noboolalpha; + HandleTypeCheckInstruction(instance_of); } void VisitArrayLength(HArrayLength* array_length) OVERRIDE { @@ -576,6 +595,11 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { } StartAttributeStream() << input_list; } + if (instruction->GetDexPc() != kNoDexPc) { + StartAttributeStream("dex_pc") << instruction->GetDexPc(); + } else { + StartAttributeStream("dex_pc") << "n/a"; + } instruction->Accept(this); if (instruction->HasEnvironment()) { StringList envs; @@ -641,20 +665,32 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { << std::boolalpha << loop_info->IsIrreducible() << std::noboolalpha; } + // For the builder and the inliner, we want to add extra information on HInstructions + // that have reference types, and also HInstanceOf/HCheckcast. if ((IsPass(HGraphBuilder::kBuilderPassName) || IsPass(HInliner::kInlinerPassName)) - && (instruction->GetType() == DataType::Type::kReference)) { - ReferenceTypeInfo info = instruction->IsLoadClass() - ? instruction->AsLoadClass()->GetLoadedClassRTI() - : instruction->GetReferenceTypeInfo(); + && (instruction->GetType() == DataType::Type::kReference || + instruction->IsInstanceOf() || + instruction->IsCheckCast())) { + ReferenceTypeInfo info = (instruction->GetType() == DataType::Type::kReference) + ? instruction->IsLoadClass() + ? instruction->AsLoadClass()->GetLoadedClassRTI() + : instruction->GetReferenceTypeInfo() + : instruction->IsInstanceOf() + ? instruction->AsInstanceOf()->GetTargetClassRTI() + : instruction->AsCheckCast()->GetTargetClassRTI(); ScopedObjectAccess soa(Thread::Current()); if (info.IsValid()) { StartAttributeStream("klass") << mirror::Class::PrettyDescriptor(info.GetTypeHandle().Get()); - StartAttributeStream("can_be_null") - << std::boolalpha << instruction->CanBeNull() << std::noboolalpha; + if (instruction->GetType() == DataType::Type::kReference) { + StartAttributeStream("can_be_null") + << std::boolalpha << instruction->CanBeNull() << std::noboolalpha; + } StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha; - } else if (instruction->IsLoadClass()) { + } else if (instruction->IsLoadClass() || + instruction->IsInstanceOf() || + instruction->IsCheckCast()) { StartAttributeStream("klass") << "unresolved"; } else { // The NullConstant may be added to the graph during other passes that happen between diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc index f05159b735329cc064bbb1db013ce5d9d4871fa9..e6b6326726eee80d67506d93098d305de1f4d9ec 100644 --- a/compiler/optimizing/gvn.cc +++ b/compiler/optimizing/gvn.cc @@ -352,7 +352,7 @@ class GlobalValueNumberer : public ValueObject { visited_blocks_.ClearAllBits(); } - void Run(); + bool Run(); private: // Per-block GVN. Will also update the ValueSet of the dominated and @@ -397,7 +397,7 @@ class GlobalValueNumberer : public ValueObject { DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer); }; -void GlobalValueNumberer::Run() { +bool GlobalValueNumberer::Run() { DCHECK(side_effects_.HasRun()); sets_[graph_->GetEntryBlock()->GetBlockId()] = new (&allocator_) ValueSet(&allocator_); @@ -406,6 +406,7 @@ void GlobalValueNumberer::Run() { for (HBasicBlock* block : graph_->GetReversePostOrder()) { VisitBasicBlock(block); } + return true; } void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) { @@ -478,7 +479,10 @@ void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) { HInstruction* next = current->GetNext(); // Do not kill the set with the side effects of the instruction just now: if // the instruction is GVN'ed, we don't need to kill. - if (current->CanBeMoved()) { + // + // BoundType is a special case example of an instruction which shouldn't be moved but can be + // GVN'ed. + if (current->CanBeMoved() || current->IsBoundType()) { if (current->IsBinaryOperation() && current->AsBinaryOperation()->IsCommutative()) { // For commutative ops, (x op y) will be treated the same as (y op x) // after fixed ordering. @@ -557,9 +561,9 @@ HBasicBlock* GlobalValueNumberer::FindVisitedBlockWithRecyclableSet( return secondary_match; } -void GVNOptimization::Run() { +bool GVNOptimization::Run() { GlobalValueNumberer gvn(graph_, side_effects_); - gvn.Run(); + return gvn.Run(); } } // namespace art diff --git a/compiler/optimizing/gvn.h b/compiler/optimizing/gvn.h index 4fdba26ebd262130403da749ba1d78324114f630..75cfff214015d8b74938fe3b3e1e04058219974a 100644 --- a/compiler/optimizing/gvn.h +++ b/compiler/optimizing/gvn.h @@ -31,7 +31,7 @@ class GVNOptimization : public HOptimization { const char* pass_name = kGlobalValueNumberingPassName) : HOptimization(graph, pass_name), side_effects_(side_effects) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kGlobalValueNumberingPassName = "GVN"; diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc index d270c6a28e23deb4626cecf63734de02514a9b88..a4d638f4c6674f17ae46e746a0aafe421c479468 100644 --- a/compiler/optimizing/induction_var_analysis.cc +++ b/compiler/optimizing/induction_var_analysis.cc @@ -243,7 +243,7 @@ HInductionVarAnalysis::HInductionVarAnalysis(HGraph* graph, const char* name) graph->GetAllocator()->Adapter(kArenaAllocInductionVarAnalysis)) { } -void HInductionVarAnalysis::Run() { +bool HInductionVarAnalysis::Run() { // Detects sequence variables (generalized induction variables) during an outer to inner // traversal of all loops using Gerlek's algorithm. The order is important to enable // range analysis on outer loop while visiting inner loops. @@ -253,6 +253,7 @@ void HInductionVarAnalysis::Run() { VisitLoop(graph_block->GetLoopInformation()); } } + return !induction_.empty(); } void HInductionVarAnalysis::VisitLoop(HLoopInformation* loop) { diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h index acad77d35fb5f52e9c7aeb9ea8b8a927215bc5cb..89fed2ec64b8859afab2467cb093aa8c1d96d556 100644 --- a/compiler/optimizing/induction_var_analysis.h +++ b/compiler/optimizing/induction_var_analysis.h @@ -37,7 +37,7 @@ class HInductionVarAnalysis : public HOptimization { public: explicit HInductionVarAnalysis(HGraph* graph, const char* name = kInductionPassName); - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kInductionPassName = "induction_var_analysis"; diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc index 99dec112400c7c0e12c50608ab5fea8c1cb617a9..55eca2316a173ad50888315803bcc5f336cd9a53 100644 --- a/compiler/optimizing/induction_var_range.cc +++ b/compiler/optimizing/induction_var_range.cc @@ -78,22 +78,15 @@ static bool IsGEZero(HInstruction* instruction) { DCHECK(instruction != nullptr); if (instruction->IsArrayLength()) { return true; - } else if (instruction->IsInvokeStaticOrDirect()) { - switch (instruction->AsInvoke()->GetIntrinsic()) { - case Intrinsics::kMathMinIntInt: - case Intrinsics::kMathMinLongLong: - // Instruction MIN(>=0, >=0) is >= 0. - return IsGEZero(instruction->InputAt(0)) && - IsGEZero(instruction->InputAt(1)); - case Intrinsics::kMathAbsInt: - case Intrinsics::kMathAbsLong: - // Instruction ABS(>=0) is >= 0. - // NOTE: ABS(minint) = minint prevents assuming - // >= 0 without looking at the argument. - return IsGEZero(instruction->InputAt(0)); - default: - break; - } + } else if (instruction->IsMin()) { + // Instruction MIN(>=0, >=0) is >= 0. + return IsGEZero(instruction->InputAt(0)) && + IsGEZero(instruction->InputAt(1)); + } else if (instruction->IsAbs()) { + // Instruction ABS(>=0) is >= 0. + // NOTE: ABS(minint) = minint prevents assuming + // >= 0 without looking at the argument. + return IsGEZero(instruction->InputAt(0)); } int64_t value = -1; return IsInt64AndGet(instruction, &value) && value >= 0; @@ -102,21 +95,14 @@ static bool IsGEZero(HInstruction* instruction) { /** Hunts "under the hood" for a suitable instruction at the hint. */ static bool IsMaxAtHint( HInstruction* instruction, HInstruction* hint, /*out*/HInstruction** suitable) { - if (instruction->IsInvokeStaticOrDirect()) { - switch (instruction->AsInvoke()->GetIntrinsic()) { - case Intrinsics::kMathMinIntInt: - case Intrinsics::kMathMinLongLong: - // For MIN(x, y), return most suitable x or y as maximum. - return IsMaxAtHint(instruction->InputAt(0), hint, suitable) || - IsMaxAtHint(instruction->InputAt(1), hint, suitable); - default: - break; - } + if (instruction->IsMin()) { + // For MIN(x, y), return most suitable x or y as maximum. + return IsMaxAtHint(instruction->InputAt(0), hint, suitable) || + IsMaxAtHint(instruction->InputAt(1), hint, suitable); } else { *suitable = instruction; return HuntForDeclaration(instruction) == hint; } - return false; } /** Post-analysis simplification of a minimum value that makes the bound more useful to clients. */ @@ -365,14 +351,16 @@ void InductionVarRange::Replace(HInstruction* instruction, } } -bool InductionVarRange::IsFinite(HLoopInformation* loop, /*out*/ int64_t* tc) const { - HInductionVarAnalysis::InductionInfo *trip = - induction_analysis_->LookupInfo(loop, GetLoopControl(loop)); - if (trip != nullptr && !IsUnsafeTripCount(trip)) { - IsConstant(trip->op_a, kExact, tc); - return true; - } - return false; +bool InductionVarRange::IsFinite(HLoopInformation* loop, /*out*/ int64_t* trip_count) const { + bool is_constant_unused = false; + return CheckForFiniteAndConstantProps(loop, &is_constant_unused, trip_count); +} + +bool InductionVarRange::HasKnownTripCount(HLoopInformation* loop, + /*out*/ int64_t* trip_count) const { + bool is_constant = false; + CheckForFiniteAndConstantProps(loop, &is_constant, trip_count); + return is_constant; } bool InductionVarRange::IsUnitStride(HInstruction* context, @@ -431,6 +419,18 @@ HInstruction* InductionVarRange::GenerateTripCount(HLoopInformation* loop, // Private class methods. // +bool InductionVarRange::CheckForFiniteAndConstantProps(HLoopInformation* loop, + /*out*/ bool* is_constant, + /*out*/ int64_t* trip_count) const { + HInductionVarAnalysis::InductionInfo *trip = + induction_analysis_->LookupInfo(loop, GetLoopControl(loop)); + if (trip != nullptr && !IsUnsafeTripCount(trip)) { + *is_constant = IsConstant(trip->op_a, kExact, trip_count); + return true; + } + return false; +} + bool InductionVarRange::IsConstant(HInductionVarAnalysis::InductionInfo* info, ConstantRequest request, /*out*/ int64_t* value) const { diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h index 0b980f596a3307ed82db741d30a4df6d18272d09..906dc6bb7b93bd583cace9b02651cc42274e9c90 100644 --- a/compiler/optimizing/induction_var_range.h +++ b/compiler/optimizing/induction_var_range.h @@ -161,9 +161,15 @@ class InductionVarRange { } /** - * Checks if header logic of a loop terminates. Sets trip-count tc if known. + * Checks if header logic of a loop terminates. If trip count is known sets 'trip_count' to its + * value. */ - bool IsFinite(HLoopInformation* loop, /*out*/ int64_t* tc) const; + bool IsFinite(HLoopInformation* loop, /*out*/ int64_t* trip_count) const; + + /** + * Checks if a trip count is known for the loop and sets 'trip_count' to its value in this case. + */ + bool HasKnownTripCount(HLoopInformation* loop, /*out*/ int64_t* trip_count) const; /** * Checks if the given instruction is a unit stride induction inside the closest enveloping @@ -193,6 +199,14 @@ class InductionVarRange { kAtLeast }; + /** + * Checks if header logic of a loop terminates. If trip count is known (constant) sets + * 'is_constant' to true and 'trip_count' to the trip count value. + */ + bool CheckForFiniteAndConstantProps(HLoopInformation* loop, + /*out*/ bool* is_constant, + /*out*/ int64_t* trip_count) const; + /** * Returns true if exact or upper/lower bound on the given induction * information is known as a 64-bit constant, which is returned in value. diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 4fc7262265e63809616c8d0c33ae3d2d019df9a7..3ba741472e919fd11d115b2bf71f375c24a7744f 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -20,6 +20,7 @@ #include "base/enums.h" #include "builder.h" #include "class_linker.h" +#include "class_root.h" #include "constant_folding.h" #include "data_type-inl.h" #include "dead_code_elimination.h" @@ -124,13 +125,18 @@ void HInliner::UpdateInliningBudget() { } } -void HInliner::Run() { - if (graph_->IsDebuggable()) { +bool HInliner::Run() { + if (codegen_->GetCompilerOptions().GetInlineMaxCodeUnits() == 0) { + // Inlining effectively disabled. + return false; + } else if (graph_->IsDebuggable()) { // For simplicity, we currently never inline when the graph is debuggable. This avoids // doing some logic in the runtime to discover if a method could have been inlined. - return; + return false; } + bool didInline = false; + // Initialize the number of instructions for the method being compiled. Recursive calls // to HInliner::Run have already updated the instruction count. if (outermost_graph_ == graph_) { @@ -147,10 +153,11 @@ void HInliner::Run() { // that this method is actually inlined; // - if a method's name contains the substring "$noinline$", do not // inline that method. - // We limit this to AOT compilation, as the JIT may or may not inline + // We limit the latter to AOT compilation, as the JIT may or may not inline // depending on the state of classes at runtime. - const bool honor_inlining_directives = - IsCompilingWithCoreImage() && Runtime::Current()->IsAotCompiler(); + const bool honor_noinline_directives = IsCompilingWithCoreImage(); + const bool honor_inline_directives = + honor_noinline_directives && Runtime::Current()->IsAotCompiler(); // Keep a copy of all blocks when starting the visit. ArenaVector blocks = graph_->GetReversePostOrder(); @@ -164,25 +171,32 @@ void HInliner::Run() { HInvoke* call = instruction->AsInvoke(); // As long as the call is not intrinsified, it is worth trying to inline. if (call != nullptr && call->GetIntrinsic() == Intrinsics::kNone) { - if (honor_inlining_directives) { + if (honor_noinline_directives) { // Debugging case: directives in method names control or assert on inlining. std::string callee_name = outer_compilation_unit_.GetDexFile()->PrettyMethod( call->GetDexMethodIndex(), /* with_signature */ false); // Tests prevent inlining by having $noinline$ in their method names. if (callee_name.find("$noinline$") == std::string::npos) { - if (!TryInline(call)) { + if (TryInline(call)) { + didInline = true; + } else if (honor_inline_directives) { bool should_have_inlined = (callee_name.find("$inline$") != std::string::npos); CHECK(!should_have_inlined) << "Could not inline " << callee_name; } } } else { + DCHECK(!honor_inline_directives); // Normal case: try to inline. - TryInline(call); + if (TryInline(call)) { + didInline = true; + } } } instruction = next; } } + + return didInline; } static bool IsMethodOrDeclaringClassFinal(ArtMethod* method) @@ -446,9 +460,10 @@ static bool AlwaysThrows(CompilerDriver* const compiler_driver, ArtMethod* metho 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()); @@ -524,7 +539,7 @@ static Handle> AllocateInlineCacheHolder( Handle> inline_cache = hs->NewHandle( mirror::ObjectArray::Alloc( self, - class_linker->GetClassRoot(ClassLinker::kClassArrayClass), + GetClassRoot>(class_linker), InlineCache::kIndividualCacheSize)); if (inline_cache == nullptr) { // We got an OOME. Just clear the exception, and don't inline. @@ -716,7 +731,7 @@ HInliner::InlineCacheType HInliner::ExtractClassesFromOfflineProfile( offline_profile.dex_references.size()); for (size_t i = 0; i < offline_profile.dex_references.size(); i++) { bool found = false; - for (const DexFile* dex_file : compiler_driver_->GetDexFilesForOatFile()) { + for (const DexFile* dex_file : codegen_->GetCompilerOptions().GetDexFilesForOatFile()) { if (offline_profile.dex_references[i].MatchesDex(dex_file)) { dex_profile_index_to_dex_cache[i] = caller_compilation_unit_.GetClassLinker()->FindDexCache(self, *dex_file); @@ -764,7 +779,7 @@ HInliner::InlineCacheType HInliner::ExtractClassesFromOfflineProfile( HInstanceFieldGet* HInliner::BuildGetReceiverClass(ClassLinker* class_linker, HInstruction* receiver, uint32_t dex_pc) const { - ArtField* field = class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0); + ArtField* field = GetClassRoot(class_linker)->GetInstanceField(0); DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_"); HInstanceFieldGet* result = new (graph_->GetAllocator()) HInstanceFieldGet( receiver, @@ -934,7 +949,7 @@ HInstruction* HInliner::AddTypeGuard(HInstruction* receiver, invoke_instruction->GetDexPc(), /* needs_access_check */ false); HLoadClass::LoadKind kind = HSharpening::ComputeLoadClassKind( - load_class, codegen_, compiler_driver_, caller_compilation_unit_); + load_class, codegen_, caller_compilation_unit_); DCHECK(kind != HLoadClass::LoadKind::kInvalid) << "We should always be able to reference a class for inline caches"; // Load kind must be set before inserting the instruction into the graph. @@ -1403,6 +1418,22 @@ size_t HInliner::CountRecursiveCallsOf(ArtMethod* method) const { return count; } +static inline bool MayInline(const CompilerOptions& compiler_options, + const DexFile& inlined_from, + const DexFile& inlined_into) { + if (kIsTargetBuild) { + return true; + } + + // We're not allowed to inline across dex files if we're the no-inline-from dex file. + if (!IsSameDexFile(inlined_from, inlined_into) && + ContainsElement(compiler_options.GetNoInlineFromDexFile(), &inlined_from)) { + return false; + } + + return true; +} + bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction, ArtMethod* method, ReferenceTypeInfo receiver_type, @@ -1424,8 +1455,9 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction, // Check whether we're allowed to inline. The outermost compilation unit is the relevant // dex file here (though the transitivity of an inline chain would allow checking the calller). - if (!compiler_driver_->MayInline(method->GetDexFile(), - outer_compilation_unit_.GetDexFile())) { + if (!MayInline(codegen_->GetCompilerOptions(), + *method->GetDexFile(), + *outer_compilation_unit_.GetDexFile())) { if (TryPatternSubstitution(invoke_instruction, method, return_replacement)) { LOG_SUCCESS() << "Successfully replaced pattern of invoke " << method->PrettyMethod(); @@ -1450,7 +1482,7 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction, return false; } - size_t inline_max_code_units = compiler_driver_->GetCompilerOptions().GetInlineMaxCodeUnits(); + size_t inline_max_code_units = codegen_->GetCompilerOptions().GetInlineMaxCodeUnits(); if (accessor.InsnsSizeInCodeUnits() > inline_max_code_units) { LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedCodeItem) << "Method " << method->PrettyMethod() @@ -1751,7 +1783,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, graph_->GetArenaStack(), callee_dex_file, method_index, - compiler_driver_->GetInstructionSet(), + codegen_->GetCompilerOptions().GetInstructionSet(), invoke_type, graph_->IsDebuggable(), /* osr */ false, @@ -1788,8 +1820,8 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, return false; } - if (!RegisterAllocator::CanAllocateRegistersFor(*callee_graph, - compiler_driver_->GetInstructionSet())) { + if (!RegisterAllocator::CanAllocateRegistersFor( + *callee_graph, codegen_->GetCompilerOptions().GetInstructionSet())) { LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedRegisterAllocator) << "Method " << callee_dex_file.PrettyMethod(method_index) << " cannot be inlined because of the register allocator"; @@ -1990,8 +2022,8 @@ void HInliner::RunOptimizations(HGraph* callee_graph, // optimization that could lead to a HDeoptimize. The following optimizations do not. HDeadCodeElimination dce(callee_graph, inline_stats_, "dead_code_elimination$inliner"); HConstantFolding fold(callee_graph, "constant_folding$inliner"); - HSharpening sharpening(callee_graph, codegen_, compiler_driver_); - InstructionSimplifier simplify(callee_graph, codegen_, compiler_driver_, inline_stats_); + HSharpening sharpening(callee_graph, codegen_); + InstructionSimplifier simplify(callee_graph, codegen_, inline_stats_); IntrinsicsRecognizer intrinsics(callee_graph, inline_stats_); HOptimization* optimizations[] = { @@ -2107,9 +2139,8 @@ bool HInliner::ReturnTypeMoreSpecific(HInvoke* invoke_instruction, return true; } else if (return_replacement->IsInstanceFieldGet()) { HInstanceFieldGet* field_get = return_replacement->AsInstanceFieldGet(); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); if (field_get->GetFieldInfo().GetField() == - class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0)) { + GetClassRoot()->GetInstanceField(0)) { return true; } } diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index 02465d37bab2a8be65e7cc2d1434e6d0d114a9c4..2fdf6a1306716aad339140d2d38fd4a9f5043678 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -19,8 +19,8 @@ #include "dex/dex_file_types.h" #include "dex/invoke_type.h" -#include "jit/profile_compilation_info.h" #include "optimization.h" +#include "profile/profile_compilation_info.h" namespace art { @@ -60,7 +60,7 @@ class HInliner : public HOptimization { handles_(handles), inline_stats_(nullptr) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kInlinerPassName = "inliner"; diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index c7aef3779d149cd95916e6b01730d975d6804a72..771e066d2f1305473cedccde9995b5b49da7847f 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -448,14 +448,8 @@ void HInstructionBuilder::BuildIntrinsic(ArtMethod* method) { invoke_type, target_method, HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); - HandleInvoke(invoke, - in_vregs, - /* args */ nullptr, - graph_->GetNumberOfVRegs() - in_vregs, - /* is_range */ true, - dex_file_->GetMethodShorty(method_idx), - /* clinit_check */ nullptr, - /* is_unresolved */ false); + RangeInstructionOperands operands(graph_->GetNumberOfVRegs() - in_vregs, in_vregs); + HandleInvoke(invoke, operands, dex_file_->GetMethodShorty(method_idx), /* is_unresolved */ false); // Add the return instruction. if (return_type_ == DataType::Type::kVoid) { @@ -916,16 +910,13 @@ static bool IsStringConstructor(ArtMethod* method) { bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, uint32_t dex_pc, uint32_t method_idx, - uint32_t number_of_vreg_arguments, - bool is_range, - uint32_t* args, - uint32_t register_index) { + const InstructionOperands& operands) { InvokeType invoke_type = GetInvokeTypeFromOpCode(instruction.Opcode()); - const char* descriptor = dex_file_->GetMethodShorty(method_idx); - DataType::Type return_type = DataType::FromShorty(descriptor[0]); + 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++; @@ -942,14 +933,7 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, dex_pc, method_idx, invoke_type); - return HandleInvoke(invoke, - number_of_vreg_arguments, - args, - register_index, - is_range, - descriptor, - nullptr, /* clinit_check */ - true /* is_unresolved */); + return HandleInvoke(invoke, operands, shorty, /* is_unresolved */ true); } // Replace calls to String. with StringFactory. @@ -976,12 +960,7 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, invoke_type, target_method, HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit); - return HandleStringInit(invoke, - number_of_vreg_arguments, - args, - register_index, - is_range, - descriptor); + return HandleStringInit(invoke, operands, shorty); } // Potential class initialization check, in the case of a static method call. @@ -1041,42 +1020,39 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, resolved_method, ImTable::GetImtIndex(resolved_method)); } - - return HandleInvoke(invoke, - number_of_vreg_arguments, - args, - register_index, - is_range, - descriptor, - clinit_check, - false /* is_unresolved */); + return HandleInvoke(invoke, operands, 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, - uint32_t proto_idx, - uint32_t number_of_vreg_arguments, - bool is_range, - uint32_t* args, - uint32_t register_index) { - const char* descriptor = dex_file_->GetShorty(proto_idx); - DCHECK_EQ(1 + ArtMethod::NumArgRegisters(descriptor), number_of_vreg_arguments); - DataType::Type return_type = DataType::FromShorty(descriptor[0]); - size_t number_of_arguments = strlen(descriptor); + dex::ProtoIndex proto_idx, + const InstructionOperands& operands) { + 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, - number_of_vreg_arguments, - args, - register_index, - is_range, - 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) { @@ -1099,6 +1075,10 @@ HNewInstance* HInstructionBuilder::BuildNewInstance(dex::TypeIndex type_index, u if (load_class->NeedsAccessCheck() || klass->IsFinalizable() || !klass->IsInstantiable()) { entrypoint = kQuickAllocObjectWithChecks; } + // We will always be able to resolve the string class since it is in the BCP. + if (!klass.IsNull() && klass->IsStringClass()) { + entrypoint = kQuickAllocStringObject; + } // Consider classes we haven't resolved as potentially finalizable. bool finalizable = (klass == nullptr) || klass->IsFinalizable(); @@ -1167,32 +1147,48 @@ void HInstructionBuilder::BuildConstructorFenceForAllocation(HInstruction* alloc MethodCompilationStat::kConstructorFenceGeneratedNew); } -static bool IsSubClass(ObjPtr to_test, ObjPtr super_class) - REQUIRES_SHARED(Locks::mutator_lock_) { - return to_test != nullptr && !to_test->IsInterface() && to_test->IsSubClass(super_class); -} - bool HInstructionBuilder::IsInitialized(Handle cls) const { if (cls == nullptr) { return false; } - // `CanAssumeClassIsLoaded` will return true if we're JITting, or will - // check whether the class is in an image for the AOT compilation. - if (cls->IsInitialized() && - compiler_driver_->CanAssumeClassIsLoaded(cls.Get())) { - return true; + // Check if the class will be initialized at runtime. + if (cls->IsInitialized()) { + Runtime* runtime = Runtime::Current(); + if (!runtime->IsAotCompiler()) { + DCHECK(runtime->UseJitCompilation()); + // For JIT, the class cannot revert to an uninitialized state. + return true; + } + // Assume loaded only if klass is in the boot image. App classes cannot be assumed + // loaded because we don't even know what class loader will be used to load them. + const CompilerOptions& compiler_options = compiler_driver_->GetCompilerOptions(); + if (compiler_options.IsBootImage()) { + std::string temp; + const char* descriptor = cls->GetDescriptor(&temp); + if (compiler_options.IsImageClass(descriptor)) { + return true; + } + } else { + if (runtime->GetHeap()->FindSpaceFromObject(cls.Get(), false)->IsImageSpace()) { + return true; + } + } } - if (IsSubClass(GetOutermostCompilingClass(), cls.Get())) { + // We can avoid the class initialization check for `cls` only in static methods in the + // very same class. Instance methods of the same class can run on an escaped instance + // of an erroneous class. Even a superclass may need to be checked as the subclass + // can be completely initialized while the superclass is initializing and the subclass + // remains initialized when the superclass initializer throws afterwards. b/62478025 + // Note: The HClinitCheck+HInvokeStaticOrDirect merging can still apply. + if ((dex_compilation_unit_->GetAccessFlags() & kAccStatic) != 0u && + GetOutermostCompilingClass() == cls.Get()) { return true; } - // TODO: We should walk over the inlined methods, but we don't pass - // that information to the builder. - if (IsSubClass(GetCompilingClass(), cls.Get())) { - return true; - } + // Note: We could walk over the inlined methods to avoid allocating excessive + // `HClinitCheck`s in inlined static methods but they shall be eliminated by GVN. return false; } @@ -1222,26 +1218,22 @@ HClinitCheck* HInstructionBuilder::ProcessClinitCheckForInvoke( } bool HInstructionBuilder::SetupInvokeArguments(HInvoke* invoke, - uint32_t number_of_vreg_arguments, - uint32_t* args, - uint32_t register_index, - bool is_range, - const char* descriptor, + const InstructionOperands& operands, + 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 // dex registers given. If the instruction was seen as dead by the verifier, // it hasn't been properly checked. - (i < number_of_vreg_arguments) && (*argument_index < invoke->GetNumberOfArguments()); + (i < number_of_operands) && (*argument_index < invoke->GetNumberOfArguments()); i++, (*argument_index)++) { - DataType::Type type = DataType::FromShorty(descriptor[descriptor_index++]); + DataType::Type type = DataType::FromShorty(shorty[shorty_index++]); bool is_wide = (type == DataType::Type::kInt64) || (type == DataType::Type::kFloat64); - if (!is_range - && is_wide - && ((i + 1 == number_of_vreg_arguments) || (args[i] + 1 != args[i + 1]))) { + if (is_wide && ((i + 1 == number_of_operands) || + (operands.GetOperand(i) + 1 != operands.GetOperand(i + 1)))) { // Longs and doubles should be in pairs, that is, sequential registers. The verifier should // reject any class where this is violated. However, the verifier only does these checks // on non trivially dead instructions, so we just bailout the compilation. @@ -1252,7 +1244,7 @@ bool HInstructionBuilder::SetupInvokeArguments(HInvoke* invoke, MethodCompilationStat::kNotCompiledMalformedOpcode); return false; } - HInstruction* arg = LoadLocal(is_range ? register_index + i : args[i], type); + HInstruction* arg = LoadLocal(operands.GetOperand(i), type); invoke->SetArgumentAt(*argument_index, arg); if (is_wide) { i++; @@ -1279,19 +1271,16 @@ bool HInstructionBuilder::SetupInvokeArguments(HInvoke* invoke, } bool HInstructionBuilder::HandleInvoke(HInvoke* invoke, - uint32_t number_of_vreg_arguments, - uint32_t* args, - uint32_t register_index, - bool is_range, - const char* descriptor, - HClinitCheck* clinit_check, - bool is_unresolved) { + const InstructionOperands& operands, + const char* shorty, + bool is_unresolved, + HClinitCheck* clinit_check) { DCHECK(!invoke->IsInvokeStaticOrDirect() || !invoke->AsInvokeStaticOrDirect()->IsStringInit()); size_t start_index = 0; size_t argument_index = 0; if (invoke->GetInvokeType() != InvokeType::kStatic) { // Instance call. - uint32_t obj_reg = is_range ? register_index : args[0]; + uint32_t obj_reg = operands.GetOperand(0); HInstruction* arg = is_unresolved ? LoadLocal(obj_reg, DataType::Type::kReference) : LoadNullCheckedLocal(obj_reg, invoke->GetDexPc()); @@ -1300,14 +1289,7 @@ bool HInstructionBuilder::HandleInvoke(HInvoke* invoke, argument_index = 1; } - if (!SetupInvokeArguments(invoke, - number_of_vreg_arguments, - args, - register_index, - is_range, - descriptor, - start_index, - &argument_index)) { + if (!SetupInvokeArguments(invoke, operands, shorty, start_index, &argument_index)) { return false; } @@ -1327,24 +1309,14 @@ bool HInstructionBuilder::HandleInvoke(HInvoke* invoke, } bool HInstructionBuilder::HandleStringInit(HInvoke* invoke, - uint32_t number_of_vreg_arguments, - uint32_t* args, - uint32_t register_index, - bool is_range, - const char* descriptor) { + const InstructionOperands& operands, + const char* shorty) { DCHECK(invoke->IsInvokeStaticOrDirect()); DCHECK(invoke->AsInvokeStaticOrDirect()->IsStringInit()); size_t start_index = 1; size_t argument_index = 0; - if (!SetupInvokeArguments(invoke, - number_of_vreg_arguments, - args, - register_index, - is_range, - descriptor, - start_index, - &argument_index)) { + if (!SetupInvokeArguments(invoke, operands, shorty, start_index, &argument_index)) { return false; } @@ -1352,26 +1324,29 @@ bool HInstructionBuilder::HandleStringInit(HInvoke* invoke, // This is a StringFactory call, not an actual String constructor. Its result // replaces the empty String pre-allocated by NewInstance. - uint32_t orig_this_reg = is_range ? register_index : args[0]; + uint32_t orig_this_reg = operands.GetOperand(0); HInstruction* arg_this = LoadLocal(orig_this_reg, DataType::Type::kReference); // Replacing the NewInstance might render it redundant. Keep a list of these - // to be visited once it is clear whether it is has remaining uses. + // to be visited once it is clear whether it has remaining uses. if (arg_this->IsNewInstance()) { ssa_builder_->AddUninitializedString(arg_this->AsNewInstance()); + // Walk over all vregs and replace any occurrence of `arg_this` with `invoke`. + for (size_t vreg = 0, e = current_locals_->size(); vreg < e; ++vreg) { + if ((*current_locals_)[vreg] == arg_this) { + (*current_locals_)[vreg] = invoke; + } + } } else { DCHECK(arg_this->IsPhi()); - // NewInstance is not the direct input of the StringFactory call. It might - // be redundant but optimizing this case is not worth the effort. - } - - // Walk over all vregs and replace any occurrence of `arg_this` with `invoke`. - for (size_t vreg = 0, e = current_locals_->size(); vreg < e; ++vreg) { - if ((*current_locals_)[vreg] == arg_this) { - (*current_locals_)[vreg] = invoke; - } + // We can get a phi as input of a String. if there is a loop between the + // allocation and the String. call. As we don't know which other phis might alias + // with `arg_this`, we keep a record of these phis and will analyze their inputs and + // uses once the inputs and users are populated (in ssa_builder.cc). + // Note: we only do this for phis, as it is a somewhat more expensive operation than + // what we're doing above when the input is the `HNewInstance`. + ssa_builder_->AddUninitializedStringPhi(arg_this->AsPhi(), invoke); } - return true; } @@ -1699,11 +1674,9 @@ void HInstructionBuilder::BuildArrayAccess(const Instruction& instruction, HNewArray* HInstructionBuilder::BuildFilledNewArray(uint32_t dex_pc, dex::TypeIndex type_index, - uint32_t number_of_vreg_arguments, - bool is_range, - uint32_t* args, - uint32_t register_index) { - HInstruction* length = graph_->GetIntConstant(number_of_vreg_arguments, dex_pc); + const InstructionOperands& operands) { + const size_t number_of_operands = operands.GetNumberOfOperands(); + HInstruction* length = graph_->GetIntConstant(number_of_operands, dex_pc); HLoadClass* cls = BuildLoadClass(type_index, dex_pc); HNewArray* const object = new (allocator_) HNewArray(cls, length, dex_pc); AppendInstruction(object); @@ -1717,8 +1690,8 @@ HNewArray* HInstructionBuilder::BuildFilledNewArray(uint32_t dex_pc, bool is_reference_array = (primitive == 'L') || (primitive == '['); DataType::Type type = is_reference_array ? DataType::Type::kReference : DataType::Type::kInt32; - for (size_t i = 0; i < number_of_vreg_arguments; ++i) { - HInstruction* value = LoadLocal(is_range ? register_index + i : args[i], type); + for (size_t i = 0; i < number_of_operands; ++i) { + HInstruction* value = LoadLocal(operands.GetOperand(i), type); HInstruction* index = graph_->GetIntConstant(i, dex_pc); HArraySet* aset = new (allocator_) HArraySet(object, index, value, type, dex_pc); ssa_builder_->MaybeAddAmbiguousArraySet(aset); @@ -1815,35 +1788,11 @@ void HInstructionBuilder::BuildFillWideArrayData(HInstruction* object, } } -static TypeCheckKind ComputeTypeCheckKind(Handle cls) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (cls == nullptr) { - return TypeCheckKind::kUnresolvedCheck; - } else if (cls->IsInterface()) { - return TypeCheckKind::kInterfaceCheck; - } else if (cls->IsArrayClass()) { - if (cls->GetComponentType()->IsObjectClass()) { - return TypeCheckKind::kArrayObjectCheck; - } else if (cls->CannotBeAssignedFromOtherTypes()) { - return TypeCheckKind::kExactCheck; - } else { - return TypeCheckKind::kArrayCheck; - } - } else if (cls->IsFinal()) { - return TypeCheckKind::kExactCheck; - } else if (cls->IsAbstract()) { - return TypeCheckKind::kAbstractClassCheck; - } else { - return TypeCheckKind::kClassHierarchyCheck; - } -} - void HInstructionBuilder::BuildLoadString(dex::StringIndex string_index, uint32_t dex_pc) { HLoadString* load_string = new (allocator_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc); HSharpening::ProcessLoadString(load_string, code_generator_, - compiler_driver_, *dex_compilation_unit_, handles_); AppendInstruction(load_string); @@ -1852,22 +1801,8 @@ void HInstructionBuilder::BuildLoadString(dex::StringIndex string_index, uint32_ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint32_t dex_pc) { ScopedObjectAccess soa(Thread::Current()); const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); - Handle class_loader = dex_compilation_unit_->GetClassLoader(); - Handle klass = handles_->NewHandle(compiler_driver_->ResolveClass( - soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_)); - - bool needs_access_check = true; - if (klass != nullptr) { - if (klass->IsPublic()) { - needs_access_check = false; - } else { - ObjPtr compiling_class = GetCompilingClass(); - if (compiling_class != nullptr && compiling_class->CanAccess(klass.Get())) { - needs_access_check = false; - } - } - } - + Handle klass = ResolveClass(soa, type_index); + bool needs_access_check = LoadClassNeedsAccessCheck(klass); return BuildLoadClass(type_index, dex_file, klass, dex_pc, needs_access_check); } @@ -1899,7 +1834,6 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, HLoadClass::LoadKind load_kind = HSharpening::ComputeLoadClassKind(load_class, code_generator_, - compiler_driver_, *dex_compilation_unit_); if (load_kind == HLoadClass::LoadKind::kInvalid) { @@ -1912,35 +1846,102 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, return load_class; } +Handle HInstructionBuilder::ResolveClass(ScopedObjectAccess& soa, + dex::TypeIndex type_index) { + Handle class_loader = dex_compilation_unit_->GetClassLoader(); + ObjPtr klass = compiler_driver_->ResolveClass( + soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_); + // TODO: Avoid creating excessive handles if the method references the same class repeatedly. + // (Use a map on the local_allocator_.) + return handles_->NewHandle(klass); +} + +bool HInstructionBuilder::LoadClassNeedsAccessCheck(Handle klass) { + if (klass == nullptr) { + return true; + } else if (klass->IsPublic()) { + return false; + } else { + ObjPtr compiling_class = GetCompilingClass(); + return compiling_class == nullptr || !compiling_class->CanAccess(klass.Get()); + } +} + +void HInstructionBuilder::BuildLoadMethodHandle(uint16_t method_handle_index, uint32_t dex_pc) { + const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); + HLoadMethodHandle* load_method_handle = new (allocator_) HLoadMethodHandle( + graph_->GetCurrentMethod(), method_handle_index, dex_file, dex_pc); + AppendInstruction(load_method_handle); +} + +void HInstructionBuilder::BuildLoadMethodType(dex::ProtoIndex proto_index, uint32_t dex_pc) { + const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); + HLoadMethodType* load_method_type = + new (allocator_) HLoadMethodType(graph_->GetCurrentMethod(), proto_index, dex_file, dex_pc); + AppendInstruction(load_method_type); +} + void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, uint8_t destination, uint8_t reference, dex::TypeIndex type_index, uint32_t dex_pc) { HInstruction* object = LoadLocal(reference, DataType::Type::kReference); - HLoadClass* cls = BuildLoadClass(type_index, dex_pc); ScopedObjectAccess soa(Thread::Current()); - TypeCheckKind check_kind = ComputeTypeCheckKind(cls->GetClass()); + const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); + Handle klass = ResolveClass(soa, type_index); + bool needs_access_check = LoadClassNeedsAccessCheck(klass); + TypeCheckKind check_kind = HSharpening::ComputeTypeCheckKind( + klass.Get(), code_generator_, needs_access_check); + + HInstruction* class_or_null = nullptr; + HIntConstant* bitstring_path_to_root = nullptr; + HIntConstant* bitstring_mask = nullptr; + if (check_kind == TypeCheckKind::kBitstringCheck) { + // TODO: Allow using the bitstring check also if we need an access check. + DCHECK(!needs_access_check); + class_or_null = graph_->GetNullConstant(dex_pc); + MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); + uint32_t path_to_root = + SubtypeCheck>::GetEncodedPathToRootForTarget(klass.Get()); + uint32_t mask = SubtypeCheck>::GetEncodedPathToRootMask(klass.Get()); + bitstring_path_to_root = graph_->GetIntConstant(static_cast(path_to_root), dex_pc); + bitstring_mask = graph_->GetIntConstant(static_cast(mask), dex_pc); + } else { + class_or_null = BuildLoadClass(type_index, dex_file, klass, dex_pc, needs_access_check); + } + DCHECK(class_or_null != nullptr); + if (instruction.Opcode() == Instruction::INSTANCE_OF) { - AppendInstruction(new (allocator_) HInstanceOf(object, cls, check_kind, dex_pc)); + AppendInstruction(new (allocator_) HInstanceOf(object, + class_or_null, + check_kind, + klass, + dex_pc, + allocator_, + bitstring_path_to_root, + bitstring_mask)); UpdateLocal(destination, current_block_->GetLastInstruction()); } else { DCHECK_EQ(instruction.Opcode(), Instruction::CHECK_CAST); // We emit a CheckCast followed by a BoundType. CheckCast is a statement // which may throw. If it succeeds BoundType sets the new type of `object` // for all subsequent uses. - AppendInstruction(new (allocator_) HCheckCast(object, cls, check_kind, dex_pc)); + AppendInstruction( + new (allocator_) HCheckCast(object, + class_or_null, + check_kind, + klass, + dex_pc, + allocator_, + bitstring_path_to_root, + bitstring_mask)); AppendInstruction(new (allocator_) HBoundType(object, dex_pc)); UpdateLocal(reference, current_block_->GetLastInstruction()); } } -bool HInstructionBuilder::NeedsAccessCheck(dex::TypeIndex type_index, bool* finalizable) const { - return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks( - LookupReferrerClass(), LookupResolvedType(type_index, *dex_compilation_unit_), finalizable); -} - bool HInstructionBuilder::CanDecodeQuickenedInfo() const { return !quicken_info_.IsNull(); } @@ -2116,11 +2117,10 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, } else { method_idx = instruction.VRegB_35c(); } - uint32_t number_of_vreg_arguments = instruction.VRegA_35c(); uint32_t args[5]; - instruction.GetVarArgs(args); - if (!BuildInvoke(instruction, dex_pc, method_idx, - number_of_vreg_arguments, false, args, -1)) { + uint32_t number_of_vreg_arguments = instruction.GetVarArgs(args); + VarArgsInstructionOperands operands(args, number_of_vreg_arguments); + if (!BuildInvoke(instruction, dex_pc, method_idx, operands)) { return false; } break; @@ -2143,10 +2143,8 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, } else { method_idx = instruction.VRegB_3rc(); } - uint32_t number_of_vreg_arguments = instruction.VRegA_3rc(); - uint32_t register_index = instruction.VRegC(); - if (!BuildInvoke(instruction, dex_pc, method_idx, - number_of_vreg_arguments, true, nullptr, register_index)) { + RangeInstructionOperands operands(instruction.VRegC(), instruction.VRegA_3rc()); + if (!BuildInvoke(instruction, dex_pc, method_idx, operands)) { return false; } break; @@ -2154,33 +2152,32 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::INVOKE_POLYMORPHIC: { uint16_t method_idx = instruction.VRegB_45cc(); - uint16_t proto_idx = instruction.VRegH_45cc(); - uint32_t number_of_vreg_arguments = instruction.VRegA_45cc(); + dex::ProtoIndex proto_idx(instruction.VRegH_45cc()); uint32_t args[5]; - instruction.GetVarArgs(args); - return BuildInvokePolymorphic(instruction, - dex_pc, - method_idx, - proto_idx, - number_of_vreg_arguments, - false, - args, - -1); + uint32_t number_of_vreg_arguments = instruction.GetVarArgs(args); + VarArgsInstructionOperands operands(args, number_of_vreg_arguments); + return BuildInvokePolymorphic(dex_pc, method_idx, proto_idx, operands); } case Instruction::INVOKE_POLYMORPHIC_RANGE: { uint16_t method_idx = instruction.VRegB_4rcc(); - uint16_t proto_idx = instruction.VRegH_4rcc(); - uint32_t number_of_vreg_arguments = instruction.VRegA_4rcc(); - uint32_t register_index = instruction.VRegC_4rcc(); - return BuildInvokePolymorphic(instruction, - dex_pc, - method_idx, - proto_idx, - number_of_vreg_arguments, - true, - nullptr, - register_index); + dex::ProtoIndex proto_idx(instruction.VRegH_4rcc()); + RangeInstructionOperands operands(instruction.VRegC_4rcc(), instruction.VRegA_4rcc()); + return BuildInvokePolymorphic(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: { @@ -2728,30 +2725,19 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, } case Instruction::FILLED_NEW_ARRAY: { - uint32_t number_of_vreg_arguments = instruction.VRegA_35c(); dex::TypeIndex type_index(instruction.VRegB_35c()); uint32_t args[5]; - instruction.GetVarArgs(args); - HNewArray* new_array = BuildFilledNewArray(dex_pc, - type_index, - number_of_vreg_arguments, - /* is_range */ false, - args, - /* register_index */ 0); + uint32_t number_of_vreg_arguments = instruction.GetVarArgs(args); + VarArgsInstructionOperands operands(args, number_of_vreg_arguments); + HNewArray* new_array = BuildFilledNewArray(dex_pc, type_index, operands); BuildConstructorFenceForAllocation(new_array); break; } case Instruction::FILLED_NEW_ARRAY_RANGE: { - uint32_t number_of_vreg_arguments = instruction.VRegA_3rc(); dex::TypeIndex type_index(instruction.VRegB_3rc()); - uint32_t register_index = instruction.VRegC_3rc(); - HNewArray* new_array = BuildFilledNewArray(dex_pc, - type_index, - number_of_vreg_arguments, - /* is_range */ true, - /* args*/ nullptr, - register_index); + RangeInstructionOperands operands(instruction.VRegC_3rc(), instruction.VRegA_3rc()); + HNewArray* new_array = BuildFilledNewArray(dex_pc, type_index, operands); BuildConstructorFenceForAllocation(new_array); break; } @@ -2906,6 +2892,20 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, break; } + case Instruction::CONST_METHOD_HANDLE: { + uint16_t method_handle_idx = instruction.VRegB_21c(); + BuildLoadMethodHandle(method_handle_idx, dex_pc); + UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction()); + break; + } + + case Instruction::CONST_METHOD_TYPE: { + dex::ProtoIndex proto_idx(instruction.VRegB_21c()); + BuildLoadMethodType(proto_idx, dex_pc); + UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction()); + break; + } + case Instruction::MOVE_EXCEPTION: { AppendInstruction(new (allocator_) HLoadException(dex_pc)); UpdateLocal(instruction.VRegA_11x(), current_block_->GetLastInstruction()); @@ -2959,7 +2959,21 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, 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 " @@ -2967,6 +2981,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, 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 4428c532779d5a81a7437d0e70590aed6f8ad3c4..578172a18e15e0731eaa670e954f88e273e9979f 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -38,12 +38,15 @@ class CompilerDriver; class DexCompilationUnit; class HBasicBlockBuilder; class Instruction; +class InstructionOperands; class OptimizingCompilerStats; +class ScopedObjectAccess; class SsaBuilder; class VariableSizedHandleScope; namespace mirror { class Class; +class MethodType; } // namespace mirror class HInstructionBuilder : public ValueObject { @@ -95,11 +98,6 @@ class HInstructionBuilder : public ValueObject { void InitializeParameters(); - // Returns whether the current method needs access check for the type. - // Output parameter finalizable is set to whether the type is finalizable. - bool NeedsAccessCheck(dex::TypeIndex type_index, /*out*/bool* finalizable) const - REQUIRES_SHARED(Locks::mutator_lock_); - template void Unop_12x(const Instruction& instruction, DataType::Type type, uint32_t dex_pc); @@ -166,29 +164,25 @@ class HInstructionBuilder : public ValueObject { bool BuildInvoke(const Instruction& instruction, uint32_t dex_pc, uint32_t method_idx, - uint32_t number_of_vreg_arguments, - bool is_range, - uint32_t* args, - uint32_t register_index); + const InstructionOperands& operands); // Builds an invocation node for invoke-polymorphic and returns whether the // instruction is supported. - bool BuildInvokePolymorphic(const Instruction& instruction, - uint32_t dex_pc, + bool BuildInvokePolymorphic(uint32_t dex_pc, uint32_t method_idx, - uint32_t proto_idx, - uint32_t number_of_vreg_arguments, - bool is_range, - uint32_t* args, - uint32_t register_index); + dex::ProtoIndex proto_idx, + const InstructionOperands& operands); + + // Builds 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, - uint32_t number_of_vreg_arguments, - bool is_range, - uint32_t* args, - uint32_t register_index); + const InstructionOperands& operands); void BuildFillArrayData(const Instruction& instruction, uint32_t dex_pc); @@ -232,6 +226,18 @@ class HInstructionBuilder : public ValueObject { bool needs_access_check) REQUIRES_SHARED(Locks::mutator_lock_); + Handle ResolveClass(ScopedObjectAccess& soa, dex::TypeIndex type_index) + REQUIRES_SHARED(Locks::mutator_lock_); + + bool LoadClassNeedsAccessCheck(Handle klass) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Builds a `HLoadMethodHandle` loading the given `method_handle_index`. + void BuildLoadMethodHandle(uint16_t method_handle_idx, uint32_t dex_pc); + + // Builds a `HLoadMethodType` loading the given `proto_index`. + void BuildLoadMethodType(dex::ProtoIndex proto_index, uint32_t dex_pc); + // Returns the outer-most compiling method's class. ObjPtr GetOutermostCompilingClass() const; @@ -246,29 +252,20 @@ class HInstructionBuilder : public ValueObject { HInvoke* invoke); bool SetupInvokeArguments(HInvoke* invoke, - uint32_t number_of_vreg_arguments, - uint32_t* args, - uint32_t register_index, - bool is_range, - const char* descriptor, + const InstructionOperands& operands, + const char* shorty, size_t start_index, size_t* argument_index); bool HandleInvoke(HInvoke* invoke, - uint32_t number_of_vreg_arguments, - uint32_t* args, - uint32_t register_index, - bool is_range, - const char* descriptor, - HClinitCheck* clinit_check, - bool is_unresolved); + const InstructionOperands& operands, + const char* shorty, + bool is_unresolved, + HClinitCheck* clinit_check = nullptr); bool HandleStringInit(HInvoke* invoke, - uint32_t number_of_vreg_arguments, - uint32_t* args, - uint32_t register_index, - bool is_range, - const char* descriptor); + const InstructionOperands& operands, + const char* shorty); void HandleStringInitResult(HInvokeStaticOrDirect* invoke); HClinitCheck* ProcessClinitCheckForInvoke( @@ -285,8 +282,7 @@ class HInstructionBuilder : public ValueObject { void BuildConstructorFenceForAllocation(HInstruction* allocation); // Return whether the compiler can assume `cls` is initialized. - bool IsInitialized(Handle cls) const - REQUIRES_SHARED(Locks::mutator_lock_); + bool IsInitialized(Handle cls) const REQUIRES_SHARED(Locks::mutator_lock_); // Try to resolve a method using the class linker. Return null if a method could // not be resolved. diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index a42a85dc1d28af42cb8544e1ec8e5256763fbec0..70af49f8f038148a111841b65e2335f8125ec933 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -18,6 +18,7 @@ #include "art_method-inl.h" #include "class_linker-inl.h" +#include "class_root.h" #include "data_type-inl.h" #include "escape.h" #include "intrinsics.h" @@ -35,14 +36,12 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { public: InstructionSimplifierVisitor(HGraph* graph, CodeGenerator* codegen, - CompilerDriver* compiler_driver, OptimizingCompilerStats* stats) : HGraphDelegateVisitor(graph), codegen_(codegen), - compiler_driver_(compiler_driver), stats_(stats) {} - void Run(); + bool Run(); private: void RecordSimplification() { @@ -67,7 +66,6 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { bool TryCombineVecMultiplyAccumulate(HVecMul* mul); void VisitShift(HBinaryOperation* shift); - void VisitEqual(HEqual* equal) OVERRIDE; void VisitNotEqual(HNotEqual* equal) OVERRIDE; void VisitBooleanNot(HBooleanNot* bool_not) OVERRIDE; @@ -78,6 +76,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void VisitNullCheck(HNullCheck* instruction) OVERRIDE; void VisitArrayLength(HArrayLength* instruction) OVERRIDE; void VisitCheckCast(HCheckCast* instruction) OVERRIDE; + void VisitAbs(HAbs* instruction) OVERRIDE; void VisitAdd(HAdd* instruction) OVERRIDE; void VisitAnd(HAnd* instruction) OVERRIDE; void VisitCondition(HCondition* instruction) OVERRIDE; @@ -116,13 +115,16 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void SimplifyFP2Int(HInvoke* invoke); void SimplifyStringCharAt(HInvoke* invoke); void SimplifyStringIsEmptyOrLength(HInvoke* invoke); + void SimplifyStringIndexOf(HInvoke* invoke); void SimplifyNPEOnArgN(HInvoke* invoke, size_t); void SimplifyReturnThis(HInvoke* invoke); void SimplifyAllocationIntrinsic(HInvoke* invoke); void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind); + void SimplifyMin(HInvoke* invoke, DataType::Type type); + void SimplifyMax(HInvoke* invoke, DataType::Type type); + void SimplifyAbs(HInvoke* invoke, DataType::Type type); CodeGenerator* codegen_; - CompilerDriver* compiler_driver_; OptimizingCompilerStats* stats_; bool simplification_occurred_ = false; int simplifications_at_current_position_ = 0; @@ -133,17 +135,18 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { static constexpr int kMaxSamePositionSimplifications = 50; }; -void InstructionSimplifier::Run() { +bool InstructionSimplifier::Run() { if (kTestInstructionClonerExhaustively) { CloneAndReplaceInstructionVisitor visitor(graph_); visitor.VisitReversePostOrder(); } - InstructionSimplifierVisitor visitor(graph_, codegen_, compiler_driver_, stats_); - visitor.Run(); + InstructionSimplifierVisitor visitor(graph_, codegen_, stats_); + return visitor.Run(); } -void InstructionSimplifierVisitor::Run() { +bool InstructionSimplifierVisitor::Run() { + bool didSimplify = false; // Iterate in reverse post order to open up more simplifications to users // of instructions that got simplified. for (HBasicBlock* block : GetGraph()->GetReversePostOrder()) { @@ -153,10 +156,14 @@ void InstructionSimplifierVisitor::Run() { do { simplification_occurred_ = false; VisitBasicBlock(block); + if (simplification_occurred_) { + didSimplify = true; + } } while (simplification_occurred_ && (simplifications_at_current_position_ < kMaxSamePositionSimplifications)); simplifications_at_current_position_ = 0; } + return didSimplify; } namespace { @@ -576,7 +583,9 @@ bool InstructionSimplifierVisitor::CanEnsureNotNullAt(HInstruction* input, HInst // Returns whether doing a type test between the class of `object` against `klass` has // a statically known outcome. The result of the test is stored in `outcome`. -static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bool* outcome) { +static bool TypeCheckHasKnownOutcome(ReferenceTypeInfo class_rti, + HInstruction* object, + /*out*/bool* outcome) { DCHECK(!object->IsNullConstant()) << "Null constants should be special cased"; ReferenceTypeInfo obj_rti = object->GetReferenceTypeInfo(); ScopedObjectAccess soa(Thread::Current()); @@ -586,7 +595,6 @@ static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bo return false; } - ReferenceTypeInfo class_rti = klass->GetLoadedClassRTI(); if (!class_rti.IsValid()) { // Happens when the loaded class is unresolved. return false; @@ -611,8 +619,8 @@ static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bo void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { HInstruction* object = check_cast->InputAt(0); - HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass(); - if (load_class->NeedsAccessCheck()) { + if (check_cast->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck && + check_cast->GetTargetClass()->NeedsAccessCheck()) { // If we need to perform an access check we cannot remove the instruction. return; } @@ -627,18 +635,21 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { 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(load_class, object, &outcome)) { + if (TypeCheckHasKnownOutcome(check_cast->GetTargetClassRTI(), object, &outcome)) { if (outcome) { check_cast->GetBlock()->RemoveInstruction(check_cast); MaybeRecordStat(stats_, MethodCompilationStat::kRemovedCheckedCast); - if (!load_class->HasUses()) { - // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. - // However, here we know that it cannot because the checkcast was successfull, hence - // the class was already loaded. - load_class->GetBlock()->RemoveInstruction(load_class); + if (check_cast->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck) { + HLoadClass* load_class = check_cast->GetTargetClass(); + if (!load_class->HasUses()) { + // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. + // However, here we know that it cannot because the checkcast was successfull, hence + // the class was already loaded. + load_class->GetBlock()->RemoveInstruction(load_class); + } } } else { // Don't do anything for exceptional cases for now. Ideally we should remove @@ -649,8 +660,8 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { HInstruction* object = instruction->InputAt(0); - HLoadClass* load_class = instruction->InputAt(1)->AsLoadClass(); - if (load_class->NeedsAccessCheck()) { + if (instruction->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck && + instruction->GetTargetClass()->NeedsAccessCheck()) { // If we need to perform an access check we cannot remove the instruction. return; } @@ -670,10 +681,10 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { 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(load_class, object, &outcome)) { + if (TypeCheckHasKnownOutcome(instruction->GetTargetClassRTI(), object, &outcome)) { MaybeRecordStat(stats_, MethodCompilationStat::kRemovedInstanceOf); if (outcome && can_be_null) { // Type test will succeed, we just need a null test. @@ -686,11 +697,14 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { } RecordSimplification(); instruction->GetBlock()->RemoveInstruction(instruction); - if (outcome && !load_class->HasUses()) { - // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. - // However, here we know that it cannot because the instanceof check was successfull, hence - // the class was already loaded. - load_class->GetBlock()->RemoveInstruction(load_class); + if (outcome && instruction->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck) { + HLoadClass* load_class = instruction->GetTargetClass(); + if (!load_class->HasUses()) { + // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. + // However, here we know that it cannot because the instanceof check was successfull, hence + // the class was already loaded. + load_class->GetBlock()->RemoveInstruction(load_class); + } } } } @@ -849,35 +863,29 @@ void InstructionSimplifierVisitor::VisitBooleanNot(HBooleanNot* bool_not) { static HInstruction* NewIntegralAbs(ArenaAllocator* allocator, HInstruction* x, HInstruction* cursor) { - DataType::Type type = x->GetType(); - DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64); - // Construct a fake intrinsic with as much context as is needed to allocate one. - // The intrinsic will always be lowered into code later anyway. - // TODO: b/65164101 : moving towards a real HAbs node makes more sense. - HInvokeStaticOrDirect::DispatchInfo dispatch_info = { - HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress, - HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod, - 0u - }; - HInvokeStaticOrDirect* invoke = new (allocator) HInvokeStaticOrDirect( - allocator, - 1, - type, - x->GetDexPc(), - /*method_idx*/ -1, - /*resolved_method*/ nullptr, - dispatch_info, - kStatic, - MethodReference(nullptr, dex::kDexNoIndex), - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); - invoke->SetArgumentAt(0, x); - invoke->SetIntrinsic(type == DataType::Type::kInt32 ? Intrinsics::kMathAbsInt - : Intrinsics::kMathAbsLong, - kNoEnvironmentOrCache, - kNoSideEffects, - kNoThrow); - cursor->GetBlock()->InsertInstructionBefore(invoke, cursor); - return invoke; + DataType::Type type = DataType::Kind(x->GetType()); + DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64); + HAbs* abs = new (allocator) HAbs(type, x, cursor->GetDexPc()); + cursor->GetBlock()->InsertInstructionBefore(abs, cursor); + return abs; +} + +// Constructs a new MIN/MAX(x, y) node in the HIR. +static HInstruction* NewIntegralMinMax(ArenaAllocator* allocator, + HInstruction* x, + HInstruction* y, + HInstruction* cursor, + bool is_min) { + DataType::Type type = DataType::Kind(x->GetType()); + DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64); + HBinaryOperation* minmax = nullptr; + if (is_min) { + minmax = new (allocator) HMin(type, x, y, cursor->GetDexPc()); + } else { + minmax = new (allocator) HMax(type, x, y, cursor->GetDexPc()); + } + cursor->GetBlock()->InsertInstructionBefore(minmax, cursor); + return minmax; } // Returns true if operands a and b consists of widening type conversions @@ -899,6 +907,30 @@ static bool AreLowerPrecisionArgs(DataType::Type to_type, HInstruction* a, HInst to_type == DataType::Type::kInt64); } +// Returns an acceptable substitution for "a" on the select +// construct "a b ? c : .." during MIN/MAX recognition. +static HInstruction* AllowInMinMax(IfCondition cmp, + HInstruction* a, + HInstruction* b, + HInstruction* c) { + int64_t value = 0; + if (IsInt64AndGet(b, /*out*/ &value) && + (((cmp == kCondLT || cmp == kCondLE) && c->IsMax()) || + ((cmp == kCondGT || cmp == kCondGE) && c->IsMin()))) { + HConstant* other = c->AsBinaryOperation()->GetConstantRight(); + if (other != nullptr && a == c->AsBinaryOperation()->GetLeastConstantLeft()) { + int64_t other_value = Int64FromConstant(other); + bool is_max = (cmp == kCondLT || cmp == kCondLE); + // Allow the max for a < 100 ? max(a, -100) : .. + // or the min for a > -100 ? min(a, 100) : .. + if (is_max ? (value >= other_value) : (value <= other_value)) { + return c; + } + } + } + return nullptr; +} + void InstructionSimplifierVisitor::VisitSelect(HSelect* select) { HInstruction* replace_with = nullptr; HInstruction* condition = select->GetCondition(); @@ -942,23 +974,35 @@ void InstructionSimplifierVisitor::VisitSelect(HSelect* select) { DataType::Type t_type = true_value->GetType(); DataType::Type f_type = false_value->GetType(); // Here we have a b ? true_value : false_value. - // Test if both values are same-typed int or long. - if (t_type == f_type && - (t_type == DataType::Type::kInt32 || t_type == DataType::Type::kInt64)) { - // Try to replace typical integral ABS constructs. - if (true_value->IsNeg()) { - HInstruction* negated = true_value->InputAt(0); - if ((cmp == kCondLT || cmp == kCondLE) && - (a == negated && a == false_value && IsInt64Value(b, 0))) { - // Found a < 0 ? -a : a which can be replaced by ABS(a). - replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), false_value, select); - } - } else if (false_value->IsNeg()) { - HInstruction* negated = false_value->InputAt(0); - if ((cmp == kCondGT || cmp == kCondGE) && - (a == true_value && a == negated && IsInt64Value(b, 0))) { - // Found a > 0 ? a : -a which can be replaced by ABS(a). - replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), true_value, select); + // Test if both values are compatible integral types (resulting MIN/MAX/ABS + // type will be int or long, like the condition). Replacements are general, + // but assume conditions prefer constants on the right. + if (DataType::IsIntegralType(t_type) && DataType::Kind(t_type) == DataType::Kind(f_type)) { + // Allow a < 100 ? max(a, -100) : .. + // or a > -100 ? min(a, 100) : .. + // to use min/max instead of a to detect nested min/max expressions. + HInstruction* new_a = AllowInMinMax(cmp, a, b, true_value); + if (new_a != nullptr) { + a = new_a; + } + // Try to replace typical integral MIN/MAX/ABS constructs. + if ((cmp == kCondLT || cmp == kCondLE || cmp == kCondGT || cmp == kCondGE) && + ((a == true_value && b == false_value) || + (b == true_value && a == false_value))) { + // Found a < b ? a : b (MIN) or a < b ? b : a (MAX) + // or a > b ? a : b (MAX) or a > b ? b : a (MIN). + bool is_min = (cmp == kCondLT || cmp == kCondLE) == (a == true_value); + replace_with = NewIntegralMinMax(GetGraph()->GetAllocator(), a, b, select, is_min); + } else if (((cmp == kCondLT || cmp == kCondLE) && true_value->IsNeg()) || + ((cmp == kCondGT || cmp == kCondGE) && false_value->IsNeg())) { + bool negLeft = (cmp == kCondLT || cmp == kCondLE); + HInstruction* the_negated = negLeft ? true_value->InputAt(0) : false_value->InputAt(0); + HInstruction* not_negated = negLeft ? false_value : true_value; + if (a == the_negated && a == not_negated && IsInt64Value(b, 0)) { + // Found a < 0 ? -a : a + // or a > 0 ? a : -a + // which can be replaced by ABS(a). + replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), a, select); } } else if (true_value->IsSub() && false_value->IsSub()) { HInstruction* true_sub1 = true_value->InputAt(0); @@ -970,8 +1014,8 @@ void InstructionSimplifierVisitor::VisitSelect(HSelect* select) { ((cmp == kCondLT || cmp == kCondLE) && (a == true_sub2 && b == true_sub1 && a == false_sub1 && b == false_sub2))) && AreLowerPrecisionArgs(t_type, a, b)) { - // Found a > b ? a - b : b - a or - // a < b ? b - a : a - b + // Found a > b ? a - b : b - a + // or a < b ? b - a : a - b // which can be replaced by ABS(a - b) for lower precision operands a, b. replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), true_value, select); } @@ -1230,6 +1274,17 @@ void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruct } } +void InstructionSimplifierVisitor::VisitAbs(HAbs* instruction) { + HInstruction* input = instruction->GetInput(); + if (DataType::IsZeroExtension(input->GetType(), instruction->GetResultType())) { + // Zero extension from narrow to wide can never set sign bit in the wider + // operand, making the subsequent Abs redundant (e.g., abs(b & 0xff) for byte b). + instruction->ReplaceWith(input); + instruction->GetBlock()->RemoveInstruction(instruction); + RecordSimplification(); + } +} + void InstructionSimplifierVisitor::VisitAdd(HAdd* instruction) { HConstant* input_cst = instruction->GetConstantRight(); HInstruction* input_other = instruction->GetLeastConstantLeft(); @@ -1507,8 +1562,7 @@ static bool RecognizeAndSimplifyClassCheck(HCondition* condition) { { ScopedObjectAccess soa(Thread::Current()); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - ArtField* field = class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0); + ArtField* field = GetClassRoot()->GetInstanceField(0); DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_"); if (field_get->GetFieldInfo().GetField() != field) { return false; @@ -2252,7 +2306,7 @@ void InstructionSimplifierVisitor::SimplifySystemArrayCopy(HInvoke* instruction) // the invoke, as we would need to look it up in the current dex file, and it // is unlikely that it exists. The most usual situation for such typed // arraycopy methods is a direct pointer to the boot image. - HSharpening::SharpenInvokeStaticOrDirect(invoke, codegen_, compiler_driver_); + HSharpening::SharpenInvokeStaticOrDirect(invoke, codegen_); } } } @@ -2361,6 +2415,43 @@ void InstructionSimplifierVisitor::SimplifyStringIsEmptyOrLength(HInvoke* invoke invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, replacement); } +void InstructionSimplifierVisitor::SimplifyStringIndexOf(HInvoke* invoke) { + DCHECK(invoke->GetIntrinsic() == Intrinsics::kStringIndexOf || + invoke->GetIntrinsic() == Intrinsics::kStringIndexOfAfter); + if (invoke->InputAt(0)->IsLoadString()) { + HLoadString* load_string = invoke->InputAt(0)->AsLoadString(); + const DexFile& dex_file = load_string->GetDexFile(); + uint32_t utf16_length; + const char* data = + dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), &utf16_length); + if (utf16_length == 0) { + invoke->ReplaceWith(GetGraph()->GetIntConstant(-1)); + invoke->GetBlock()->RemoveInstruction(invoke); + RecordSimplification(); + return; + } + if (utf16_length == 1 && invoke->GetIntrinsic() == Intrinsics::kStringIndexOf) { + // Simplify to HSelect(HEquals(., load_string.charAt(0)), 0, -1). + // If the sought character is supplementary, this gives the correct result, i.e. -1. + uint32_t c = GetUtf16FromUtf8(&data); + DCHECK_EQ(GetTrailingUtf16Char(c), 0u); + DCHECK_EQ(GetLeadingUtf16Char(c), c); + uint32_t dex_pc = invoke->GetDexPc(); + ArenaAllocator* allocator = GetGraph()->GetAllocator(); + HEqual* equal = + new (allocator) HEqual(invoke->InputAt(1), GetGraph()->GetIntConstant(c), dex_pc); + invoke->GetBlock()->InsertInstructionBefore(equal, invoke); + HSelect* result = new (allocator) HSelect(equal, + GetGraph()->GetIntConstant(0), + GetGraph()->GetIntConstant(-1), + dex_pc); + invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, result); + RecordSimplification(); + return; + } + } +} + // This method should only be used on intrinsics whose sole way of throwing an // exception is raising a NPE when the nth argument is null. If that argument // is provably non-null, we can clear the flag. @@ -2430,6 +2521,27 @@ void InstructionSimplifierVisitor::SimplifyMemBarrier(HInvoke* invoke, invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, mem_barrier); } +void InstructionSimplifierVisitor::SimplifyMin(HInvoke* invoke, DataType::Type type) { + DCHECK(invoke->IsInvokeStaticOrDirect()); + HMin* min = new (GetGraph()->GetAllocator()) + HMin(type, invoke->InputAt(0), invoke->InputAt(1), invoke->GetDexPc()); + invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, min); +} + +void InstructionSimplifierVisitor::SimplifyMax(HInvoke* invoke, DataType::Type type) { + DCHECK(invoke->IsInvokeStaticOrDirect()); + HMax* max = new (GetGraph()->GetAllocator()) + HMax(type, invoke->InputAt(0), invoke->InputAt(1), invoke->GetDexPc()); + invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, max); +} + +void InstructionSimplifierVisitor::SimplifyAbs(HInvoke* invoke, DataType::Type type) { + DCHECK(invoke->IsInvokeStaticOrDirect()); + HAbs* abs = new (GetGraph()->GetAllocator()) + HAbs(type, invoke->InputAt(0), invoke->GetDexPc()); + invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, abs); +} + void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { switch (instruction->GetIntrinsic()) { case Intrinsics::kStringEquals: @@ -2477,6 +2589,10 @@ void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { case Intrinsics::kStringLength: SimplifyStringIsEmptyOrLength(instruction); break; + case Intrinsics::kStringIndexOf: + case Intrinsics::kStringIndexOfAfter: + SimplifyStringIndexOf(instruction); + break; case Intrinsics::kStringStringIndexOf: case Intrinsics::kStringStringIndexOfAfter: SimplifyNPEOnArgN(instruction, 1); // 0th has own NullCheck @@ -2513,6 +2629,42 @@ void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { case Intrinsics::kVarHandleStoreStoreFence: SimplifyMemBarrier(instruction, MemBarrierKind::kStoreStore); break; + case Intrinsics::kMathMinIntInt: + SimplifyMin(instruction, DataType::Type::kInt32); + break; + case Intrinsics::kMathMinLongLong: + SimplifyMin(instruction, DataType::Type::kInt64); + break; + case Intrinsics::kMathMinFloatFloat: + SimplifyMin(instruction, DataType::Type::kFloat32); + break; + case Intrinsics::kMathMinDoubleDouble: + SimplifyMin(instruction, DataType::Type::kFloat64); + break; + case Intrinsics::kMathMaxIntInt: + SimplifyMax(instruction, DataType::Type::kInt32); + break; + case Intrinsics::kMathMaxLongLong: + SimplifyMax(instruction, DataType::Type::kInt64); + break; + case Intrinsics::kMathMaxFloatFloat: + SimplifyMax(instruction, DataType::Type::kFloat32); + break; + case Intrinsics::kMathMaxDoubleDouble: + SimplifyMax(instruction, DataType::Type::kFloat64); + break; + case Intrinsics::kMathAbsInt: + SimplifyAbs(instruction, DataType::Type::kInt32); + break; + case Intrinsics::kMathAbsLong: + SimplifyAbs(instruction, DataType::Type::kInt64); + break; + case Intrinsics::kMathAbsFloat: + SimplifyAbs(instruction, DataType::Type::kFloat32); + break; + case Intrinsics::kMathAbsDouble: + SimplifyAbs(instruction, DataType::Type::kFloat64); + break; default: break; } @@ -2553,10 +2705,10 @@ bool InstructionSimplifierVisitor::TryHandleAssociativeAndCommutativeOperation( HConstant* const2; HBinaryOperation* y; - if (instruction->InstructionTypeEquals(left) && right->IsConstant()) { + if (instruction->GetKind() == left->GetKind() && right->IsConstant()) { const2 = right->AsConstant(); y = left->AsBinaryOperation(); - } else if (left->IsConstant() && instruction->InstructionTypeEquals(right)) { + } else if (left->IsConstant() && instruction->GetKind() == right->GetKind()) { const2 = left->AsConstant(); y = right->AsBinaryOperation(); } else { diff --git a/compiler/optimizing/instruction_simplifier.h b/compiler/optimizing/instruction_simplifier.h index 5e2045580bcb1254ccb1002f2c2737d52825dca9..2d134e0067754b9bc68d2f223482ffbd93f33bad 100644 --- a/compiler/optimizing/instruction_simplifier.h +++ b/compiler/optimizing/instruction_simplifier.h @@ -24,7 +24,6 @@ namespace art { class CodeGenerator; -class CompilerDriver; /** * Implements optimizations specific to each instruction. @@ -40,20 +39,17 @@ class InstructionSimplifier : public HOptimization { public: InstructionSimplifier(HGraph* graph, CodeGenerator* codegen, - CompilerDriver* compiler_driver, OptimizingCompilerStats* stats = nullptr, const char* name = kInstructionSimplifierPassName) : HOptimization(graph, name, stats), - codegen_(codegen), - compiler_driver_(compiler_driver) {} + codegen_(codegen) {} static constexpr const char* kInstructionSimplifierPassName = "instruction_simplifier"; - void Run() OVERRIDE; + bool Run() OVERRIDE; private: CodeGenerator* codegen_; - CompilerDriver* compiler_driver_; DISALLOW_COPY_AND_ASSIGN(InstructionSimplifier); }; diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc index 92081e30b1085191c66beae7904bd6f5c7855091..37fcdb9d5c70ab1f97ec32f7509f8d0f78e93738 100644 --- a/compiler/optimizing/instruction_simplifier_arm.cc +++ b/compiler/optimizing/instruction_simplifier_arm.cc @@ -283,9 +283,10 @@ void InstructionSimplifierArmVisitor::VisitUShr(HUShr* instruction) { } } -void InstructionSimplifierArm::Run() { +bool InstructionSimplifierArm::Run() { InstructionSimplifierArmVisitor visitor(graph_, stats_); visitor.VisitReversePostOrder(); + return true; } } // namespace arm diff --git a/compiler/optimizing/instruction_simplifier_arm.h b/compiler/optimizing/instruction_simplifier_arm.h index 2f6572931f1d6fa3b1ed28efa47e2954a5e48ab8..f1a16efc6157456b36a4eeb1c758d6ccff10df8d 100644 --- a/compiler/optimizing/instruction_simplifier_arm.h +++ b/compiler/optimizing/instruction_simplifier_arm.h @@ -30,7 +30,7 @@ class InstructionSimplifierArm : public HOptimization { static constexpr const char* kInstructionSimplifierArmPassName = "instruction_simplifier_arm"; - void Run() OVERRIDE; + bool Run() OVERRIDE; }; } // namespace arm diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc index 1c44e5ac49af0005b67ca4e60685df74415c2b94..e0a627994d0746468ecd3c2a401b236725405575 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.cc +++ b/compiler/optimizing/instruction_simplifier_arm64.cc @@ -278,9 +278,10 @@ void InstructionSimplifierArm64Visitor::VisitVecStore(HVecStore* instruction) { } } -void InstructionSimplifierArm64::Run() { +bool InstructionSimplifierArm64::Run() { InstructionSimplifierArm64Visitor visitor(graph_, stats_); visitor.VisitReversePostOrder(); + return true; } } // namespace arm64 diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h index d180a8dc4643366cb9c146e36365120b697fb303..8659c1f5f4b3db7f8c52a71bf2a469abdd4a9fc2 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.h +++ b/compiler/optimizing/instruction_simplifier_arm64.h @@ -30,7 +30,7 @@ class InstructionSimplifierArm64 : public HOptimization { static constexpr const char* kInstructionSimplifierArm64PassName = "instruction_simplifier_arm64"; - void Run() OVERRIDE; + bool Run() OVERRIDE; }; } // namespace arm64 diff --git a/compiler/optimizing/instruction_simplifier_mips.cc b/compiler/optimizing/instruction_simplifier_mips.cc index fa97401a0c90d0379e800c38d9ee09386801edb6..3bdf90f652819e80d5de4b24783dac7913f4b161 100644 --- a/compiler/optimizing/instruction_simplifier_mips.cc +++ b/compiler/optimizing/instruction_simplifier_mips.cc @@ -131,9 +131,10 @@ void InstructionSimplifierMipsVisitor::VisitArraySet(HArraySet* instruction) { } } -void InstructionSimplifierMips::Run() { +bool InstructionSimplifierMips::Run() { InstructionSimplifierMipsVisitor visitor(graph_, codegen_, stats_); visitor.VisitReversePostOrder(); + return true; } } // namespace mips diff --git a/compiler/optimizing/instruction_simplifier_mips.h b/compiler/optimizing/instruction_simplifier_mips.h index 6cb8affe85c0190699a3c1874d71beacb2870a68..94ef73d42525ea83741d7a0aa0fd2c0a97acab04 100644 --- a/compiler/optimizing/instruction_simplifier_mips.h +++ b/compiler/optimizing/instruction_simplifier_mips.h @@ -35,7 +35,7 @@ class InstructionSimplifierMips : public HOptimization { static constexpr const char* kInstructionSimplifierMipsPassName = "instruction_simplifier_mips"; - void Run() OVERRIDE; + bool Run() OVERRIDE; private: CodeGeneratorMIPS* codegen_; diff --git a/compiler/optimizing/intrinsic_objects.cc b/compiler/optimizing/intrinsic_objects.cc new file mode 100644 index 0000000000000000000000000000000000000000..3c20ad698b526db34545bb3d87a9a638acc251d4 --- /dev/null +++ b/compiler/optimizing/intrinsic_objects.cc @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "intrinsic_objects.h" + +#include "art_field-inl.h" +#include "base/logging.h" +#include "class_root.h" +#include "handle.h" +#include "obj_ptr-inl.h" +#include "mirror/object_array-inl.h" + +namespace art { + +static ObjPtr> LookupIntegerCache(Thread* self, + ClassLinker* class_linker) + REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr 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> integer_cache = + ObjPtr>::DownCast( + cache_field->GetObject(integer_cache_class)); + CHECK(integer_cache != nullptr); + return integer_cache; +} + +ObjPtr> IntrinsicObjects::AllocateBootImageLiveObjects( + 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> integer_cache = + hs.NewHandle(LookupIntegerCache(self, class_linker)); + size_t live_objects_size = + (integer_cache != nullptr) ? (/* cache */ 1u + integer_cache->GetLength()) : 0u; + ObjPtr> live_objects = + mirror::ObjectArray::Alloc( + self, GetClassRoot>(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()); + + if (kIsDebugBuild && integer_cache != nullptr) { + CHECK_EQ(integer_cache.Get(), GetIntegerValueOfCache(live_objects)); + for (int32_t i = 0, len = integer_cache->GetLength(); i != len; ++i) { + CHECK_EQ(integer_cache->GetWithoutChecks(i), GetIntegerValueOfObject(live_objects, i)); + } + } + return live_objects; +} + +ObjPtr> IntrinsicObjects::GetIntegerValueOfCache( + ObjPtr> boot_image_live_objects) { + DCHECK(boot_image_live_objects != nullptr); + if (boot_image_live_objects->GetLength() == 0u) { + return nullptr; // No intrinsic objects. + } + // No need for read barrier for boot image object or for verifying the value that was just stored. + ObjPtr result = + boot_image_live_objects->GetWithoutChecks(0); + DCHECK(result != nullptr); + DCHECK(result->IsObjectArray()); + DCHECK(result->GetClass()->DescriptorEquals("[Ljava/lang/Integer;")); + return ObjPtr>::DownCast(result); +} + +ObjPtr IntrinsicObjects::GetIntegerValueOfObject( + ObjPtr> boot_image_live_objects, + uint32_t index) { + DCHECK(boot_image_live_objects != nullptr); + DCHECK_NE(boot_image_live_objects->GetLength(), 0); + DCHECK_LT(index, + static_cast(GetIntegerValueOfCache(boot_image_live_objects)->GetLength())); + + // No need for read barrier for boot image object or for verifying the value that was just stored. + ObjPtr result = + boot_image_live_objects->GetWithoutChecks( + /* skip the IntegerCache.cache */ 1u + index); + DCHECK(result != nullptr); + DCHECK(result->GetClass()->DescriptorEquals("Ljava/lang/Integer;")); + return result; +} + +MemberOffset IntrinsicObjects::GetIntegerValueOfArrayDataOffset( + ObjPtr> boot_image_live_objects) { + DCHECK_NE(boot_image_live_objects->GetLength(), 0); + MemberOffset result = mirror::ObjectArray::OffsetOfElement(1u); + DCHECK_EQ(GetIntegerValueOfObject(boot_image_live_objects, 0u), + (boot_image_live_objects + ->GetFieldObject(result))); + return result; +} + +} // namespace art diff --git a/compiler/optimizing/intrinsic_objects.h b/compiler/optimizing/intrinsic_objects.h new file mode 100644 index 0000000000000000000000000000000000000000..863017be38bbb752f5d7c6a7870065e059963621 --- /dev/null +++ b/compiler/optimizing/intrinsic_objects.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_OPTIMIZING_INTRINSIC_OBJECTS_H_ +#define ART_COMPILER_OPTIMIZING_INTRINSIC_OBJECTS_H_ + +#include "base/bit_field.h" +#include "base/bit_utils.h" +#include "base/mutex.h" + +namespace art { + +class ClassLinker; +template class ObjPtr; +class MemberOffset; +class Thread; + +namespace mirror { +class Object; +template class ObjectArray; +} // namespace mirror + +class IntrinsicObjects { + public: + enum class PatchType { + kIntegerValueOfObject, + kIntegerValueOfArray, + + kLast = kIntegerValueOfArray + }; + + static uint32_t EncodePatch(PatchType patch_type, uint32_t index = 0u) { + DCHECK(patch_type == PatchType::kIntegerValueOfObject || index == 0u); + return PatchTypeField::Encode(static_cast(patch_type)) | IndexField::Encode(index); + } + + static PatchType DecodePatchType(uint32_t intrinsic_data) { + return static_cast(PatchTypeField::Decode(intrinsic_data)); + } + + static uint32_t DecodePatchIndex(uint32_t intrinsic_data) { + return IndexField::Decode(intrinsic_data); + } + + static ObjPtr> AllocateBootImageLiveObjects( + Thread* self, + ClassLinker* class_linker) REQUIRES_SHARED(Locks::mutator_lock_); + + // Functions for retrieving data for Integer.valueOf(). + static ObjPtr> GetIntegerValueOfCache( + ObjPtr> boot_image_live_objects) + REQUIRES_SHARED(Locks::mutator_lock_); + static ObjPtr GetIntegerValueOfObject( + ObjPtr> boot_image_live_objects, + uint32_t index) REQUIRES_SHARED(Locks::mutator_lock_); + static MemberOffset GetIntegerValueOfArrayDataOffset( + ObjPtr> boot_image_live_objects) + REQUIRES_SHARED(Locks::mutator_lock_); + + private: + static constexpr size_t kPatchTypeBits = + MinimumBitsToStore(static_cast(PatchType::kLast)); + static constexpr size_t kIndexBits = BitSizeOf() - kPatchTypeBits; + using PatchTypeField = BitField; + using IndexField = BitField; +}; + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_INTRINSIC_OBJECTS_H_ diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index f8dc316e45bfad451d02a961ddcf843cb3d68dad..21efe11f31091d9263d4eb5b6ab72af3a16808bc 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -21,10 +21,12 @@ #include "base/utils.h" #include "class_linker.h" #include "dex/invoke_type.h" -#include "driver/compiler_driver.h" #include "driver/compiler_options.h" -#include "mirror/dex_cache-inl.h" +#include "gc/space/image_space.h" +#include "image-inl.h" +#include "intrinsic_objects.h" #include "nodes.h" +#include "obj_ptr-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" @@ -142,6 +144,7 @@ static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) case kSuper: case kInterface: case kPolymorphic: + case kCustom: return false; } LOG(FATAL) << "Unknown intrinsic invoke type: " << intrinsic_type; @@ -178,7 +181,8 @@ bool IntrinsicsRecognizer::Recognize(HInvoke* invoke, return true; } -void IntrinsicsRecognizer::Run() { +bool IntrinsicsRecognizer::Run() { + bool didRecognize = false; ScopedObjectAccess soa(Thread::Current()); for (HBasicBlock* block : graph_->GetReversePostOrder()) { for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done(); @@ -187,6 +191,7 @@ void IntrinsicsRecognizer::Run() { if (inst->IsInvoke()) { bool wrong_invoke_type = false; if (Recognize(inst->AsInvoke(), /* art_method */ nullptr, &wrong_invoke_type)) { + didRecognize = true; MaybeRecordStat(stats_, MethodCompilationStat::kIntrinsicRecognized); } else if (wrong_invoke_type) { LOG(WARNING) @@ -197,6 +202,7 @@ void IntrinsicsRecognizer::Run() { } } } + return didRecognize; } std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) { @@ -217,111 +223,315 @@ std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) { return os; } +static const char kIntegerCacheDescriptor[] = "Ljava/lang/Integer$IntegerCache;"; +static const char kIntegerDescriptor[] = "Ljava/lang/Integer;"; +static const char kIntegerArrayDescriptor[] = "[Ljava/lang/Integer;"; +static const char kLowFieldName[] = "low"; +static const char kHighFieldName[] = "high"; +static const char kValueFieldName[] = "value"; + +static ObjPtr> GetBootImageLiveObjects() + REQUIRES_SHARED(Locks::mutator_lock_) { + gc::Heap* heap = Runtime::Current()->GetHeap(); + const std::vector& boot_image_spaces = heap->GetBootImageSpaces(); + DCHECK(!boot_image_spaces.empty()); + const ImageHeader& main_header = boot_image_spaces[0]->GetImageHeader(); + ObjPtr> boot_image_live_objects = + ObjPtr>::DownCast( + main_header.GetImageRoot(ImageHeader::kBootImageLiveObjects)); + DCHECK(boot_image_live_objects != nullptr); + DCHECK(heap->ObjectIsInBootImageSpace(boot_image_live_objects)); + return boot_image_live_objects; +} + +static ObjPtr LookupInitializedClass(Thread* self, + ClassLinker* class_linker, + const char* descriptor) + REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr klass = + class_linker->LookupClass(self, descriptor, /* class_loader */ nullptr); + DCHECK(klass != nullptr); + DCHECK(klass->IsInitialized()); + return klass; +} + +static ObjPtr> GetIntegerCacheArray( + ObjPtr cache_class) REQUIRES_SHARED(Locks::mutator_lock_) { + ArtField* cache_field = cache_class->FindDeclaredStaticField("cache", kIntegerArrayDescriptor); + DCHECK(cache_field != nullptr); + return ObjPtr>::DownCast(cache_field->GetObject(cache_class)); +} + +static int32_t GetIntegerCacheField(ObjPtr cache_class, const char* field_name) + REQUIRES_SHARED(Locks::mutator_lock_) { + ArtField* field = cache_class->FindDeclaredStaticField(field_name, "I"); + DCHECK(field != nullptr); + return field->GetInt(cache_class); +} + +static bool CheckIntegerCache(Thread* self, + ClassLinker* class_linker, + ObjPtr> boot_image_live_objects, + ObjPtr> boot_image_cache) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(boot_image_cache != nullptr); + + // Since we have a cache in the boot image, both java.lang.Integer and + // java.lang.Integer$IntegerCache must be initialized in the boot image. + ObjPtr cache_class = + LookupInitializedClass(self, class_linker, kIntegerCacheDescriptor); + ObjPtr integer_class = + LookupInitializedClass(self, class_linker, kIntegerDescriptor); + + // Check that the current cache is the same as the `boot_image_cache`. + ObjPtr> current_cache = GetIntegerCacheArray(cache_class); + if (current_cache != boot_image_cache) { + return false; // Messed up IntegerCache.cache. + } + + // Check that the range matches the boot image cache length. + int32_t low = GetIntegerCacheField(cache_class, kLowFieldName); + int32_t high = GetIntegerCacheField(cache_class, kHighFieldName); + if (boot_image_cache->GetLength() != high - low + 1) { + return false; // Messed up IntegerCache.low or IntegerCache.high. + } + + // Check that the elements match the boot image intrinsic objects and check their values as well. + ArtField* value_field = integer_class->FindDeclaredInstanceField(kValueFieldName, "I"); + DCHECK(value_field != nullptr); + for (int32_t i = 0, len = boot_image_cache->GetLength(); i != len; ++i) { + ObjPtr boot_image_object = + IntrinsicObjects::GetIntegerValueOfObject(boot_image_live_objects, i); + DCHECK(Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boot_image_object)); + // No need for read barrier for comparison with a boot image object. + ObjPtr current_object = + boot_image_cache->GetWithoutChecks(i); + if (boot_image_object != current_object) { + return false; // Messed up IntegerCache.cache[i] + } + if (value_field->GetInt(boot_image_object) != low + i) { + return false; // Messed up IntegerCache.cache[i].value. + } + } + + return true; +} + void IntrinsicVisitor::ComputeIntegerValueOfLocations(HInvoke* invoke, CodeGenerator* codegen, Location return_location, Location first_argument_location) { - if (Runtime::Current()->IsAotCompiler()) { - if (codegen->GetCompilerOptions().IsBootImage() || - codegen->GetCompilerOptions().GetCompilePic()) { - // TODO(ngeoffray): Support boot image compilation. + // The intrinsic will call if it needs to allocate a j.l.Integer. + LocationSummary::CallKind call_kind = LocationSummary::kCallOnMainOnly; + const CompilerOptions& compiler_options = codegen->GetCompilerOptions(); + if (compiler_options.IsBootImage()) { + // Piggyback on the method load kind to determine whether we can use PC-relative addressing. + // This should cover both the testing config (non-PIC boot image) and codegens that reject + // PC-relative load kinds and fall back to the runtime call. + if (!invoke->AsInvokeStaticOrDirect()->HasPcRelativeMethodLoadKind()) { + return; + } + if (!compiler_options.IsImageClass(kIntegerCacheDescriptor) || + !compiler_options.IsImageClass(kIntegerDescriptor)) { + return; + } + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + ObjPtr cache_class = class_linker->LookupClass( + self, kIntegerCacheDescriptor, /* class_loader */ nullptr); + DCHECK(cache_class != nullptr); + if (UNLIKELY(!cache_class->IsInitialized())) { + LOG(WARNING) << "Image class " << cache_class->PrettyDescriptor() << " is uninitialized."; return; } + ObjPtr integer_class = + class_linker->LookupClass(self, kIntegerDescriptor, /* class_loader */ nullptr); + DCHECK(integer_class != nullptr); + if (UNLIKELY(!integer_class->IsInitialized())) { + LOG(WARNING) << "Image class " << integer_class->PrettyDescriptor() << " is uninitialized."; + return; + } + int32_t low = GetIntegerCacheField(cache_class, kLowFieldName); + int32_t high = GetIntegerCacheField(cache_class, kHighFieldName); + if (kIsDebugBuild) { + ObjPtr> current_cache = GetIntegerCacheArray(cache_class); + CHECK(current_cache != nullptr); + CHECK_EQ(current_cache->GetLength(), high - low + 1); + ArtField* value_field = integer_class->FindDeclaredInstanceField(kValueFieldName, "I"); + CHECK(value_field != nullptr); + for (int32_t i = 0, len = current_cache->GetLength(); i != len; ++i) { + ObjPtr current_object = current_cache->GetWithoutChecks(i); + CHECK(current_object != nullptr); + CHECK_EQ(value_field->GetInt(current_object), low + i); + } + } + if (invoke->InputAt(0)->IsIntConstant()) { + int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue(); + if (static_cast(value) - static_cast(low) < + static_cast(high - low + 1)) { + // No call, we shall use direct pointer to the Integer object. + call_kind = LocationSummary::kNoCall; + } + } + } else { + Runtime* runtime = Runtime::Current(); + if (runtime->GetHeap()->GetBootImageSpaces().empty()) { + return; // Running without boot image, cannot use required boot image objects. + } + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + ObjPtr> boot_image_live_objects = GetBootImageLiveObjects(); + ObjPtr> cache = + IntrinsicObjects::GetIntegerValueOfCache(boot_image_live_objects); + if (cache == nullptr) { + return; // No cache in the boot image. + } + if (runtime->UseJitCompilation()) { + if (!CheckIntegerCache(self, runtime->GetClassLinker(), boot_image_live_objects, cache)) { + return; // The cache was somehow messed up, probably by using reflection. + } + } else { + DCHECK(runtime->IsAotCompiler()); + DCHECK(CheckIntegerCache(self, runtime->GetClassLinker(), boot_image_live_objects, cache)); + if (invoke->InputAt(0)->IsIntConstant()) { + int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue(); + // Retrieve the `value` from the lowest cached Integer. + ObjPtr low_integer = + IntrinsicObjects::GetIntegerValueOfObject(boot_image_live_objects, 0u); + ObjPtr integer_class = + low_integer->GetClass(); + ArtField* value_field = integer_class->FindDeclaredInstanceField(kValueFieldName, "I"); + DCHECK(value_field != nullptr); + int32_t low = value_field->GetInt(low_integer); + if (static_cast(value) - static_cast(low) < + static_cast(cache->GetLength())) { + // No call, we shall use direct pointer to the Integer object. Note that we cannot + // do this for JIT as the "low" can change through reflection before emitting the code. + call_kind = LocationSummary::kNoCall; + } + } + } } - IntegerValueOfInfo info = ComputeIntegerValueOfInfo(); - - // Most common case is that we have found all we needed (classes are initialized - // and in the boot image). Bail if not. - if (info.integer_cache == nullptr || - info.integer == nullptr || - info.cache == nullptr || - info.value_offset == 0 || - // low and high cannot be 0, per the spec. - info.low == 0 || - info.high == 0) { - LOG(INFO) << "Integer.valueOf will not be optimized"; - return; + ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetAllocator(); + LocationSummary* locations = new (allocator) LocationSummary(invoke, call_kind, kIntrinsified); + if (call_kind == LocationSummary::kCallOnMainOnly) { + locations->SetInAt(0, Location::RegisterOrConstant(invoke->InputAt(0))); + locations->AddTemp(first_argument_location); + locations->SetOut(return_location); + } else { + locations->SetInAt(0, Location::ConstantLocation(invoke->InputAt(0)->AsConstant())); + locations->SetOut(Location::RequiresRegister()); } +} - // The intrinsic will call if it needs to allocate a j.l.Integer. - LocationSummary* locations = new (invoke->GetBlock()->GetGraph()->GetAllocator()) LocationSummary( - invoke, LocationSummary::kCallOnMainOnly, kIntrinsified); - if (!invoke->InputAt(0)->IsConstant()) { - locations->SetInAt(0, Location::RequiresRegister()); - } - locations->AddTemp(first_argument_location); - locations->SetOut(return_location); +static int32_t GetIntegerCacheLowFromIntegerCache(Thread* self, ClassLinker* class_linker) + REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr cache_class = + LookupInitializedClass(self, class_linker, kIntegerCacheDescriptor); + return GetIntegerCacheField(cache_class, kLowFieldName); +} + +static uint32_t CalculateBootImageOffset(ObjPtr object) + REQUIRES_SHARED(Locks::mutator_lock_) { + gc::Heap* heap = Runtime::Current()->GetHeap(); + DCHECK(heap->ObjectIsInBootImageSpace(object)); + return reinterpret_cast(object.Ptr()) - heap->GetBootImageSpaces()[0]->Begin(); } -IntrinsicVisitor::IntegerValueOfInfo IntrinsicVisitor::ComputeIntegerValueOfInfo() { +inline IntrinsicVisitor::IntegerValueOfInfo::IntegerValueOfInfo() + : value_offset(0), + low(0), + length(0u), + integer_boot_image_offset(kInvalidReference), + value_boot_image_reference(kInvalidReference) {} + +IntrinsicVisitor::IntegerValueOfInfo IntrinsicVisitor::ComputeIntegerValueOfInfo( + HInvoke* invoke, const CompilerOptions& compiler_options) { // Note that we could cache all of the data looked up here. but there's no good // location for it. We don't want to add it to WellKnownClasses, to avoid creating global // jni values. Adding it as state to the compiler singleton seems like wrong // separation of concerns. // The need for this data should be pretty rare though. - // The most common case is that the classes are in the boot image and initialized, - // which is easy to generate code for. We bail if not. - Thread* self = Thread::Current(); - ScopedObjectAccess soa(self); + // Note that at this point we can no longer abort the code generation. Therefore, + // we need to provide data that shall not lead to a crash even if the fields were + // modified through reflection since ComputeIntegerValueOfLocations() when JITting. + Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); - gc::Heap* heap = runtime->GetHeap(); - IntegerValueOfInfo info; - info.integer_cache = class_linker->FindSystemClass(self, "Ljava/lang/Integer$IntegerCache;"); - if (info.integer_cache == nullptr) { - self->ClearException(); - 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. - return info; - } - info.integer = class_linker->FindSystemClass(self, "Ljava/lang/Integer;"); - 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. - return info; - } - - ArtField* field = info.integer_cache->FindDeclaredStaticField("cache", "[Ljava/lang/Integer;"); - if (field == nullptr) { - return info; - } - info.cache = static_cast*>( - field->GetObject(info.integer_cache).Ptr()); - if (info.cache == nullptr) { - return info; - } - - if (!heap->ObjectIsInBootImageSpace(info.cache)) { - // Optimization only works if the object is in the boot image. - return info; - } - - field = info.integer->FindDeclaredInstanceField("value", "I"); - if (field == nullptr) { - return info; - } - info.value_offset = field->GetOffset().Int32Value(); - - field = info.integer_cache->FindDeclaredStaticField("low", "I"); - if (field == nullptr) { - return info; - } - info.low = field->GetInt(info.integer_cache); + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); - field = info.integer_cache->FindDeclaredStaticField("high", "I"); - if (field == nullptr) { - return info; + IntegerValueOfInfo info; + if (compiler_options.IsBootImage()) { + ObjPtr integer_class = + LookupInitializedClass(self, class_linker, kIntegerDescriptor); + ArtField* value_field = integer_class->FindDeclaredInstanceField(kValueFieldName, "I"); + DCHECK(value_field != nullptr); + info.value_offset = value_field->GetOffset().Uint32Value(); + ObjPtr cache_class = + LookupInitializedClass(self, class_linker, kIntegerCacheDescriptor); + info.low = GetIntegerCacheField(cache_class, kLowFieldName); + int32_t high = GetIntegerCacheField(cache_class, kHighFieldName); + info.length = dchecked_integral_cast(high - info.low + 1); + + info.integer_boot_image_offset = IntegerValueOfInfo::kInvalidReference; + if (invoke->InputAt(0)->IsIntConstant()) { + int32_t input_value = invoke->InputAt(0)->AsIntConstant()->GetValue(); + uint32_t index = static_cast(input_value) - static_cast(info.low); + if (index < static_cast(info.length)) { + info.value_boot_image_reference = IntrinsicObjects::EncodePatch( + IntrinsicObjects::PatchType::kIntegerValueOfObject, index); + } else { + // Not in the cache. + info.value_boot_image_reference = IntegerValueOfInfo::kInvalidReference; + } + } else { + info.array_data_boot_image_reference = + IntrinsicObjects::EncodePatch(IntrinsicObjects::PatchType::kIntegerValueOfArray); + } + } else { + ObjPtr> boot_image_live_objects = GetBootImageLiveObjects(); + ObjPtr low_integer = + IntrinsicObjects::GetIntegerValueOfObject(boot_image_live_objects, 0u); + ObjPtr integer_class = low_integer->GetClass(); + ArtField* value_field = integer_class->FindDeclaredInstanceField(kValueFieldName, "I"); + DCHECK(value_field != nullptr); + info.value_offset = value_field->GetOffset().Uint32Value(); + if (runtime->UseJitCompilation()) { + // Use the current `IntegerCache.low` for JIT to avoid truly surprising behavior if the + // code messes up the `value` field in the lowest cached Integer using reflection. + info.low = GetIntegerCacheLowFromIntegerCache(self, class_linker); + } else { + // For app AOT, the `low_integer->value` should be the same as `IntegerCache.low`. + info.low = value_field->GetInt(low_integer); + DCHECK_EQ(info.low, GetIntegerCacheLowFromIntegerCache(self, class_linker)); + } + // Do not look at `IntegerCache.high`, use the immutable length of the cache array instead. + info.length = dchecked_integral_cast( + IntrinsicObjects::GetIntegerValueOfCache(boot_image_live_objects)->GetLength()); + + info.integer_boot_image_offset = CalculateBootImageOffset(integer_class); + if (invoke->InputAt(0)->IsIntConstant()) { + int32_t input_value = invoke->InputAt(0)->AsIntConstant()->GetValue(); + uint32_t index = static_cast(input_value) - static_cast(info.low); + if (index < static_cast(info.length)) { + ObjPtr integer = + IntrinsicObjects::GetIntegerValueOfObject(boot_image_live_objects, index); + info.value_boot_image_reference = CalculateBootImageOffset(integer); + } else { + // Not in the cache. + info.value_boot_image_reference = IntegerValueOfInfo::kInvalidReference; + } + } else { + info.array_data_boot_image_reference = + CalculateBootImageOffset(boot_image_live_objects) + + IntrinsicObjects::GetIntegerValueOfArrayDataOffset(boot_image_live_objects).Uint32Value(); + } } - info.high = field->GetInt(info.integer_cache); - DCHECK_EQ(info.cache->GetLength(), info.high - info.low + 1); return info; } diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index 62991435c7ab12db5d42bd769117205ce8a351b8..993648f76505254337949405495fd67e4b79eaf1 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -42,7 +42,7 @@ class IntrinsicsRecognizer : public HOptimization { const char* name = kIntrinsicsRecognizerPassName) : HOptimization(graph, name, stats) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; // Static helper that recognizes intrinsic call. Returns true on success. // If it fails due to invoke type mismatch, wrong_invoke_type is set. @@ -126,33 +126,41 @@ class IntrinsicVisitor : public ValueObject { Location return_location, Location first_argument_location); - // Temporary data structure for holding Integer.valueOf useful data. We only - // use it if the mirror::Class* are in the boot image, so it is fine to keep raw - // mirror::Class pointers in this structure. + // Temporary data structure for holding Integer.valueOf data for generating code. + // We only use it if the boot image contains the IntegerCache objects. struct IntegerValueOfInfo { - IntegerValueOfInfo() - : integer_cache(nullptr), - integer(nullptr), - cache(nullptr), - low(0), - high(0), - value_offset(0) {} - - // The java.lang.IntegerCache class. - mirror::Class* integer_cache; - // The java.lang.Integer class. - mirror::Class* integer; - // Value of java.lang.IntegerCache#cache. - mirror::ObjectArray* cache; - // Value of java.lang.IntegerCache#low. + static constexpr uint32_t kInvalidReference = static_cast(-1); + + IntegerValueOfInfo(); + + // Offset of the Integer.value field for initializing a newly allocated instance. + uint32_t value_offset; + // The low value in the cache. int32_t low; - // Value of java.lang.IntegerCache#high. - int32_t high; - // The offset of java.lang.Integer.value. - int32_t value_offset; + // The length of the cache array. + uint32_t length; + + // Boot image offset of java.lang.Integer for allocating an instance. + uint32_t integer_boot_image_offset; // Set to kInvalidReference when compiling the boot image. + + // This union contains references to the boot image. For app AOT or JIT compilation, + // these are the boot image offsets of the target. For boot image compilation, the + // location shall be known only at link time, so we encode a symbolic reference using + // IntrinsicObjects::EncodePatch(). + union { + // The target value for a constant input in the cache range. If the constant input + // is out of range (use `low` and `length` to check), this value is bogus (set to + // kInvalidReference) and the code must allocate a new Integer. + uint32_t value_boot_image_reference; + + // The cache array data used for a non-constant input in the cache range. + // If the input is out of range, the code must allocate a new Integer. + uint32_t array_data_boot_image_reference; + }; }; - static IntegerValueOfInfo ComputeIntegerValueOfInfo(); + static IntegerValueOfInfo ComputeIntegerValueOfInfo( + HInvoke* invoke, const CompilerOptions& compiler_options); protected: IntrinsicVisitor() {} @@ -266,6 +274,18 @@ void IntrinsicCodeGenerator ## Arch::Visit ## Name(HInvoke* invoke) { \ << " should have been converted to HIR"; \ } #define UNREACHABLE_INTRINSICS(Arch) \ +UNREACHABLE_INTRINSIC(Arch, MathMinIntInt) \ +UNREACHABLE_INTRINSIC(Arch, MathMinLongLong) \ +UNREACHABLE_INTRINSIC(Arch, MathMinFloatFloat) \ +UNREACHABLE_INTRINSIC(Arch, MathMinDoubleDouble) \ +UNREACHABLE_INTRINSIC(Arch, MathMaxIntInt) \ +UNREACHABLE_INTRINSIC(Arch, MathMaxLongLong) \ +UNREACHABLE_INTRINSIC(Arch, MathMaxFloatFloat) \ +UNREACHABLE_INTRINSIC(Arch, MathMaxDoubleDouble) \ +UNREACHABLE_INTRINSIC(Arch, MathAbsInt) \ +UNREACHABLE_INTRINSIC(Arch, MathAbsLong) \ +UNREACHABLE_INTRINSIC(Arch, MathAbsFloat) \ +UNREACHABLE_INTRINSIC(Arch, MathAbsDouble) \ UNREACHABLE_INTRINSIC(Arch, FloatFloatToIntBits) \ UNREACHABLE_INTRINSIC(Arch, DoubleDoubleToLongBits) \ UNREACHABLE_INTRINSIC(Arch, FloatIsNaN) \ diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 0e6485be9f7df1da607ae36b017412c13308cef9..4b2bcc8ca8ee0fbc883ba40c7a07d874f245254a 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -344,14 +344,6 @@ void IntrinsicCodeGeneratorARM64::VisitShortReverseBytes(HInvoke* invoke) { GenReverseBytes(invoke->GetLocations(), DataType::Type::kInt16, GetVIXLAssembler()); } -static void CreateIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); -} - static void GenNumberOfLeadingZeros(LocationSummary* locations, DataType::Type type, MacroAssembler* masm) { @@ -536,168 +528,6 @@ static void CreateFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); } -static void MathAbsFP(LocationSummary* locations, bool is64bit, MacroAssembler* masm) { - Location in = locations->InAt(0); - Location out = locations->Out(); - - FPRegister in_reg = is64bit ? DRegisterFrom(in) : SRegisterFrom(in); - FPRegister out_reg = is64bit ? DRegisterFrom(out) : SRegisterFrom(out); - - __ Fabs(out_reg, in_reg); -} - -void IntrinsicLocationsBuilderARM64::VisitMathAbsDouble(HInvoke* invoke) { - CreateFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathAbsDouble(HInvoke* invoke) { - MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler()); -} - -void IntrinsicLocationsBuilderARM64::VisitMathAbsFloat(HInvoke* invoke) { - CreateFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathAbsFloat(HInvoke* invoke) { - MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler()); -} - -static void GenAbsInteger(LocationSummary* locations, - bool is64bit, - MacroAssembler* masm) { - Location in = locations->InAt(0); - Location output = locations->Out(); - - Register in_reg = is64bit ? XRegisterFrom(in) : WRegisterFrom(in); - Register out_reg = is64bit ? XRegisterFrom(output) : WRegisterFrom(output); - - __ Cmp(in_reg, Operand(0)); - __ Cneg(out_reg, in_reg, lt); -} - -void IntrinsicLocationsBuilderARM64::VisitMathAbsInt(HInvoke* invoke) { - CreateIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathAbsInt(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler()); -} - -void IntrinsicLocationsBuilderARM64::VisitMathAbsLong(HInvoke* invoke) { - CreateIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathAbsLong(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler()); -} - -static void GenMinMaxFP(LocationSummary* locations, - bool is_min, - bool is_double, - MacroAssembler* masm) { - Location op1 = locations->InAt(0); - Location op2 = locations->InAt(1); - Location out = locations->Out(); - - FPRegister op1_reg = is_double ? DRegisterFrom(op1) : SRegisterFrom(op1); - FPRegister op2_reg = is_double ? DRegisterFrom(op2) : SRegisterFrom(op2); - FPRegister out_reg = is_double ? DRegisterFrom(out) : SRegisterFrom(out); - if (is_min) { - __ Fmin(out_reg, op1_reg, op2_reg); - } else { - __ Fmax(out_reg, op1_reg, op2_reg); - } -} - -static void CreateFPFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetInAt(1, Location::RequiresFpuRegister()); - locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); -} - -void IntrinsicLocationsBuilderARM64::VisitMathMinDoubleDouble(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathMinDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetVIXLAssembler()); -} - -void IntrinsicLocationsBuilderARM64::VisitMathMinFloatFloat(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathMinFloatFloat(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetVIXLAssembler()); -} - -void IntrinsicLocationsBuilderARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetVIXLAssembler()); -} - -void IntrinsicLocationsBuilderARM64::VisitMathMaxFloatFloat(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathMaxFloatFloat(HInvoke* invoke) { - GenMinMaxFP( - invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetVIXLAssembler()); -} - -static void GenMinMax(LocationSummary* locations, - bool is_min, - bool is_long, - MacroAssembler* masm) { - Location op1 = locations->InAt(0); - Location op2 = locations->InAt(1); - Location out = locations->Out(); - - Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1); - Register op2_reg = is_long ? XRegisterFrom(op2) : WRegisterFrom(op2); - Register out_reg = is_long ? XRegisterFrom(out) : WRegisterFrom(out); - - __ Cmp(op1_reg, op2_reg); - __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt); -} - -void IntrinsicLocationsBuilderARM64::VisitMathMinIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathMinIntInt(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetVIXLAssembler()); -} - -void IntrinsicLocationsBuilderARM64::VisitMathMinLongLong(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathMinLongLong(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetVIXLAssembler()); -} - -void IntrinsicLocationsBuilderARM64::VisitMathMaxIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathMaxIntInt(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetVIXLAssembler()); -} - -void IntrinsicLocationsBuilderARM64::VisitMathMaxLongLong(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathMaxLongLong(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetVIXLAssembler()); -} - void IntrinsicLocationsBuilderARM64::VisitMathSqrt(HInvoke* invoke) { CreateFPToFPLocations(allocator_, invoke); } @@ -2961,33 +2791,27 @@ void IntrinsicLocationsBuilderARM64::VisitIntegerValueOf(HInvoke* invoke) { } void IntrinsicCodeGeneratorARM64::VisitIntegerValueOf(HInvoke* invoke) { - IntrinsicVisitor::IntegerValueOfInfo info = IntrinsicVisitor::ComputeIntegerValueOfInfo(); + IntrinsicVisitor::IntegerValueOfInfo info = + IntrinsicVisitor::ComputeIntegerValueOfInfo(invoke, codegen_->GetCompilerOptions()); LocationSummary* locations = invoke->GetLocations(); MacroAssembler* masm = GetVIXLAssembler(); Register out = RegisterFrom(locations->Out(), DataType::Type::kReference); UseScratchRegisterScope temps(masm); Register temp = temps.AcquireW(); - InvokeRuntimeCallingConvention calling_convention; - Register argument = calling_convention.GetRegisterAt(0); if (invoke->InputAt(0)->IsConstant()) { int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue(); - if (value >= info.low && value <= info.high) { + if (static_cast(value - info.low) < info.length) { // Just embed the j.l.Integer in the code. - ScopedObjectAccess soa(Thread::Current()); - mirror::Object* boxed = info.cache->Get(value + (-info.low)); - DCHECK(boxed != nullptr && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boxed)); - uint32_t address = dchecked_integral_cast(reinterpret_cast(boxed)); - __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(address)); + DCHECK_NE(info.value_boot_image_reference, IntegerValueOfInfo::kInvalidReference); + codegen_->LoadBootImageAddress(out, info.value_boot_image_reference); } else { + DCHECK(locations->CanCall()); // Allocate and initialize a new j.l.Integer. // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the // JIT object table. - uint32_t address = - dchecked_integral_cast(reinterpret_cast(info.integer)); - __ Ldr(argument.W(), codegen_->DeduplicateBootImageAddressLiteral(address)); - codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc()); - CheckEntrypointTypes(); + codegen_->AllocateInstanceForIntrinsic(invoke->AsInvokeStaticOrDirect(), + info.integer_boot_image_offset); __ Mov(temp.W(), value); __ Str(temp.W(), HeapOperand(out.W(), info.value_offset)); // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation @@ -2995,16 +2819,15 @@ void IntrinsicCodeGeneratorARM64::VisitIntegerValueOf(HInvoke* invoke) { codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore); } } else { + DCHECK(locations->CanCall()); Register in = RegisterFrom(locations->InAt(0), DataType::Type::kInt32); // Check bounds of our cache. __ Add(out.W(), in.W(), -info.low); - __ Cmp(out.W(), info.high - info.low + 1); + __ Cmp(out.W(), info.length); vixl::aarch64::Label allocate, done; __ B(&allocate, hs); // If the value is within the bounds, load the j.l.Integer directly from the array. - uint32_t data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value(); - uint32_t address = dchecked_integral_cast(reinterpret_cast(info.cache)); - __ Ldr(temp.W(), codegen_->DeduplicateBootImageAddressLiteral(data_offset + address)); + codegen_->LoadBootImageAddress(temp, info.array_data_boot_image_reference); MemOperand source = HeapOperand( temp, out.X(), LSL, DataType::SizeShift(DataType::Type::kReference)); codegen_->Load(DataType::Type::kReference, out, source); @@ -3012,10 +2835,8 @@ void IntrinsicCodeGeneratorARM64::VisitIntegerValueOf(HInvoke* invoke) { __ B(&done); __ Bind(&allocate); // Otherwise allocate and initialize a new j.l.Integer. - address = dchecked_integral_cast(reinterpret_cast(info.integer)); - __ Ldr(argument.W(), codegen_->DeduplicateBootImageAddressLiteral(address)); - codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc()); - CheckEntrypointTypes(); + codegen_->AllocateInstanceForIntrinsic(invoke->AsInvokeStaticOrDirect(), + info.integer_boot_image_offset); __ Str(in.W(), HeapOperand(out.W(), info.value_offset)); // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation // one. diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index 97a145664c317c8da1aff64c61cefb9476b5e25d..f11e5a19892814b1b6ad438510546a55fbd9ea2b 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -25,7 +25,7 @@ #include "mirror/array-inl.h" #include "mirror/object_array-inl.h" #include "mirror/reference.h" -#include "mirror/string.h" +#include "mirror/string-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" @@ -432,341 +432,6 @@ void IntrinsicCodeGeneratorARMVIXL::VisitLongNumberOfTrailingZeros(HInvoke* invo GenNumberOfTrailingZeros(invoke, DataType::Type::kInt64, codegen_); } -static void MathAbsFP(HInvoke* invoke, ArmVIXLAssembler* assembler) { - __ Vabs(OutputVRegister(invoke), InputVRegisterAt(invoke, 0)); -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathAbsDouble(HInvoke* invoke) { - CreateFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathAbsDouble(HInvoke* invoke) { - MathAbsFP(invoke, GetAssembler()); -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathAbsFloat(HInvoke* invoke) { - CreateFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathAbsFloat(HInvoke* invoke) { - MathAbsFP(invoke, GetAssembler()); -} - -static void CreateIntToIntPlusTemp(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); - - locations->AddTemp(Location::RequiresRegister()); -} - -static void GenAbsInteger(LocationSummary* locations, - bool is64bit, - ArmVIXLAssembler* assembler) { - Location in = locations->InAt(0); - Location output = locations->Out(); - - vixl32::Register mask = RegisterFrom(locations->GetTemp(0)); - - if (is64bit) { - vixl32::Register in_reg_lo = LowRegisterFrom(in); - vixl32::Register in_reg_hi = HighRegisterFrom(in); - vixl32::Register out_reg_lo = LowRegisterFrom(output); - vixl32::Register out_reg_hi = HighRegisterFrom(output); - - DCHECK(!out_reg_lo.Is(in_reg_hi)) << "Diagonal overlap unexpected."; - - __ Asr(mask, in_reg_hi, 31); - __ Adds(out_reg_lo, in_reg_lo, mask); - __ Adc(out_reg_hi, in_reg_hi, mask); - __ Eor(out_reg_lo, mask, out_reg_lo); - __ Eor(out_reg_hi, mask, out_reg_hi); - } else { - vixl32::Register in_reg = RegisterFrom(in); - vixl32::Register out_reg = RegisterFrom(output); - - __ Asr(mask, in_reg, 31); - __ Add(out_reg, in_reg, mask); - __ Eor(out_reg, mask, out_reg); - } -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathAbsInt(HInvoke* invoke) { - CreateIntToIntPlusTemp(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathAbsInt(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetAssembler()); -} - - -void IntrinsicLocationsBuilderARMVIXL::VisitMathAbsLong(HInvoke* invoke) { - CreateIntToIntPlusTemp(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathAbsLong(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); -} - -static void GenMinMaxFloat(HInvoke* invoke, bool is_min, CodeGeneratorARMVIXL* codegen) { - ArmVIXLAssembler* assembler = codegen->GetAssembler(); - Location op1_loc = invoke->GetLocations()->InAt(0); - Location op2_loc = invoke->GetLocations()->InAt(1); - Location out_loc = invoke->GetLocations()->Out(); - - // Optimization: don't generate any code if inputs are the same. - if (op1_loc.Equals(op2_loc)) { - DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in location builder. - return; - } - - vixl32::SRegister op1 = SRegisterFrom(op1_loc); - vixl32::SRegister op2 = SRegisterFrom(op2_loc); - vixl32::SRegister out = OutputSRegister(invoke); - UseScratchRegisterScope temps(assembler->GetVIXLAssembler()); - const vixl32::Register temp1 = temps.Acquire(); - vixl32::Register temp2 = RegisterFrom(invoke->GetLocations()->GetTemp(0)); - vixl32::Label nan, done; - vixl32::Label* final_label = codegen->GetFinalLabel(invoke, &done); - - DCHECK(op1.Is(out)); - - __ Vcmp(op1, op2); - __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR); - __ B(vs, &nan, /* far_target */ false); // if un-ordered, go to NaN handling. - - // op1 <> op2 - vixl32::ConditionType cond = is_min ? gt : lt; - { - ExactAssemblyScope it_scope(assembler->GetVIXLAssembler(), - 2 * kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); - __ it(cond); - __ vmov(cond, F32, out, op2); - } - // for <>(not equal), we've done min/max calculation. - __ B(ne, final_label, /* far_target */ false); - - // handle op1 == op2, max(+0.0,-0.0), min(+0.0,-0.0). - __ Vmov(temp1, op1); - __ Vmov(temp2, op2); - if (is_min) { - __ Orr(temp1, temp1, temp2); - } else { - __ And(temp1, temp1, temp2); - } - __ Vmov(out, temp1); - __ B(final_label); - - // handle NaN input. - __ Bind(&nan); - __ Movt(temp1, High16Bits(kNanFloat)); // 0x7FC0xxxx is a NaN. - __ Vmov(out, temp1); - - if (done.IsReferenced()) { - __ Bind(&done); - } -} - -static void CreateFPFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetInAt(1, Location::RequiresFpuRegister()); - locations->SetOut(Location::SameAsFirstInput()); -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathMinFloatFloat(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); - invoke->GetLocations()->AddTemp(Location::RequiresRegister()); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathMinFloatFloat(HInvoke* invoke) { - GenMinMaxFloat(invoke, /* is_min */ true, codegen_); -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathMaxFloatFloat(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); - invoke->GetLocations()->AddTemp(Location::RequiresRegister()); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathMaxFloatFloat(HInvoke* invoke) { - GenMinMaxFloat(invoke, /* is_min */ false, codegen_); -} - -static void GenMinMaxDouble(HInvoke* invoke, bool is_min, CodeGeneratorARMVIXL* codegen) { - ArmVIXLAssembler* assembler = codegen->GetAssembler(); - Location op1_loc = invoke->GetLocations()->InAt(0); - Location op2_loc = invoke->GetLocations()->InAt(1); - Location out_loc = invoke->GetLocations()->Out(); - - // Optimization: don't generate any code if inputs are the same. - if (op1_loc.Equals(op2_loc)) { - DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in. - return; - } - - vixl32::DRegister op1 = DRegisterFrom(op1_loc); - vixl32::DRegister op2 = DRegisterFrom(op2_loc); - vixl32::DRegister out = OutputDRegister(invoke); - vixl32::Label handle_nan_eq, done; - vixl32::Label* final_label = codegen->GetFinalLabel(invoke, &done); - - DCHECK(op1.Is(out)); - - __ Vcmp(op1, op2); - __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR); - __ B(vs, &handle_nan_eq, /* far_target */ false); // if un-ordered, go to NaN handling. - - // op1 <> op2 - vixl32::ConditionType cond = is_min ? gt : lt; - { - ExactAssemblyScope it_scope(assembler->GetVIXLAssembler(), - 2 * kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); - __ it(cond); - __ vmov(cond, F64, out, op2); - } - // for <>(not equal), we've done min/max calculation. - __ B(ne, final_label, /* far_target */ false); - - // handle op1 == op2, max(+0.0,-0.0). - if (!is_min) { - __ Vand(F64, out, op1, op2); - __ B(final_label); - } - - // handle op1 == op2, min(+0.0,-0.0), NaN input. - __ Bind(&handle_nan_eq); - __ Vorr(F64, out, op1, op2); // assemble op1/-0.0/NaN. - - if (done.IsReferenced()) { - __ Bind(&done); - } -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathMinDoubleDouble(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathMinDoubleDouble(HInvoke* invoke) { - GenMinMaxDouble(invoke, /* is_min */ true , codegen_); -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathMaxDoubleDouble(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathMaxDoubleDouble(HInvoke* invoke) { - GenMinMaxDouble(invoke, /* is_min */ false, codegen_); -} - -static void GenMinMaxLong(HInvoke* invoke, bool is_min, ArmVIXLAssembler* assembler) { - Location op1_loc = invoke->GetLocations()->InAt(0); - Location op2_loc = invoke->GetLocations()->InAt(1); - Location out_loc = invoke->GetLocations()->Out(); - - // Optimization: don't generate any code if inputs are the same. - if (op1_loc.Equals(op2_loc)) { - DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in location builder. - return; - } - - vixl32::Register op1_lo = LowRegisterFrom(op1_loc); - vixl32::Register op1_hi = HighRegisterFrom(op1_loc); - vixl32::Register op2_lo = LowRegisterFrom(op2_loc); - vixl32::Register op2_hi = HighRegisterFrom(op2_loc); - vixl32::Register out_lo = LowRegisterFrom(out_loc); - vixl32::Register out_hi = HighRegisterFrom(out_loc); - UseScratchRegisterScope temps(assembler->GetVIXLAssembler()); - const vixl32::Register temp = temps.Acquire(); - - DCHECK(op1_lo.Is(out_lo)); - DCHECK(op1_hi.Is(out_hi)); - - // Compare op1 >= op2, or op1 < op2. - __ Cmp(out_lo, op2_lo); - __ Sbcs(temp, out_hi, op2_hi); - - // Now GE/LT condition code is correct for the long comparison. - { - vixl32::ConditionType cond = is_min ? ge : lt; - ExactAssemblyScope it_scope(assembler->GetVIXLAssembler(), - 3 * kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); - __ itt(cond); - __ mov(cond, out_lo, op2_lo); - __ mov(cond, out_hi, op2_hi); - } -} - -static void CreateLongLongToLongLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::SameAsFirstInput()); -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathMinLongLong(HInvoke* invoke) { - CreateLongLongToLongLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathMinLongLong(HInvoke* invoke) { - GenMinMaxLong(invoke, /* is_min */ true, GetAssembler()); -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathMaxLongLong(HInvoke* invoke) { - CreateLongLongToLongLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathMaxLongLong(HInvoke* invoke) { - GenMinMaxLong(invoke, /* is_min */ false, GetAssembler()); -} - -static void GenMinMax(HInvoke* invoke, bool is_min, ArmVIXLAssembler* assembler) { - vixl32::Register op1 = InputRegisterAt(invoke, 0); - vixl32::Register op2 = InputRegisterAt(invoke, 1); - vixl32::Register out = OutputRegister(invoke); - - __ Cmp(op1, op2); - - { - ExactAssemblyScope aas(assembler->GetVIXLAssembler(), - 3 * kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); - - __ ite(is_min ? lt : gt); - __ mov(is_min ? lt : gt, out, op1); - __ mov(is_min ? ge : le, out, op2); - } -} - -static void CreateIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathMinIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathMinIntInt(HInvoke* invoke) { - GenMinMax(invoke, /* is_min */ true, GetAssembler()); -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathMaxIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathMaxIntInt(HInvoke* invoke) { - GenMinMax(invoke, /* is_min */ false, GetAssembler()); -} - void IntrinsicLocationsBuilderARMVIXL::VisitMathSqrt(HInvoke* invoke) { CreateFPToFPLocations(allocator_, invoke); } @@ -2137,8 +1802,6 @@ void IntrinsicLocationsBuilderARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { // is clobbered by ReadBarrierMarkRegX entry points). Get an extra // temporary register from the register allocator. locations->AddTemp(Location::RequiresRegister()); - CodeGeneratorARMVIXL* arm_codegen = down_cast(codegen_); - arm_codegen->MaybeAddBakerCcEntrypointTempForFields(locations); } } @@ -3277,33 +2940,27 @@ void IntrinsicLocationsBuilderARMVIXL::VisitIntegerValueOf(HInvoke* invoke) { } void IntrinsicCodeGeneratorARMVIXL::VisitIntegerValueOf(HInvoke* invoke) { - IntrinsicVisitor::IntegerValueOfInfo info = IntrinsicVisitor::ComputeIntegerValueOfInfo(); + IntrinsicVisitor::IntegerValueOfInfo info = + IntrinsicVisitor::ComputeIntegerValueOfInfo(invoke, codegen_->GetCompilerOptions()); LocationSummary* locations = invoke->GetLocations(); ArmVIXLAssembler* const assembler = GetAssembler(); vixl32::Register out = RegisterFrom(locations->Out()); UseScratchRegisterScope temps(assembler->GetVIXLAssembler()); vixl32::Register temp = temps.Acquire(); - InvokeRuntimeCallingConventionARMVIXL calling_convention; - vixl32::Register argument = calling_convention.GetRegisterAt(0); if (invoke->InputAt(0)->IsConstant()) { int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue(); - if (value >= info.low && value <= info.high) { + if (static_cast(value - info.low) < info.length) { // Just embed the j.l.Integer in the code. - ScopedObjectAccess soa(Thread::Current()); - mirror::Object* boxed = info.cache->Get(value + (-info.low)); - DCHECK(boxed != nullptr && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boxed)); - uint32_t address = dchecked_integral_cast(reinterpret_cast(boxed)); - __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address)); + DCHECK_NE(info.value_boot_image_reference, IntegerValueOfInfo::kInvalidReference); + codegen_->LoadBootImageAddress(out, info.value_boot_image_reference); } else { + DCHECK(locations->CanCall()); // Allocate and initialize a new j.l.Integer. // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the // JIT object table. - uint32_t address = - dchecked_integral_cast(reinterpret_cast(info.integer)); - __ Ldr(argument, codegen_->DeduplicateBootImageAddressLiteral(address)); - codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc()); - CheckEntrypointTypes(); + codegen_->AllocateInstanceForIntrinsic(invoke->AsInvokeStaticOrDirect(), + info.integer_boot_image_offset); __ Mov(temp, value); assembler->StoreToOffset(kStoreWord, temp, out, info.value_offset); // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation @@ -3311,25 +2968,22 @@ void IntrinsicCodeGeneratorARMVIXL::VisitIntegerValueOf(HInvoke* invoke) { codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore); } } else { + DCHECK(locations->CanCall()); vixl32::Register in = RegisterFrom(locations->InAt(0)); // Check bounds of our cache. __ Add(out, in, -info.low); - __ Cmp(out, info.high - info.low + 1); + __ Cmp(out, info.length); vixl32::Label allocate, done; __ B(hs, &allocate, /* is_far_target */ false); // If the value is within the bounds, load the j.l.Integer directly from the array. - uint32_t data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value(); - uint32_t address = dchecked_integral_cast(reinterpret_cast(info.cache)); - __ Ldr(temp, codegen_->DeduplicateBootImageAddressLiteral(data_offset + address)); + codegen_->LoadBootImageAddress(temp, info.array_data_boot_image_reference); codegen_->LoadFromShiftedRegOffset(DataType::Type::kReference, locations->Out(), temp, out); assembler->MaybeUnpoisonHeapReference(out); __ B(&done); __ Bind(&allocate); // Otherwise allocate and initialize a new j.l.Integer. - address = dchecked_integral_cast(reinterpret_cast(info.integer)); - __ Ldr(argument, codegen_->DeduplicateBootImageAddressLiteral(address)); - codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc()); - CheckEntrypointTypes(); + codegen_->AllocateInstanceForIntrinsic(invoke->AsInvokeStaticOrDirect(), + info.integer_boot_image_offset); assembler->StoreToOffset(kStoreWord, in, out, info.value_offset); // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation // one. diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index b7936b9c8ec9c0fee342b30e1ff6d8d1cfa82e33..01d9f962f2ab90439e9d60a5465a0d49dcd1ae00 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -58,6 +58,10 @@ inline bool IntrinsicCodeGeneratorMIPS::Is32BitFPU() const { return codegen_->GetInstructionSetFeatures().Is32BitFloatingPoint(); } +inline bool IntrinsicCodeGeneratorMIPS::HasMsa() const { + return codegen_->GetInstructionSetFeatures().HasMsa(); +} + #define __ codegen->GetAssembler()-> static void MoveFromReturnRegister(Location trg, @@ -612,6 +616,7 @@ static void CreateFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { static void GenBitCount(LocationSummary* locations, DataType::Type type, bool isR6, + bool hasMsa, MipsAssembler* assembler) { Register out = locations->Out().AsRegister(); @@ -637,85 +642,102 @@ static void GenBitCount(LocationSummary* locations, // instructions compared to a loop-based algorithm which required 47 // instructions. - if (type == DataType::Type::kInt32) { - Register in = locations->InAt(0).AsRegister(); - - __ Srl(TMP, in, 1); - __ LoadConst32(AT, 0x55555555); - __ And(TMP, TMP, AT); - __ Subu(TMP, in, TMP); - __ LoadConst32(AT, 0x33333333); - __ And(out, TMP, AT); - __ Srl(TMP, TMP, 2); - __ And(TMP, TMP, AT); - __ Addu(TMP, out, TMP); - __ Srl(out, TMP, 4); - __ Addu(out, out, TMP); - __ LoadConst32(AT, 0x0F0F0F0F); - __ And(out, out, AT); - __ LoadConst32(TMP, 0x01010101); - if (isR6) { - __ MulR6(out, out, TMP); + if (hasMsa) { + if (type == DataType::Type::kInt32) { + Register in = locations->InAt(0).AsRegister(); + __ Mtc1(in, FTMP); + __ PcntW(static_cast(FTMP), static_cast(FTMP)); + __ Mfc1(out, FTMP); } else { - __ MulR2(out, out, TMP); + DCHECK_EQ(type, DataType::Type::kInt64); + Register in_lo = locations->InAt(0).AsRegisterPairLow(); + Register in_hi = locations->InAt(0).AsRegisterPairHigh(); + __ Mtc1(in_lo, FTMP); + __ Mthc1(in_hi, FTMP); + __ PcntD(static_cast(FTMP), static_cast(FTMP)); + __ Mfc1(out, FTMP); } - __ Srl(out, out, 24); } else { - DCHECK_EQ(type, DataType::Type::kInt64); - Register in_lo = locations->InAt(0).AsRegisterPairLow(); - Register in_hi = locations->InAt(0).AsRegisterPairHigh(); - Register tmp_hi = locations->GetTemp(0).AsRegister(); - Register out_hi = locations->GetTemp(1).AsRegister(); - Register tmp_lo = TMP; - Register out_lo = out; + if (type == DataType::Type::kInt32) { + Register in = locations->InAt(0).AsRegister(); - __ Srl(tmp_lo, in_lo, 1); - __ Srl(tmp_hi, in_hi, 1); + __ Srl(TMP, in, 1); + __ LoadConst32(AT, 0x55555555); + __ And(TMP, TMP, AT); + __ Subu(TMP, in, TMP); + __ LoadConst32(AT, 0x33333333); + __ And(out, TMP, AT); + __ Srl(TMP, TMP, 2); + __ And(TMP, TMP, AT); + __ Addu(TMP, out, TMP); + __ Srl(out, TMP, 4); + __ Addu(out, out, TMP); + __ LoadConst32(AT, 0x0F0F0F0F); + __ And(out, out, AT); + __ LoadConst32(TMP, 0x01010101); + if (isR6) { + __ MulR6(out, out, TMP); + } else { + __ MulR2(out, out, TMP); + } + __ Srl(out, out, 24); + } else { + DCHECK_EQ(type, DataType::Type::kInt64); + Register in_lo = locations->InAt(0).AsRegisterPairLow(); + Register in_hi = locations->InAt(0).AsRegisterPairHigh(); + Register tmp_hi = locations->GetTemp(0).AsRegister(); + Register out_hi = locations->GetTemp(1).AsRegister(); + Register tmp_lo = TMP; + Register out_lo = out; - __ LoadConst32(AT, 0x55555555); + __ Srl(tmp_lo, in_lo, 1); + __ Srl(tmp_hi, in_hi, 1); - __ And(tmp_lo, tmp_lo, AT); - __ Subu(tmp_lo, in_lo, tmp_lo); + __ LoadConst32(AT, 0x55555555); - __ And(tmp_hi, tmp_hi, AT); - __ Subu(tmp_hi, in_hi, tmp_hi); + __ And(tmp_lo, tmp_lo, AT); + __ Subu(tmp_lo, in_lo, tmp_lo); - __ LoadConst32(AT, 0x33333333); + __ And(tmp_hi, tmp_hi, AT); + __ Subu(tmp_hi, in_hi, tmp_hi); - __ And(out_lo, tmp_lo, AT); - __ Srl(tmp_lo, tmp_lo, 2); - __ And(tmp_lo, tmp_lo, AT); - __ Addu(tmp_lo, out_lo, tmp_lo); + __ LoadConst32(AT, 0x33333333); - __ And(out_hi, tmp_hi, AT); - __ Srl(tmp_hi, tmp_hi, 2); - __ And(tmp_hi, tmp_hi, AT); - __ Addu(tmp_hi, out_hi, tmp_hi); + __ And(out_lo, tmp_lo, AT); + __ Srl(tmp_lo, tmp_lo, 2); + __ And(tmp_lo, tmp_lo, AT); + __ Addu(tmp_lo, out_lo, tmp_lo); - // Here we deviate from the original algorithm a bit. We've reached - // the stage where the bitfields holding the subtotals are large - // enough to hold the combined subtotals for both the low word, and - // the high word. This means that we can add the subtotals for the - // the high, and low words into a single word, and compute the final - // result for both the high, and low words using fewer instructions. - __ LoadConst32(AT, 0x0F0F0F0F); + __ And(out_hi, tmp_hi, AT); + __ Srl(tmp_hi, tmp_hi, 2); + __ And(tmp_hi, tmp_hi, AT); + __ Addu(tmp_hi, out_hi, tmp_hi); - __ Addu(TMP, tmp_hi, tmp_lo); + // Here we deviate from the original algorithm a bit. We've reached + // the stage where the bitfields holding the subtotals are large + // enough to hold the combined subtotals for both the low word, and + // the high word. This means that we can add the subtotals for the + // the high, and low words into a single word, and compute the final + // result for both the high, and low words using fewer instructions. + __ LoadConst32(AT, 0x0F0F0F0F); - __ Srl(out, TMP, 4); - __ And(out, out, AT); - __ And(TMP, TMP, AT); - __ Addu(out, out, TMP); + __ Addu(TMP, tmp_hi, tmp_lo); - __ LoadConst32(AT, 0x01010101); + __ Srl(out, TMP, 4); + __ And(out, out, AT); + __ And(TMP, TMP, AT); + __ Addu(out, out, TMP); - if (isR6) { - __ MulR6(out, out, AT); - } else { - __ MulR2(out, out, AT); - } + __ LoadConst32(AT, 0x01010101); - __ Srl(out, out, 24); + if (isR6) { + __ MulR6(out, out, AT); + } else { + __ MulR2(out, out, AT); + } + + __ Srl(out, out, 24); + } } } @@ -725,7 +747,7 @@ void IntrinsicLocationsBuilderMIPS::VisitIntegerBitCount(HInvoke* invoke) { } void IntrinsicCodeGeneratorMIPS::VisitIntegerBitCount(HInvoke* invoke) { - GenBitCount(invoke->GetLocations(), DataType::Type::kInt32, IsR6(), GetAssembler()); + GenBitCount(invoke->GetLocations(), DataType::Type::kInt32, IsR6(), HasMsa(), GetAssembler()); } // int java.lang.Long.bitCount(int) @@ -739,575 +761,7 @@ void IntrinsicLocationsBuilderMIPS::VisitLongBitCount(HInvoke* invoke) { } void IntrinsicCodeGeneratorMIPS::VisitLongBitCount(HInvoke* invoke) { - GenBitCount(invoke->GetLocations(), DataType::Type::kInt64, IsR6(), GetAssembler()); -} - -static void MathAbsFP(LocationSummary* locations, - bool is64bit, - bool isR2OrNewer, - bool isR6, - MipsAssembler* assembler) { - FRegister in = locations->InAt(0).AsFpuRegister(); - FRegister out = locations->Out().AsFpuRegister(); - - // Note, as a "quality of implementation", rather than pure "spec compliance", we require that - // Math.abs() clears the sign bit (but changes nothing else) for all numbers, including NaN - // (signaling NaN may become quiet though). - // - // The ABS.fmt instructions (abs.s and abs.d) do exactly that when NAN2008=1 (R6). For this case, - // both regular floating point numbers and NAN values are treated alike, only the sign bit is - // affected by this instruction. - // But when NAN2008=0 (R2 and before), the ABS.fmt instructions can't be used. For this case, any - // NaN operand signals invalid operation. This means that other bits (not just sign bit) might be - // changed when doing abs(NaN). Because of that, we clear sign bit in a different way. - if (isR6) { - if (is64bit) { - __ AbsD(out, in); - } else { - __ AbsS(out, in); - } - } else { - if (is64bit) { - if (in != out) { - __ MovD(out, in); - } - __ MoveFromFpuHigh(TMP, in); - // ins instruction is not available for R1. - if (isR2OrNewer) { - __ Ins(TMP, ZERO, 31, 1); - } else { - __ Sll(TMP, TMP, 1); - __ Srl(TMP, TMP, 1); - } - __ MoveToFpuHigh(TMP, out); - } else { - __ Mfc1(TMP, in); - // ins instruction is not available for R1. - if (isR2OrNewer) { - __ Ins(TMP, ZERO, 31, 1); - } else { - __ Sll(TMP, TMP, 1); - __ Srl(TMP, TMP, 1); - } - __ Mtc1(TMP, out); - } - } -} - -// double java.lang.Math.abs(double) -void IntrinsicLocationsBuilderMIPS::VisitMathAbsDouble(HInvoke* invoke) { - CreateFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathAbsDouble(HInvoke* invoke) { - MathAbsFP(invoke->GetLocations(), /* is64bit */ true, IsR2OrNewer(), IsR6(), GetAssembler()); -} - -// float java.lang.Math.abs(float) -void IntrinsicLocationsBuilderMIPS::VisitMathAbsFloat(HInvoke* invoke) { - CreateFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathAbsFloat(HInvoke* invoke) { - MathAbsFP(invoke->GetLocations(), /* is64bit */ false, IsR2OrNewer(), IsR6(), GetAssembler()); -} - -static void GenAbsInteger(LocationSummary* locations, bool is64bit, MipsAssembler* assembler) { - if (is64bit) { - Register in_lo = locations->InAt(0).AsRegisterPairLow(); - Register in_hi = locations->InAt(0).AsRegisterPairHigh(); - Register out_lo = locations->Out().AsRegisterPairLow(); - Register out_hi = locations->Out().AsRegisterPairHigh(); - - // The comments in this section show the analogous operations which would - // be performed if we had 64-bit registers "in", and "out". - // __ Dsra32(AT, in, 31); - __ Sra(AT, in_hi, 31); - // __ Xor(out, in, AT); - __ Xor(TMP, in_lo, AT); - __ Xor(out_hi, in_hi, AT); - // __ Dsubu(out, out, AT); - __ Subu(out_lo, TMP, AT); - __ Sltu(TMP, out_lo, TMP); - __ Addu(out_hi, out_hi, TMP); - } else { - Register in = locations->InAt(0).AsRegister(); - Register out = locations->Out().AsRegister(); - - __ Sra(AT, in, 31); - __ Xor(out, in, AT); - __ Subu(out, out, AT); - } -} - -// int java.lang.Math.abs(int) -void IntrinsicLocationsBuilderMIPS::VisitMathAbsInt(HInvoke* invoke) { - CreateIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathAbsInt(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetAssembler()); -} - -// long java.lang.Math.abs(long) -void IntrinsicLocationsBuilderMIPS::VisitMathAbsLong(HInvoke* invoke) { - CreateIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathAbsLong(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); -} - -static void GenMinMaxFP(LocationSummary* locations, - bool is_min, - DataType::Type type, - bool is_R6, - MipsAssembler* assembler) { - FRegister out = locations->Out().AsFpuRegister(); - FRegister a = locations->InAt(0).AsFpuRegister(); - FRegister b = locations->InAt(1).AsFpuRegister(); - - if (is_R6) { - MipsLabel noNaNs; - MipsLabel done; - FRegister ftmp = ((out != a) && (out != b)) ? out : FTMP; - - // When Java computes min/max it prefers a NaN to a number; the - // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of - // the inputs is a NaN and the other is a valid number, the MIPS - // instruction will return the number; Java wants the NaN value - // returned. This is why there is extra logic preceding the use of - // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a - // NaN, return the NaN, otherwise return the min/max. - if (type == DataType::Type::kFloat64) { - __ CmpUnD(FTMP, a, b); - __ Bc1eqz(FTMP, &noNaNs); - - // One of the inputs is a NaN - __ CmpEqD(ftmp, a, a); - // If a == a then b is the NaN, otherwise a is the NaN. - __ SelD(ftmp, a, b); - - if (ftmp != out) { - __ MovD(out, ftmp); - } - - __ B(&done); - - __ Bind(&noNaNs); - - if (is_min) { - __ MinD(out, a, b); - } else { - __ MaxD(out, a, b); - } - } else { - DCHECK_EQ(type, DataType::Type::kFloat32); - __ CmpUnS(FTMP, a, b); - __ Bc1eqz(FTMP, &noNaNs); - - // One of the inputs is a NaN - __ CmpEqS(ftmp, a, a); - // If a == a then b is the NaN, otherwise a is the NaN. - __ SelS(ftmp, a, b); - - if (ftmp != out) { - __ MovS(out, ftmp); - } - - __ B(&done); - - __ Bind(&noNaNs); - - if (is_min) { - __ MinS(out, a, b); - } else { - __ MaxS(out, a, b); - } - } - - __ Bind(&done); - } else { - MipsLabel ordered; - MipsLabel compare; - MipsLabel select; - MipsLabel done; - - if (type == DataType::Type::kFloat64) { - __ CunD(a, b); - } else { - DCHECK_EQ(type, DataType::Type::kFloat32); - __ CunS(a, b); - } - __ Bc1f(&ordered); - - // a or b (or both) is a NaN. Return one, which is a NaN. - if (type == DataType::Type::kFloat64) { - __ CeqD(b, b); - } else { - __ CeqS(b, b); - } - __ B(&select); - - __ Bind(&ordered); - - // Neither is a NaN. - // a == b? (-0.0 compares equal with +0.0) - // If equal, handle zeroes, else compare further. - if (type == DataType::Type::kFloat64) { - __ CeqD(a, b); - } else { - __ CeqS(a, b); - } - __ Bc1f(&compare); - - // a == b either bit for bit or one is -0.0 and the other is +0.0. - if (type == DataType::Type::kFloat64) { - __ MoveFromFpuHigh(TMP, a); - __ MoveFromFpuHigh(AT, b); - } else { - __ Mfc1(TMP, a); - __ Mfc1(AT, b); - } - - if (is_min) { - // -0.0 prevails over +0.0. - __ Or(TMP, TMP, AT); - } else { - // +0.0 prevails over -0.0. - __ And(TMP, TMP, AT); - } - - if (type == DataType::Type::kFloat64) { - __ Mfc1(AT, a); - __ Mtc1(AT, out); - __ MoveToFpuHigh(TMP, out); - } else { - __ Mtc1(TMP, out); - } - __ B(&done); - - __ Bind(&compare); - - if (type == DataType::Type::kFloat64) { - if (is_min) { - // return (a <= b) ? a : b; - __ ColeD(a, b); - } else { - // return (a >= b) ? a : b; - __ ColeD(b, a); // b <= a - } - } else { - if (is_min) { - // return (a <= b) ? a : b; - __ ColeS(a, b); - } else { - // return (a >= b) ? a : b; - __ ColeS(b, a); // b <= a - } - } - - __ Bind(&select); - - if (type == DataType::Type::kFloat64) { - __ MovtD(out, a); - __ MovfD(out, b); - } else { - __ MovtS(out, a); - __ MovfS(out, b); - } - - __ Bind(&done); - } -} - -static void CreateFPFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetInAt(1, Location::RequiresFpuRegister()); - locations->SetOut(Location::RequiresFpuRegister(), Location::kOutputOverlap); -} - -// double java.lang.Math.min(double, double) -void IntrinsicLocationsBuilderMIPS::VisitMathMinDoubleDouble(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathMinDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), - /* is_min */ true, - DataType::Type::kFloat64, - IsR6(), - GetAssembler()); -} - -// float java.lang.Math.min(float, float) -void IntrinsicLocationsBuilderMIPS::VisitMathMinFloatFloat(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathMinFloatFloat(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), - /* is_min */ true, - DataType::Type::kFloat32, - IsR6(), - GetAssembler()); -} - -// double java.lang.Math.max(double, double) -void IntrinsicLocationsBuilderMIPS::VisitMathMaxDoubleDouble(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathMaxDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), - /* is_min */ false, - DataType::Type::kFloat64, - IsR6(), - GetAssembler()); -} - -// float java.lang.Math.max(float, float) -void IntrinsicLocationsBuilderMIPS::VisitMathMaxFloatFloat(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathMaxFloatFloat(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), - /* is_min */ false, - DataType::Type::kFloat32, - IsR6(), - GetAssembler()); -} - -static void CreateIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); -} - -static void GenMinMax(LocationSummary* locations, - bool is_min, - DataType::Type type, - bool is_R6, - MipsAssembler* assembler) { - if (is_R6) { - // Some architectures, such as ARM and MIPS (prior to r6), have a - // conditional move instruction which only changes the target - // (output) register if the condition is true (MIPS prior to r6 had - // MOVF, MOVT, MOVN, and MOVZ). The SELEQZ and SELNEZ instructions - // always change the target (output) register. If the condition is - // true the output register gets the contents of the "rs" register; - // otherwise, the output register is set to zero. One consequence - // of this is that to implement something like "rd = c==0 ? rs : rt" - // MIPS64r6 needs to use a pair of SELEQZ/SELNEZ instructions. - // After executing this pair of instructions one of the output - // registers from the pair will necessarily contain zero. Then the - // code ORs the output registers from the SELEQZ/SELNEZ instructions - // to get the final result. - // - // The initial test to see if the output register is same as the - // first input register is needed to make sure that value in the - // first input register isn't clobbered before we've finished - // computing the output value. The logic in the corresponding else - // clause performs the same task but makes sure the second input - // register isn't clobbered in the event that it's the same register - // as the output register; the else clause also handles the case - // where the output register is distinct from both the first, and the - // second input registers. - if (type == DataType::Type::kInt64) { - Register a_lo = locations->InAt(0).AsRegisterPairLow(); - Register a_hi = locations->InAt(0).AsRegisterPairHigh(); - Register b_lo = locations->InAt(1).AsRegisterPairLow(); - Register b_hi = locations->InAt(1).AsRegisterPairHigh(); - Register out_lo = locations->Out().AsRegisterPairLow(); - Register out_hi = locations->Out().AsRegisterPairHigh(); - - MipsLabel compare_done; - - if (a_lo == b_lo) { - if (out_lo != a_lo) { - __ Move(out_lo, a_lo); - __ Move(out_hi, a_hi); - } - } else { - __ Slt(TMP, b_hi, a_hi); - __ Bne(b_hi, a_hi, &compare_done); - - __ Sltu(TMP, b_lo, a_lo); - - __ Bind(&compare_done); - - if (is_min) { - __ Seleqz(AT, a_lo, TMP); - __ Selnez(out_lo, b_lo, TMP); // Safe even if out_lo == a_lo/b_lo - // because at this point we're - // done using a_lo/b_lo. - } else { - __ Selnez(AT, a_lo, TMP); - __ Seleqz(out_lo, b_lo, TMP); // ditto - } - __ Or(out_lo, out_lo, AT); - if (is_min) { - __ Seleqz(AT, a_hi, TMP); - __ Selnez(out_hi, b_hi, TMP); // ditto but for out_hi & a_hi/b_hi - } else { - __ Selnez(AT, a_hi, TMP); - __ Seleqz(out_hi, b_hi, TMP); // ditto but for out_hi & a_hi/b_hi - } - __ Or(out_hi, out_hi, AT); - } - } else { - DCHECK_EQ(type, DataType::Type::kInt32); - Register a = locations->InAt(0).AsRegister(); - Register b = locations->InAt(1).AsRegister(); - Register out = locations->Out().AsRegister(); - - if (a == b) { - if (out != a) { - __ Move(out, a); - } - } else { - __ Slt(AT, b, a); - if (is_min) { - __ Seleqz(TMP, a, AT); - __ Selnez(AT, b, AT); - } else { - __ Selnez(TMP, a, AT); - __ Seleqz(AT, b, AT); - } - __ Or(out, TMP, AT); - } - } - } else { - if (type == DataType::Type::kInt64) { - Register a_lo = locations->InAt(0).AsRegisterPairLow(); - Register a_hi = locations->InAt(0).AsRegisterPairHigh(); - Register b_lo = locations->InAt(1).AsRegisterPairLow(); - Register b_hi = locations->InAt(1).AsRegisterPairHigh(); - Register out_lo = locations->Out().AsRegisterPairLow(); - Register out_hi = locations->Out().AsRegisterPairHigh(); - - MipsLabel compare_done; - - if (a_lo == b_lo) { - if (out_lo != a_lo) { - __ Move(out_lo, a_lo); - __ Move(out_hi, a_hi); - } - } else { - __ Slt(TMP, a_hi, b_hi); - __ Bne(a_hi, b_hi, &compare_done); - - __ Sltu(TMP, a_lo, b_lo); - - __ Bind(&compare_done); - - if (is_min) { - if (out_lo != a_lo) { - __ Movn(out_hi, a_hi, TMP); - __ Movn(out_lo, a_lo, TMP); - } - if (out_lo != b_lo) { - __ Movz(out_hi, b_hi, TMP); - __ Movz(out_lo, b_lo, TMP); - } - } else { - if (out_lo != a_lo) { - __ Movz(out_hi, a_hi, TMP); - __ Movz(out_lo, a_lo, TMP); - } - if (out_lo != b_lo) { - __ Movn(out_hi, b_hi, TMP); - __ Movn(out_lo, b_lo, TMP); - } - } - } - } else { - DCHECK_EQ(type, DataType::Type::kInt32); - Register a = locations->InAt(0).AsRegister(); - Register b = locations->InAt(1).AsRegister(); - Register out = locations->Out().AsRegister(); - - if (a == b) { - if (out != a) { - __ Move(out, a); - } - } else { - __ Slt(AT, a, b); - if (is_min) { - if (out != a) { - __ Movn(out, a, AT); - } - if (out != b) { - __ Movz(out, b, AT); - } - } else { - if (out != a) { - __ Movz(out, a, AT); - } - if (out != b) { - __ Movn(out, b, AT); - } - } - } - } - } -} - -// int java.lang.Math.min(int, int) -void IntrinsicLocationsBuilderMIPS::VisitMathMinIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathMinIntInt(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), - /* is_min */ true, - DataType::Type::kInt32, - IsR6(), - GetAssembler()); -} - -// long java.lang.Math.min(long, long) -void IntrinsicLocationsBuilderMIPS::VisitMathMinLongLong(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathMinLongLong(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), - /* is_min */ true, - DataType::Type::kInt64, - IsR6(), - GetAssembler()); -} - -// int java.lang.Math.max(int, int) -void IntrinsicLocationsBuilderMIPS::VisitMathMaxIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathMaxIntInt(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), - /* is_min */ false, - DataType::Type::kInt32, - IsR6(), - GetAssembler()); -} - -// long java.lang.Math.max(long, long) -void IntrinsicLocationsBuilderMIPS::VisitMathMaxLongLong(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathMaxLongLong(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), - /* is_min */ false, - DataType::Type::kInt64, - IsR6(), - GetAssembler()); + GenBitCount(invoke->GetLocations(), DataType::Type::kInt64, IsR6(), HasMsa(), GetAssembler()); } // double java.lang.Math.sqrt(double) @@ -3147,59 +2601,50 @@ void IntrinsicLocationsBuilderMIPS::VisitIntegerValueOf(HInvoke* invoke) { } void IntrinsicCodeGeneratorMIPS::VisitIntegerValueOf(HInvoke* invoke) { - IntrinsicVisitor::IntegerValueOfInfo info = IntrinsicVisitor::ComputeIntegerValueOfInfo(); + IntrinsicVisitor::IntegerValueOfInfo info = + IntrinsicVisitor::ComputeIntegerValueOfInfo(invoke, codegen_->GetCompilerOptions()); LocationSummary* locations = invoke->GetLocations(); MipsAssembler* assembler = GetAssembler(); InstructionCodeGeneratorMIPS* icodegen = down_cast(codegen_->GetInstructionVisitor()); Register out = locations->Out().AsRegister(); - InvokeRuntimeCallingConvention calling_convention; if (invoke->InputAt(0)->IsConstant()) { int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue(); - if (value >= info.low && value <= info.high) { + if (static_cast(value - info.low) < info.length) { // Just embed the j.l.Integer in the code. - ScopedObjectAccess soa(Thread::Current()); - mirror::Object* boxed = info.cache->Get(value + (-info.low)); - DCHECK(boxed != nullptr && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boxed)); - uint32_t address = dchecked_integral_cast(reinterpret_cast(boxed)); - __ LoadConst32(out, address); + DCHECK_NE(info.value_boot_image_reference, IntegerValueOfInfo::kInvalidReference); + codegen_->LoadBootImageAddress(out, info.value_boot_image_reference); } else { + DCHECK(locations->CanCall()); // Allocate and initialize a new j.l.Integer. // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the // JIT object table. - uint32_t address = - dchecked_integral_cast(reinterpret_cast(info.integer)); - __ LoadConst32(calling_convention.GetRegisterAt(0), address); - codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc()); - CheckEntrypointTypes(); + codegen_->AllocateInstanceForIntrinsic(invoke->AsInvokeStaticOrDirect(), + info.integer_boot_image_offset); __ StoreConstToOffset(kStoreWord, value, out, info.value_offset, TMP); // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation // one. icodegen->GenerateMemoryBarrier(MemBarrierKind::kStoreStore); } } else { + DCHECK(locations->CanCall()); Register in = locations->InAt(0).AsRegister(); MipsLabel allocate, done; - int32_t count = static_cast(info.high) - info.low + 1; - // Is (info.low <= in) && (in <= info.high)? __ Addiu32(out, in, -info.low); - // As unsigned quantities is out < (info.high - info.low + 1)? - if (IsInt<16>(count)) { - __ Sltiu(AT, out, count); + // As unsigned quantities is out < info.length ? + if (IsUint<15>(info.length)) { + __ Sltiu(AT, out, info.length); } else { - __ LoadConst32(AT, count); + __ LoadConst32(AT, info.length); __ Sltu(AT, out, AT); } - // Branch if out >= (info.high - info.low + 1). - // This means that "in" is outside of the range [info.low, info.high]. + // Branch if out >= info.length. This means that "in" is outside of the valid range. __ Beqz(AT, &allocate); // If the value is within the bounds, load the j.l.Integer directly from the array. - uint32_t data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value(); - uint32_t address = dchecked_integral_cast(reinterpret_cast(info.cache)); - __ LoadConst32(TMP, data_offset + address); + codegen_->LoadBootImageAddress(TMP, info.array_data_boot_image_reference); __ ShiftAndAdd(out, out, TMP, TIMES_4); __ Lw(out, out, 0); __ MaybeUnpoisonHeapReference(out); @@ -3207,10 +2652,8 @@ void IntrinsicCodeGeneratorMIPS::VisitIntegerValueOf(HInvoke* invoke) { __ Bind(&allocate); // Otherwise allocate and initialize a new j.l.Integer. - address = dchecked_integral_cast(reinterpret_cast(info.integer)); - __ LoadConst32(calling_convention.GetRegisterAt(0), address); - codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc()); - CheckEntrypointTypes(); + codegen_->AllocateInstanceForIntrinsic(invoke->AsInvokeStaticOrDirect(), + info.integer_boot_image_offset); __ StoreToOffset(kStoreWord, in, out, info.value_offset); // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation // one. diff --git a/compiler/optimizing/intrinsics_mips.h b/compiler/optimizing/intrinsics_mips.h index 13397f11d4ceb2de04e2bb6b1b27440f06fda6d1..1c1ba401325a84fed9e84b1ecbb7d370e92cab63 100644 --- a/compiler/optimizing/intrinsics_mips.h +++ b/compiler/optimizing/intrinsics_mips.h @@ -71,6 +71,7 @@ class IntrinsicCodeGeneratorMIPS FINAL : public IntrinsicVisitor { bool IsR2OrNewer() const; bool IsR6() const; bool Is32BitFPU() const; + bool HasMsa() const; private: MipsAssembler* GetAssembler(); diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 4668c561ed36720a881d8d371b34edcf6aec6c29..0bd69c6ec84c0e8c85ee89dafaf61bfb177d999c 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -46,6 +46,10 @@ ArenaAllocator* IntrinsicCodeGeneratorMIPS64::GetAllocator() { return codegen_->GetGraph()->GetAllocator(); } +inline bool IntrinsicCodeGeneratorMIPS64::HasMsa() const { + return codegen_->GetInstructionSetFeatures().HasMsa(); +} + #define __ codegen->GetAssembler()-> static void MoveFromReturnRegister(Location trg, @@ -386,6 +390,7 @@ static void CreateFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { static void GenBitCount(LocationSummary* locations, const DataType::Type type, + const bool hasMsa, Mips64Assembler* assembler) { GpuRegister out = locations->Out().AsRegister(); GpuRegister in = locations->InAt(0).AsRegister(); @@ -414,41 +419,52 @@ static void GenBitCount(LocationSummary* locations, // bits are set but the algorithm here attempts to minimize the total // number of instructions executed even when a large number of bits // are set. - - if (type == DataType::Type::kInt32) { - __ Srl(TMP, in, 1); - __ LoadConst32(AT, 0x55555555); - __ And(TMP, TMP, AT); - __ Subu(TMP, in, TMP); - __ LoadConst32(AT, 0x33333333); - __ And(out, TMP, AT); - __ Srl(TMP, TMP, 2); - __ And(TMP, TMP, AT); - __ Addu(TMP, out, TMP); - __ Srl(out, TMP, 4); - __ Addu(out, out, TMP); - __ LoadConst32(AT, 0x0F0F0F0F); - __ And(out, out, AT); - __ LoadConst32(TMP, 0x01010101); - __ MulR6(out, out, TMP); - __ Srl(out, out, 24); - } else if (type == DataType::Type::kInt64) { - __ Dsrl(TMP, in, 1); - __ LoadConst64(AT, 0x5555555555555555L); - __ And(TMP, TMP, AT); - __ Dsubu(TMP, in, TMP); - __ LoadConst64(AT, 0x3333333333333333L); - __ And(out, TMP, AT); - __ Dsrl(TMP, TMP, 2); - __ And(TMP, TMP, AT); - __ Daddu(TMP, out, TMP); - __ Dsrl(out, TMP, 4); - __ Daddu(out, out, TMP); - __ LoadConst64(AT, 0x0F0F0F0F0F0F0F0FL); - __ And(out, out, AT); - __ LoadConst64(TMP, 0x0101010101010101L); - __ Dmul(out, out, TMP); - __ Dsrl32(out, out, 24); + if (hasMsa) { + if (type == DataType::Type::kInt32) { + __ Mtc1(in, FTMP); + __ PcntW(static_cast(FTMP), static_cast(FTMP)); + __ Mfc1(out, FTMP); + } else { + __ Dmtc1(in, FTMP); + __ PcntD(static_cast(FTMP), static_cast(FTMP)); + __ Dmfc1(out, FTMP); + } + } else { + if (type == DataType::Type::kInt32) { + __ Srl(TMP, in, 1); + __ LoadConst32(AT, 0x55555555); + __ And(TMP, TMP, AT); + __ Subu(TMP, in, TMP); + __ LoadConst32(AT, 0x33333333); + __ And(out, TMP, AT); + __ Srl(TMP, TMP, 2); + __ And(TMP, TMP, AT); + __ Addu(TMP, out, TMP); + __ Srl(out, TMP, 4); + __ Addu(out, out, TMP); + __ LoadConst32(AT, 0x0F0F0F0F); + __ And(out, out, AT); + __ LoadConst32(TMP, 0x01010101); + __ MulR6(out, out, TMP); + __ Srl(out, out, 24); + } else { + __ Dsrl(TMP, in, 1); + __ LoadConst64(AT, 0x5555555555555555L); + __ And(TMP, TMP, AT); + __ Dsubu(TMP, in, TMP); + __ LoadConst64(AT, 0x3333333333333333L); + __ And(out, TMP, AT); + __ Dsrl(TMP, TMP, 2); + __ And(TMP, TMP, AT); + __ Daddu(TMP, out, TMP); + __ Dsrl(out, TMP, 4); + __ Daddu(out, out, TMP); + __ LoadConst64(AT, 0x0F0F0F0F0F0F0F0FL); + __ And(out, out, AT); + __ LoadConst64(TMP, 0x0101010101010101L); + __ Dmul(out, out, TMP); + __ Dsrl32(out, out, 24); + } } } @@ -458,7 +474,7 @@ void IntrinsicLocationsBuilderMIPS64::VisitIntegerBitCount(HInvoke* invoke) { } void IntrinsicCodeGeneratorMIPS64::VisitIntegerBitCount(HInvoke* invoke) { - GenBitCount(invoke->GetLocations(), DataType::Type::kInt32, GetAssembler()); + GenBitCount(invoke->GetLocations(), DataType::Type::kInt32, HasMsa(), GetAssembler()); } // int java.lang.Long.bitCount(long) @@ -467,291 +483,7 @@ void IntrinsicLocationsBuilderMIPS64::VisitLongBitCount(HInvoke* invoke) { } void IntrinsicCodeGeneratorMIPS64::VisitLongBitCount(HInvoke* invoke) { - GenBitCount(invoke->GetLocations(), DataType::Type::kInt64, GetAssembler()); -} - -static void MathAbsFP(LocationSummary* locations, bool is64bit, Mips64Assembler* assembler) { - FpuRegister in = locations->InAt(0).AsFpuRegister(); - FpuRegister out = locations->Out().AsFpuRegister(); - - if (is64bit) { - __ AbsD(out, in); - } else { - __ AbsS(out, in); - } -} - -// double java.lang.Math.abs(double) -void IntrinsicLocationsBuilderMIPS64::VisitMathAbsDouble(HInvoke* invoke) { - CreateFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathAbsDouble(HInvoke* invoke) { - MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); -} - -// float java.lang.Math.abs(float) -void IntrinsicLocationsBuilderMIPS64::VisitMathAbsFloat(HInvoke* invoke) { - CreateFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathAbsFloat(HInvoke* invoke) { - MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler()); -} - -static void CreateIntToInt(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); -} - -static void GenAbsInteger(LocationSummary* locations, bool is64bit, Mips64Assembler* assembler) { - GpuRegister in = locations->InAt(0).AsRegister(); - GpuRegister out = locations->Out().AsRegister(); - - if (is64bit) { - __ Dsra32(AT, in, 31); - __ Xor(out, in, AT); - __ Dsubu(out, out, AT); - } else { - __ Sra(AT, in, 31); - __ Xor(out, in, AT); - __ Subu(out, out, AT); - } -} - -// int java.lang.Math.abs(int) -void IntrinsicLocationsBuilderMIPS64::VisitMathAbsInt(HInvoke* invoke) { - CreateIntToInt(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathAbsInt(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetAssembler()); -} - -// long java.lang.Math.abs(long) -void IntrinsicLocationsBuilderMIPS64::VisitMathAbsLong(HInvoke* invoke) { - CreateIntToInt(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathAbsLong(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); -} - -static void GenMinMaxFP(LocationSummary* locations, - bool is_min, - DataType::Type type, - Mips64Assembler* assembler) { - FpuRegister a = locations->InAt(0).AsFpuRegister(); - FpuRegister b = locations->InAt(1).AsFpuRegister(); - FpuRegister out = locations->Out().AsFpuRegister(); - - Mips64Label noNaNs; - Mips64Label done; - FpuRegister ftmp = ((out != a) && (out != b)) ? out : FTMP; - - // When Java computes min/max it prefers a NaN to a number; the - // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of - // the inputs is a NaN and the other is a valid number, the MIPS - // instruction will return the number; Java wants the NaN value - // returned. This is why there is extra logic preceding the use of - // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a - // NaN, return the NaN, otherwise return the min/max. - if (type == DataType::Type::kFloat64) { - __ CmpUnD(FTMP, a, b); - __ Bc1eqz(FTMP, &noNaNs); - - // One of the inputs is a NaN - __ CmpEqD(ftmp, a, a); - // If a == a then b is the NaN, otherwise a is the NaN. - __ SelD(ftmp, a, b); - - if (ftmp != out) { - __ MovD(out, ftmp); - } - - __ Bc(&done); - - __ Bind(&noNaNs); - - if (is_min) { - __ MinD(out, a, b); - } else { - __ MaxD(out, a, b); - } - } else { - DCHECK_EQ(type, DataType::Type::kFloat32); - __ CmpUnS(FTMP, a, b); - __ Bc1eqz(FTMP, &noNaNs); - - // One of the inputs is a NaN - __ CmpEqS(ftmp, a, a); - // If a == a then b is the NaN, otherwise a is the NaN. - __ SelS(ftmp, a, b); - - if (ftmp != out) { - __ MovS(out, ftmp); - } - - __ Bc(&done); - - __ Bind(&noNaNs); - - if (is_min) { - __ MinS(out, a, b); - } else { - __ MaxS(out, a, b); - } - } - - __ Bind(&done); -} - -static void CreateFPFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetInAt(1, Location::RequiresFpuRegister()); - locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); -} - -// double java.lang.Math.min(double, double) -void IntrinsicLocationsBuilderMIPS64::VisitMathMinDoubleDouble(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathMinDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, DataType::Type::kFloat64, GetAssembler()); -} - -// float java.lang.Math.min(float, float) -void IntrinsicLocationsBuilderMIPS64::VisitMathMinFloatFloat(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathMinFloatFloat(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, DataType::Type::kFloat32, GetAssembler()); -} - -// double java.lang.Math.max(double, double) -void IntrinsicLocationsBuilderMIPS64::VisitMathMaxDoubleDouble(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathMaxDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, DataType::Type::kFloat64, GetAssembler()); -} - -// float java.lang.Math.max(float, float) -void IntrinsicLocationsBuilderMIPS64::VisitMathMaxFloatFloat(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathMaxFloatFloat(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, DataType::Type::kFloat32, GetAssembler()); -} - -static void GenMinMax(LocationSummary* locations, - bool is_min, - Mips64Assembler* assembler) { - GpuRegister lhs = locations->InAt(0).AsRegister(); - GpuRegister rhs = locations->InAt(1).AsRegister(); - GpuRegister out = locations->Out().AsRegister(); - - if (lhs == rhs) { - if (out != lhs) { - __ Move(out, lhs); - } - } else { - // Some architectures, such as ARM and MIPS (prior to r6), have a - // conditional move instruction which only changes the target - // (output) register if the condition is true (MIPS prior to r6 had - // MOVF, MOVT, and MOVZ). The SELEQZ and SELNEZ instructions always - // change the target (output) register. If the condition is true the - // output register gets the contents of the "rs" register; otherwise, - // the output register is set to zero. One consequence of this is - // that to implement something like "rd = c==0 ? rs : rt" MIPS64r6 - // needs to use a pair of SELEQZ/SELNEZ instructions. After - // executing this pair of instructions one of the output registers - // from the pair will necessarily contain zero. Then the code ORs the - // output registers from the SELEQZ/SELNEZ instructions to get the - // final result. - // - // The initial test to see if the output register is same as the - // first input register is needed to make sure that value in the - // first input register isn't clobbered before we've finished - // computing the output value. The logic in the corresponding else - // clause performs the same task but makes sure the second input - // register isn't clobbered in the event that it's the same register - // as the output register; the else clause also handles the case - // where the output register is distinct from both the first, and the - // second input registers. - if (out == lhs) { - __ Slt(AT, rhs, lhs); - if (is_min) { - __ Seleqz(out, lhs, AT); - __ Selnez(AT, rhs, AT); - } else { - __ Selnez(out, lhs, AT); - __ Seleqz(AT, rhs, AT); - } - } else { - __ Slt(AT, lhs, rhs); - if (is_min) { - __ Seleqz(out, rhs, AT); - __ Selnez(AT, lhs, AT); - } else { - __ Selnez(out, rhs, AT); - __ Seleqz(AT, lhs, AT); - } - } - __ Or(out, out, AT); - } -} - -static void CreateIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); -} - -// int java.lang.Math.min(int, int) -void IntrinsicLocationsBuilderMIPS64::VisitMathMinIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathMinIntInt(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ true, GetAssembler()); -} - -// long java.lang.Math.min(long, long) -void IntrinsicLocationsBuilderMIPS64::VisitMathMinLongLong(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathMinLongLong(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ true, GetAssembler()); -} - -// int java.lang.Math.max(int, int) -void IntrinsicLocationsBuilderMIPS64::VisitMathMaxIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathMaxIntInt(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ false, GetAssembler()); -} - -// long java.lang.Math.max(long, long) -void IntrinsicLocationsBuilderMIPS64::VisitMathMaxLongLong(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathMaxLongLong(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ false, GetAssembler()); + GenBitCount(invoke->GetLocations(), DataType::Type::kInt64, HasMsa(), GetAssembler()); } // double java.lang.Math.sqrt(double) @@ -2535,54 +2267,45 @@ void IntrinsicLocationsBuilderMIPS64::VisitIntegerValueOf(HInvoke* invoke) { } void IntrinsicCodeGeneratorMIPS64::VisitIntegerValueOf(HInvoke* invoke) { - IntrinsicVisitor::IntegerValueOfInfo info = IntrinsicVisitor::ComputeIntegerValueOfInfo(); + IntrinsicVisitor::IntegerValueOfInfo info = + IntrinsicVisitor::ComputeIntegerValueOfInfo(invoke, codegen_->GetCompilerOptions()); LocationSummary* locations = invoke->GetLocations(); Mips64Assembler* assembler = GetAssembler(); InstructionCodeGeneratorMIPS64* icodegen = down_cast(codegen_->GetInstructionVisitor()); GpuRegister out = locations->Out().AsRegister(); - InvokeRuntimeCallingConvention calling_convention; if (invoke->InputAt(0)->IsConstant()) { int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue(); - if (value >= info.low && value <= info.high) { + if (static_cast(value - info.low) < info.length) { // Just embed the j.l.Integer in the code. - ScopedObjectAccess soa(Thread::Current()); - mirror::Object* boxed = info.cache->Get(value + (-info.low)); - DCHECK(boxed != nullptr && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boxed)); - uint32_t address = dchecked_integral_cast(reinterpret_cast(boxed)); - __ LoadConst64(out, address); + DCHECK_NE(info.value_boot_image_reference, IntegerValueOfInfo::kInvalidReference); + codegen_->LoadBootImageAddress(out, info.value_boot_image_reference); } else { + DCHECK(locations->CanCall()); // Allocate and initialize a new j.l.Integer. // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the // JIT object table. - uint32_t address = - dchecked_integral_cast(reinterpret_cast(info.integer)); - __ LoadConst64(calling_convention.GetRegisterAt(0), address); - codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc()); - CheckEntrypointTypes(); + codegen_->AllocateInstanceForIntrinsic(invoke->AsInvokeStaticOrDirect(), + info.integer_boot_image_offset); __ StoreConstToOffset(kStoreWord, value, out, info.value_offset, TMP); // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation // one. icodegen->GenerateMemoryBarrier(MemBarrierKind::kStoreStore); } } else { + DCHECK(locations->CanCall()); GpuRegister in = locations->InAt(0).AsRegister(); Mips64Label allocate, done; - int32_t count = static_cast(info.high) - info.low + 1; - // Is (info.low <= in) && (in <= info.high)? __ Addiu32(out, in, -info.low); - // As unsigned quantities is out < (info.high - info.low + 1)? - __ LoadConst32(AT, count); - // Branch if out >= (info.high - info.low + 1). - // This means that "in" is outside of the range [info.low, info.high]. + // As unsigned quantities is out < info.length ? + __ LoadConst32(AT, info.length); + // Branch if out >= info.length . This means that "in" is outside of the valid range. __ Bgeuc(out, AT, &allocate); // If the value is within the bounds, load the j.l.Integer directly from the array. - uint32_t data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value(); - uint32_t address = dchecked_integral_cast(reinterpret_cast(info.cache)); - __ LoadConst64(TMP, data_offset + address); + codegen_->LoadBootImageAddress(TMP, info.array_data_boot_image_reference); __ Dlsa(out, out, TMP, TIMES_4); __ Lwu(out, out, 0); __ MaybeUnpoisonHeapReference(out); @@ -2590,10 +2313,8 @@ void IntrinsicCodeGeneratorMIPS64::VisitIntegerValueOf(HInvoke* invoke) { __ Bind(&allocate); // Otherwise allocate and initialize a new j.l.Integer. - address = dchecked_integral_cast(reinterpret_cast(info.integer)); - __ LoadConst64(calling_convention.GetRegisterAt(0), address); - codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc()); - CheckEntrypointTypes(); + codegen_->AllocateInstanceForIntrinsic(invoke->AsInvokeStaticOrDirect(), + info.integer_boot_image_offset); __ StoreToOffset(kStoreWord, in, out, info.value_offset); // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation // one. diff --git a/compiler/optimizing/intrinsics_mips64.h b/compiler/optimizing/intrinsics_mips64.h index 6f40d90ddbffb2951055d24842364932b1e80be6..748b0b02b2e8d2ab0f23a54e26ee1fbe1b9e9928 100644 --- a/compiler/optimizing/intrinsics_mips64.h +++ b/compiler/optimizing/intrinsics_mips64.h @@ -68,6 +68,8 @@ class IntrinsicCodeGeneratorMIPS64 FINAL : public IntrinsicVisitor { #undef INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS + bool HasMsa() const; + private: Mips64Assembler* GetAssembler(); diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 0763ef235297371d1420758c8c5911558c296ca9..98cea35af154cb5e9af775467a31b135973efdee 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -40,11 +40,6 @@ namespace art { namespace x86 { -static constexpr int kDoubleNaNHigh = 0x7FF80000; -static constexpr int kDoubleNaNLow = 0x00000000; -static constexpr int64_t kDoubleNaN = INT64_C(0x7FF8000000000000); -static constexpr int32_t kFloatNaN = INT32_C(0x7FC00000); - IntrinsicLocationsBuilderX86::IntrinsicLocationsBuilderX86(CodeGeneratorX86* codegen) : allocator_(codegen->GetGraph()->GetAllocator()), codegen_(codegen) { @@ -333,432 +328,6 @@ void IntrinsicCodeGeneratorX86::VisitShortReverseBytes(HInvoke* invoke) { GenReverseBytes(invoke->GetLocations(), DataType::Type::kInt16, GetAssembler()); } - -// TODO: Consider Quick's way of doing Double abs through integer operations, as the immediate we -// need is 64b. - -static void CreateFloatToFloat(ArenaAllocator* allocator, HInvoke* invoke) { - // TODO: Enable memory operations when the assembler supports them. - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetOut(Location::SameAsFirstInput()); - HInvokeStaticOrDirect* static_or_direct = invoke->AsInvokeStaticOrDirect(); - DCHECK(static_or_direct != nullptr); - if (static_or_direct->HasSpecialInput() && - invoke->InputAt(static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) { - // We need addressibility for the constant area. - locations->SetInAt(1, Location::RequiresRegister()); - // We need a temporary to hold the constant. - locations->AddTemp(Location::RequiresFpuRegister()); - } -} - -static void MathAbsFP(HInvoke* invoke, - bool is64bit, - X86Assembler* assembler, - CodeGeneratorX86* codegen) { - LocationSummary* locations = invoke->GetLocations(); - Location output = locations->Out(); - - DCHECK(output.IsFpuRegister()); - if (locations->GetInputCount() == 2 && locations->InAt(1).IsValid()) { - HX86ComputeBaseMethodAddress* method_address = - invoke->InputAt(1)->AsX86ComputeBaseMethodAddress(); - DCHECK(locations->InAt(1).IsRegister()); - // We also have a constant area pointer. - Register constant_area = locations->InAt(1).AsRegister(); - XmmRegister temp = locations->GetTemp(0).AsFpuRegister(); - if (is64bit) { - __ movsd(temp, codegen->LiteralInt64Address( - INT64_C(0x7FFFFFFFFFFFFFFF), method_address, constant_area)); - __ andpd(output.AsFpuRegister(), temp); - } else { - __ movss(temp, codegen->LiteralInt32Address( - INT32_C(0x7FFFFFFF), method_address, constant_area)); - __ andps(output.AsFpuRegister(), temp); - } - } else { - // Create the right constant on an aligned stack. - if (is64bit) { - __ subl(ESP, Immediate(8)); - __ pushl(Immediate(0x7FFFFFFF)); - __ pushl(Immediate(0xFFFFFFFF)); - __ andpd(output.AsFpuRegister(), Address(ESP, 0)); - } else { - __ subl(ESP, Immediate(12)); - __ pushl(Immediate(0x7FFFFFFF)); - __ andps(output.AsFpuRegister(), Address(ESP, 0)); - } - __ addl(ESP, Immediate(16)); - } -} - -void IntrinsicLocationsBuilderX86::VisitMathAbsDouble(HInvoke* invoke) { - CreateFloatToFloat(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathAbsDouble(HInvoke* invoke) { - MathAbsFP(invoke, /* is64bit */ true, GetAssembler(), codegen_); -} - -void IntrinsicLocationsBuilderX86::VisitMathAbsFloat(HInvoke* invoke) { - CreateFloatToFloat(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathAbsFloat(HInvoke* invoke) { - MathAbsFP(invoke, /* is64bit */ false, GetAssembler(), codegen_); -} - -static void CreateAbsIntLocation(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RegisterLocation(EAX)); - locations->SetOut(Location::SameAsFirstInput()); - locations->AddTemp(Location::RegisterLocation(EDX)); -} - -static void GenAbsInteger(LocationSummary* locations, X86Assembler* assembler) { - Location output = locations->Out(); - Register out = output.AsRegister(); - DCHECK_EQ(out, EAX); - Register temp = locations->GetTemp(0).AsRegister(); - DCHECK_EQ(temp, EDX); - - // Sign extend EAX into EDX. - __ cdq(); - - // XOR EAX with sign. - __ xorl(EAX, EDX); - - // Subtract out sign to correct. - __ subl(EAX, EDX); - - // The result is in EAX. -} - -static void CreateAbsLongLocation(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); - locations->AddTemp(Location::RequiresRegister()); -} - -static void GenAbsLong(LocationSummary* locations, X86Assembler* assembler) { - Location input = locations->InAt(0); - Register input_lo = input.AsRegisterPairLow(); - Register input_hi = input.AsRegisterPairHigh(); - Location output = locations->Out(); - Register output_lo = output.AsRegisterPairLow(); - Register output_hi = output.AsRegisterPairHigh(); - Register temp = locations->GetTemp(0).AsRegister(); - - // Compute the sign into the temporary. - __ movl(temp, input_hi); - __ sarl(temp, Immediate(31)); - - // Store the sign into the output. - __ movl(output_lo, temp); - __ movl(output_hi, temp); - - // XOR the input to the output. - __ xorl(output_lo, input_lo); - __ xorl(output_hi, input_hi); - - // Subtract the sign. - __ subl(output_lo, temp); - __ sbbl(output_hi, temp); -} - -void IntrinsicLocationsBuilderX86::VisitMathAbsInt(HInvoke* invoke) { - CreateAbsIntLocation(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathAbsInt(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), GetAssembler()); -} - -void IntrinsicLocationsBuilderX86::VisitMathAbsLong(HInvoke* invoke) { - CreateAbsLongLocation(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathAbsLong(HInvoke* invoke) { - GenAbsLong(invoke->GetLocations(), GetAssembler()); -} - -static void GenMinMaxFP(HInvoke* invoke, - bool is_min, - bool is_double, - X86Assembler* assembler, - CodeGeneratorX86* codegen) { - LocationSummary* locations = invoke->GetLocations(); - Location op1_loc = locations->InAt(0); - Location op2_loc = locations->InAt(1); - Location out_loc = locations->Out(); - XmmRegister out = out_loc.AsFpuRegister(); - - // Shortcut for same input locations. - if (op1_loc.Equals(op2_loc)) { - DCHECK(out_loc.Equals(op1_loc)); - return; - } - - // (out := op1) - // out <=? op2 - // if Nan jmp Nan_label - // if out is min jmp done - // if op2 is min jmp op2_label - // handle -0/+0 - // jmp done - // Nan_label: - // out := NaN - // op2_label: - // out := op2 - // done: - // - // This removes one jmp, but needs to copy one input (op1) to out. - // - // TODO: This is straight from Quick (except literal pool). Make NaN an out-of-line slowpath? - - XmmRegister op2 = op2_loc.AsFpuRegister(); - - NearLabel nan, done, op2_label; - if (is_double) { - __ ucomisd(out, op2); - } else { - __ ucomiss(out, op2); - } - - __ j(Condition::kParityEven, &nan); - - __ j(is_min ? Condition::kAbove : Condition::kBelow, &op2_label); - __ j(is_min ? Condition::kBelow : Condition::kAbove, &done); - - // Handle 0.0/-0.0. - if (is_min) { - if (is_double) { - __ orpd(out, op2); - } else { - __ orps(out, op2); - } - } else { - if (is_double) { - __ andpd(out, op2); - } else { - __ andps(out, op2); - } - } - __ jmp(&done); - - // NaN handling. - __ Bind(&nan); - // Do we have a constant area pointer? - if (locations->GetInputCount() == 3 && locations->InAt(2).IsValid()) { - HX86ComputeBaseMethodAddress* method_address = - invoke->InputAt(2)->AsX86ComputeBaseMethodAddress(); - DCHECK(locations->InAt(2).IsRegister()); - Register constant_area = locations->InAt(2).AsRegister(); - if (is_double) { - __ movsd(out, codegen->LiteralInt64Address(kDoubleNaN, method_address, constant_area)); - } else { - __ movss(out, codegen->LiteralInt32Address(kFloatNaN, method_address, constant_area)); - } - } else { - if (is_double) { - __ pushl(Immediate(kDoubleNaNHigh)); - __ pushl(Immediate(kDoubleNaNLow)); - __ movsd(out, Address(ESP, 0)); - __ addl(ESP, Immediate(8)); - } else { - __ pushl(Immediate(kFloatNaN)); - __ movss(out, Address(ESP, 0)); - __ addl(ESP, Immediate(4)); - } - } - __ jmp(&done); - - // out := op2; - __ Bind(&op2_label); - if (is_double) { - __ movsd(out, op2); - } else { - __ movss(out, op2); - } - - // Done. - __ Bind(&done); -} - -static void CreateFPFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetInAt(1, Location::RequiresFpuRegister()); - // The following is sub-optimal, but all we can do for now. It would be fine to also accept - // the second input to be the output (we can simply swap inputs). - locations->SetOut(Location::SameAsFirstInput()); - HInvokeStaticOrDirect* static_or_direct = invoke->AsInvokeStaticOrDirect(); - DCHECK(static_or_direct != nullptr); - if (static_or_direct->HasSpecialInput() && - invoke->InputAt(static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) { - locations->SetInAt(2, Location::RequiresRegister()); - } -} - -void IntrinsicLocationsBuilderX86::VisitMathMinDoubleDouble(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathMinDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke, - /* is_min */ true, - /* is_double */ true, - GetAssembler(), - codegen_); -} - -void IntrinsicLocationsBuilderX86::VisitMathMinFloatFloat(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathMinFloatFloat(HInvoke* invoke) { - GenMinMaxFP(invoke, - /* is_min */ true, - /* is_double */ false, - GetAssembler(), - codegen_); -} - -void IntrinsicLocationsBuilderX86::VisitMathMaxDoubleDouble(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathMaxDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke, - /* is_min */ false, - /* is_double */ true, - GetAssembler(), - codegen_); -} - -void IntrinsicLocationsBuilderX86::VisitMathMaxFloatFloat(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathMaxFloatFloat(HInvoke* invoke) { - GenMinMaxFP(invoke, - /* is_min */ false, - /* is_double */ false, - GetAssembler(), - codegen_); -} - -static void GenMinMax(LocationSummary* locations, bool is_min, bool is_long, - X86Assembler* assembler) { - Location op1_loc = locations->InAt(0); - Location op2_loc = locations->InAt(1); - - // Shortcut for same input locations. - if (op1_loc.Equals(op2_loc)) { - // Can return immediately, as op1_loc == out_loc. - // Note: if we ever support separate registers, e.g., output into memory, we need to check for - // a copy here. - DCHECK(locations->Out().Equals(op1_loc)); - return; - } - - if (is_long) { - // Need to perform a subtract to get the sign right. - // op1 is already in the same location as the output. - Location output = locations->Out(); - Register output_lo = output.AsRegisterPairLow(); - Register output_hi = output.AsRegisterPairHigh(); - - Register op2_lo = op2_loc.AsRegisterPairLow(); - Register op2_hi = op2_loc.AsRegisterPairHigh(); - - // Spare register to compute the subtraction to set condition code. - Register temp = locations->GetTemp(0).AsRegister(); - - // Subtract off op2_low. - __ movl(temp, output_lo); - __ subl(temp, op2_lo); - - // Now use the same tempo and the borrow to finish the subtraction of op2_hi. - __ movl(temp, output_hi); - __ sbbl(temp, op2_hi); - - // Now the condition code is correct. - Condition cond = is_min ? Condition::kGreaterEqual : Condition::kLess; - __ cmovl(cond, output_lo, op2_lo); - __ cmovl(cond, output_hi, op2_hi); - } else { - Register out = locations->Out().AsRegister(); - Register op2 = op2_loc.AsRegister(); - - // (out := op1) - // out <=? op2 - // if out is min jmp done - // out := op2 - // done: - - __ cmpl(out, op2); - Condition cond = is_min ? Condition::kGreater : Condition::kLess; - __ cmovl(cond, out, op2); - } -} - -static void CreateIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::SameAsFirstInput()); -} - -static void CreateLongLongToLongLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::SameAsFirstInput()); - // Register to use to perform a long subtract to set cc. - locations->AddTemp(Location::RequiresRegister()); -} - -void IntrinsicLocationsBuilderX86::VisitMathMinIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathMinIntInt(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetAssembler()); -} - -void IntrinsicLocationsBuilderX86::VisitMathMinLongLong(HInvoke* invoke) { - CreateLongLongToLongLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathMinLongLong(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetAssembler()); -} - -void IntrinsicLocationsBuilderX86::VisitMathMaxIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathMaxIntInt(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetAssembler()); -} - -void IntrinsicLocationsBuilderX86::VisitMathMaxLongLong(HInvoke* invoke) { - CreateLongLongToLongLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathMaxLongLong(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetAssembler()); -} - static void CreateFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { LocationSummary* locations = new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); @@ -3282,16 +2851,30 @@ void IntrinsicCodeGeneratorX86::VisitSystemArrayCopy(HInvoke* invoke) { } void IntrinsicLocationsBuilderX86::VisitIntegerValueOf(HInvoke* invoke) { + DCHECK(invoke->IsInvokeStaticOrDirect()); InvokeRuntimeCallingConvention calling_convention; IntrinsicVisitor::ComputeIntegerValueOfLocations( invoke, codegen_, Location::RegisterLocation(EAX), Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + + LocationSummary* locations = invoke->GetLocations(); + if (locations != nullptr) { + HInvokeStaticOrDirect* invoke_static_or_direct = invoke->AsInvokeStaticOrDirect(); + if (invoke_static_or_direct->HasSpecialInput() && + invoke->InputAt(invoke_static_or_direct->GetSpecialInputIndex()) + ->IsX86ComputeBaseMethodAddress()) { + locations->SetInAt(invoke_static_or_direct->GetSpecialInputIndex(), + Location::RequiresRegister()); + } + } } void IntrinsicCodeGeneratorX86::VisitIntegerValueOf(HInvoke* invoke) { - IntrinsicVisitor::IntegerValueOfInfo info = IntrinsicVisitor::ComputeIntegerValueOfInfo(); + DCHECK(invoke->IsInvokeStaticOrDirect()); + IntrinsicVisitor::IntegerValueOfInfo info = + IntrinsicVisitor::ComputeIntegerValueOfInfo(invoke, codegen_->GetCompilerOptions()); LocationSummary* locations = invoke->GetLocations(); X86Assembler* assembler = GetAssembler(); @@ -3299,42 +2882,58 @@ void IntrinsicCodeGeneratorX86::VisitIntegerValueOf(HInvoke* invoke) { InvokeRuntimeCallingConvention calling_convention; if (invoke->InputAt(0)->IsConstant()) { int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue(); - if (value >= info.low && value <= info.high) { + if (static_cast(value - info.low) < info.length) { // Just embed the j.l.Integer in the code. - ScopedObjectAccess soa(Thread::Current()); - mirror::Object* boxed = info.cache->Get(value + (-info.low)); - DCHECK(boxed != nullptr && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boxed)); - uint32_t address = dchecked_integral_cast(reinterpret_cast(boxed)); - __ movl(out, Immediate(address)); + DCHECK_NE(info.value_boot_image_reference, IntegerValueOfInfo::kInvalidReference); + codegen_->LoadBootImageAddress( + out, info.value_boot_image_reference, invoke->AsInvokeStaticOrDirect()); } else { + DCHECK(locations->CanCall()); // Allocate and initialize a new j.l.Integer. // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the // JIT object table. - uint32_t address = dchecked_integral_cast(reinterpret_cast(info.integer)); - __ movl(calling_convention.GetRegisterAt(0), Immediate(address)); - codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc()); - CheckEntrypointTypes(); + codegen_->AllocateInstanceForIntrinsic(invoke->AsInvokeStaticOrDirect(), + info.integer_boot_image_offset); __ movl(Address(out, info.value_offset), Immediate(value)); } } else { + DCHECK(locations->CanCall()); Register in = locations->InAt(0).AsRegister(); // Check bounds of our cache. __ leal(out, Address(in, -info.low)); - __ cmpl(out, Immediate(info.high - info.low + 1)); + __ cmpl(out, Immediate(info.length)); NearLabel allocate, done; __ j(kAboveEqual, &allocate); // If the value is within the bounds, load the j.l.Integer directly from the array. - uint32_t data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value(); - uint32_t address = dchecked_integral_cast(reinterpret_cast(info.cache)); - __ movl(out, Address(out, TIMES_4, data_offset + address)); + constexpr size_t kElementSize = sizeof(mirror::HeapReference); + static_assert((1u << TIMES_4) == sizeof(mirror::HeapReference), + "Check heap reference size."); + if (codegen_->GetCompilerOptions().IsBootImage()) { + DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u); + size_t method_address_index = invoke->AsInvokeStaticOrDirect()->GetSpecialInputIndex(); + HX86ComputeBaseMethodAddress* method_address = + invoke->InputAt(method_address_index)->AsX86ComputeBaseMethodAddress(); + DCHECK(method_address != nullptr); + Register method_address_reg = + invoke->GetLocations()->InAt(method_address_index).AsRegister(); + __ movl(out, Address(method_address_reg, out, TIMES_4, CodeGeneratorX86::kDummy32BitOffset)); + codegen_->RecordBootImageIntrinsicPatch(method_address, info.array_data_boot_image_reference); + } else { + // Note: We're about to clobber the index in `out`, so we need to use `in` and + // adjust the offset accordingly. + uint32_t mid_array_boot_image_offset = + info.array_data_boot_image_reference - info.low * kElementSize; + codegen_->LoadBootImageAddress( + out, mid_array_boot_image_offset, invoke->AsInvokeStaticOrDirect()); + DCHECK_NE(out, in); + __ movl(out, Address(out, in, TIMES_4, 0)); + } __ MaybeUnpoisonHeapReference(out); __ jmp(&done); __ Bind(&allocate); // Otherwise allocate and initialize a new j.l.Integer. - address = dchecked_integral_cast(reinterpret_cast(info.integer)); - __ movl(calling_convention.GetRegisterAt(0), Immediate(address)); - codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc()); - CheckEntrypointTypes(); + codegen_->AllocateInstanceForIntrinsic(invoke->AsInvokeStaticOrDirect(), + info.integer_boot_image_offset); __ movl(Address(out, info.value_offset), in); __ Bind(&done); } diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 91a505ede1a663e6f1938e7d04ee48f83eb44b5a..ac6eab0834bfb830b86bfa2ad74c0e65ef143da4 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -236,304 +236,6 @@ void IntrinsicCodeGeneratorX86_64::VisitShortReverseBytes(HInvoke* invoke) { GenReverseBytes(invoke->GetLocations(), DataType::Type::kInt16, GetAssembler()); } - -// TODO: Consider Quick's way of doing Double abs through integer operations, as the immediate we -// need is 64b. - -static void CreateFloatToFloatPlusTemps(ArenaAllocator* allocator, HInvoke* invoke) { - // TODO: Enable memory operations when the assembler supports them. - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetOut(Location::SameAsFirstInput()); - locations->AddTemp(Location::RequiresFpuRegister()); // FP reg to hold mask. -} - -static void MathAbsFP(LocationSummary* locations, - bool is64bit, - X86_64Assembler* assembler, - CodeGeneratorX86_64* codegen) { - Location output = locations->Out(); - - DCHECK(output.IsFpuRegister()); - XmmRegister xmm_temp = locations->GetTemp(0).AsFpuRegister(); - - // TODO: Can mask directly with constant area using pand if we can guarantee - // that the literal is aligned on a 16 byte boundary. This will avoid a - // temporary. - if (is64bit) { - __ movsd(xmm_temp, codegen->LiteralInt64Address(INT64_C(0x7FFFFFFFFFFFFFFF))); - __ andpd(output.AsFpuRegister(), xmm_temp); - } else { - __ movss(xmm_temp, codegen->LiteralInt32Address(INT32_C(0x7FFFFFFF))); - __ andps(output.AsFpuRegister(), xmm_temp); - } -} - -void IntrinsicLocationsBuilderX86_64::VisitMathAbsDouble(HInvoke* invoke) { - CreateFloatToFloatPlusTemps(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathAbsDouble(HInvoke* invoke) { - MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler(), codegen_); -} - -void IntrinsicLocationsBuilderX86_64::VisitMathAbsFloat(HInvoke* invoke) { - CreateFloatToFloatPlusTemps(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathAbsFloat(HInvoke* invoke) { - MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler(), codegen_); -} - -static void CreateIntToIntPlusTemp(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::SameAsFirstInput()); - locations->AddTemp(Location::RequiresRegister()); -} - -static void GenAbsInteger(LocationSummary* locations, bool is64bit, X86_64Assembler* assembler) { - Location output = locations->Out(); - CpuRegister out = output.AsRegister(); - CpuRegister mask = locations->GetTemp(0).AsRegister(); - - if (is64bit) { - // Create mask. - __ movq(mask, out); - __ sarq(mask, Immediate(63)); - // Add mask. - __ addq(out, mask); - __ xorq(out, mask); - } else { - // Create mask. - __ movl(mask, out); - __ sarl(mask, Immediate(31)); - // Add mask. - __ addl(out, mask); - __ xorl(out, mask); - } -} - -void IntrinsicLocationsBuilderX86_64::VisitMathAbsInt(HInvoke* invoke) { - CreateIntToIntPlusTemp(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathAbsInt(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetAssembler()); -} - -void IntrinsicLocationsBuilderX86_64::VisitMathAbsLong(HInvoke* invoke) { - CreateIntToIntPlusTemp(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathAbsLong(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); -} - -static void GenMinMaxFP(LocationSummary* locations, - bool is_min, - bool is_double, - X86_64Assembler* assembler, - CodeGeneratorX86_64* codegen) { - Location op1_loc = locations->InAt(0); - Location op2_loc = locations->InAt(1); - Location out_loc = locations->Out(); - XmmRegister out = out_loc.AsFpuRegister(); - - // Shortcut for same input locations. - if (op1_loc.Equals(op2_loc)) { - DCHECK(out_loc.Equals(op1_loc)); - return; - } - - // (out := op1) - // out <=? op2 - // if Nan jmp Nan_label - // if out is min jmp done - // if op2 is min jmp op2_label - // handle -0/+0 - // jmp done - // Nan_label: - // out := NaN - // op2_label: - // out := op2 - // done: - // - // This removes one jmp, but needs to copy one input (op1) to out. - // - // TODO: This is straight from Quick. Make NaN an out-of-line slowpath? - - XmmRegister op2 = op2_loc.AsFpuRegister(); - - NearLabel nan, done, op2_label; - if (is_double) { - __ ucomisd(out, op2); - } else { - __ ucomiss(out, op2); - } - - __ j(Condition::kParityEven, &nan); - - __ j(is_min ? Condition::kAbove : Condition::kBelow, &op2_label); - __ j(is_min ? Condition::kBelow : Condition::kAbove, &done); - - // Handle 0.0/-0.0. - if (is_min) { - if (is_double) { - __ orpd(out, op2); - } else { - __ orps(out, op2); - } - } else { - if (is_double) { - __ andpd(out, op2); - } else { - __ andps(out, op2); - } - } - __ jmp(&done); - - // NaN handling. - __ Bind(&nan); - if (is_double) { - __ movsd(out, codegen->LiteralInt64Address(INT64_C(0x7FF8000000000000))); - } else { - __ movss(out, codegen->LiteralInt32Address(INT32_C(0x7FC00000))); - } - __ jmp(&done); - - // out := op2; - __ Bind(&op2_label); - if (is_double) { - __ movsd(out, op2); - } else { - __ movss(out, op2); - } - - // Done. - __ Bind(&done); -} - -static void CreateFPFPToFP(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetInAt(1, Location::RequiresFpuRegister()); - // The following is sub-optimal, but all we can do for now. It would be fine to also accept - // the second input to be the output (we can simply swap inputs). - locations->SetOut(Location::SameAsFirstInput()); -} - -void IntrinsicLocationsBuilderX86_64::VisitMathMinDoubleDouble(HInvoke* invoke) { - CreateFPFPToFP(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathMinDoubleDouble(HInvoke* invoke) { - GenMinMaxFP( - invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetAssembler(), codegen_); -} - -void IntrinsicLocationsBuilderX86_64::VisitMathMinFloatFloat(HInvoke* invoke) { - CreateFPFPToFP(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathMinFloatFloat(HInvoke* invoke) { - GenMinMaxFP( - invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetAssembler(), codegen_); -} - -void IntrinsicLocationsBuilderX86_64::VisitMathMaxDoubleDouble(HInvoke* invoke) { - CreateFPFPToFP(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathMaxDoubleDouble(HInvoke* invoke) { - GenMinMaxFP( - invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetAssembler(), codegen_); -} - -void IntrinsicLocationsBuilderX86_64::VisitMathMaxFloatFloat(HInvoke* invoke) { - CreateFPFPToFP(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathMaxFloatFloat(HInvoke* invoke) { - GenMinMaxFP( - invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetAssembler(), codegen_); -} - -static void GenMinMax(LocationSummary* locations, bool is_min, bool is_long, - X86_64Assembler* assembler) { - Location op1_loc = locations->InAt(0); - Location op2_loc = locations->InAt(1); - - // Shortcut for same input locations. - if (op1_loc.Equals(op2_loc)) { - // Can return immediately, as op1_loc == out_loc. - // Note: if we ever support separate registers, e.g., output into memory, we need to check for - // a copy here. - DCHECK(locations->Out().Equals(op1_loc)); - return; - } - - CpuRegister out = locations->Out().AsRegister(); - CpuRegister op2 = op2_loc.AsRegister(); - - // (out := op1) - // out <=? op2 - // if out is min jmp done - // out := op2 - // done: - - if (is_long) { - __ cmpq(out, op2); - } else { - __ cmpl(out, op2); - } - - __ cmov(is_min ? Condition::kGreater : Condition::kLess, out, op2, is_long); -} - -static void CreateIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::SameAsFirstInput()); -} - -void IntrinsicLocationsBuilderX86_64::VisitMathMinIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathMinIntInt(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetAssembler()); -} - -void IntrinsicLocationsBuilderX86_64::VisitMathMinLongLong(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathMinLongLong(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetAssembler()); -} - -void IntrinsicLocationsBuilderX86_64::VisitMathMaxIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathMaxIntInt(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetAssembler()); -} - -void IntrinsicLocationsBuilderX86_64::VisitMathMaxLongLong(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathMaxLongLong(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetAssembler()); -} - static void CreateFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { LocationSummary* locations = new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); @@ -2958,58 +2660,49 @@ void IntrinsicLocationsBuilderX86_64::VisitIntegerValueOf(HInvoke* invoke) { } void IntrinsicCodeGeneratorX86_64::VisitIntegerValueOf(HInvoke* invoke) { - IntrinsicVisitor::IntegerValueOfInfo info = IntrinsicVisitor::ComputeIntegerValueOfInfo(); + IntrinsicVisitor::IntegerValueOfInfo info = + IntrinsicVisitor::ComputeIntegerValueOfInfo(invoke, codegen_->GetCompilerOptions()); LocationSummary* locations = invoke->GetLocations(); X86_64Assembler* assembler = GetAssembler(); CpuRegister out = locations->Out().AsRegister(); InvokeRuntimeCallingConvention calling_convention; - if (invoke->InputAt(0)->IsConstant()) { + CpuRegister argument = CpuRegister(calling_convention.GetRegisterAt(0)); + if (invoke->InputAt(0)->IsIntConstant()) { int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue(); - if (value >= info.low && value <= info.high) { + if (static_cast(value - info.low) < info.length) { // Just embed the j.l.Integer in the code. - ScopedObjectAccess soa(Thread::Current()); - mirror::Object* boxed = info.cache->Get(value + (-info.low)); - DCHECK(boxed != nullptr && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boxed)); - uint32_t address = dchecked_integral_cast(reinterpret_cast(boxed)); - __ movl(out, Immediate(static_cast(address))); + DCHECK_NE(info.value_boot_image_reference, IntegerValueOfInfo::kInvalidReference); + codegen_->LoadBootImageAddress(out, info.value_boot_image_reference); } else { + DCHECK(locations->CanCall()); // Allocate and initialize a new j.l.Integer. // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the // JIT object table. - CpuRegister argument = CpuRegister(calling_convention.GetRegisterAt(0)); - uint32_t address = dchecked_integral_cast(reinterpret_cast(info.integer)); - __ movl(argument, Immediate(static_cast(address))); - codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc()); - CheckEntrypointTypes(); + codegen_->AllocateInstanceForIntrinsic(invoke->AsInvokeStaticOrDirect(), + info.integer_boot_image_offset); __ movl(Address(out, info.value_offset), Immediate(value)); } } else { + DCHECK(locations->CanCall()); CpuRegister in = locations->InAt(0).AsRegister(); // Check bounds of our cache. __ leal(out, Address(in, -info.low)); - __ cmpl(out, Immediate(info.high - info.low + 1)); + __ cmpl(out, Immediate(info.length)); NearLabel allocate, done; __ j(kAboveEqual, &allocate); // If the value is within the bounds, load the j.l.Integer directly from the array. - uint32_t data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value(); - uint32_t address = dchecked_integral_cast(reinterpret_cast(info.cache)); - if (data_offset + address <= std::numeric_limits::max()) { - __ movl(out, Address(out, TIMES_4, data_offset + address)); - } else { - CpuRegister temp = CpuRegister(calling_convention.GetRegisterAt(0)); - __ movl(temp, Immediate(static_cast(data_offset + address))); - __ movl(out, Address(temp, out, TIMES_4, 0)); - } + DCHECK_NE(out.AsRegister(), argument.AsRegister()); + codegen_->LoadBootImageAddress(argument, info.array_data_boot_image_reference); + static_assert((1u << TIMES_4) == sizeof(mirror::HeapReference), + "Check heap reference size."); + __ movl(out, Address(argument, out, TIMES_4, 0)); __ MaybeUnpoisonHeapReference(out); __ jmp(&done); __ Bind(&allocate); // Otherwise allocate and initialize a new j.l.Integer. - CpuRegister argument = CpuRegister(calling_convention.GetRegisterAt(0)); - address = dchecked_integral_cast(reinterpret_cast(info.integer)); - __ movl(argument, Immediate(static_cast(address))); - codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc()); - CheckEntrypointTypes(); + codegen_->AllocateInstanceForIntrinsic(invoke->AsInvokeStaticOrDirect(), + info.integer_boot_image_offset); __ movl(Address(out, info.value_offset), in); __ Bind(&done); } diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc index d3a0376e9c4ace06f6789ee646fe27a71b97cdf5..0edb23b85796fa0e8d21f7a78f3573e7c180aca5 100644 --- a/compiler/optimizing/licm.cc +++ b/compiler/optimizing/licm.cc @@ -78,7 +78,8 @@ static void UpdateLoopPhisIn(HEnvironment* environment, HLoopInformation* info) } } -void LICM::Run() { +bool LICM::Run() { + bool didLICM = false; DCHECK(side_effects_.HasRun()); // Only used during debug. @@ -157,6 +158,7 @@ void LICM::Run() { } instruction->MoveBefore(pre_header->GetLastInstruction()); MaybeRecordStat(stats_, MethodCompilationStat::kLoopInvariantMoved); + didLICM = true; } if (!can_move && (instruction->CanThrow() || instruction->DoesAnyWrite())) { @@ -167,6 +169,7 @@ void LICM::Run() { } } } + return didLICM; } } // namespace art diff --git a/compiler/optimizing/licm.h b/compiler/optimizing/licm.h index ee567aeb20e2a45a441696a862ae79e89d6a3991..f72d195ab2cc3e948ca0555c2a172607b21ab13c 100644 --- a/compiler/optimizing/licm.h +++ b/compiler/optimizing/licm.h @@ -33,7 +33,7 @@ class LICM : public HOptimization { : HOptimization(graph, name, stats), side_effects_(side_effects) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kLoopInvariantCodeMotionPassName = "licm"; diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc index 9fa5b74c6255a7cb015b8c16e34a8dda3cafb940..50bfe843b506e9481f4df7040e38b76c418c646a 100644 --- a/compiler/optimizing/linearize_test.cc +++ b/compiler/optimizing/linearize_test.cc @@ -16,11 +16,9 @@ #include -#include "arch/x86/instruction_set_features_x86.h" #include "base/arena_allocator.h" #include "builder.h" #include "code_generator.h" -#include "code_generator_x86.h" #include "dex/dex_file.h" #include "dex/dex_instruction.h" #include "driver/compiler_options.h" @@ -43,10 +41,8 @@ template void LinearizeTest::TestCode(const std::vector& data, const uint32_t (&expected_order)[number_of_blocks]) { HGraph* graph = CreateCFG(data); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); - SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); + std::unique_ptr codegen = CodeGenerator::Create(graph, *compiler_options_); + SsaLivenessAnalysis liveness(graph, codegen.get(), GetScopedAllocator()); liveness.Analyze(); ASSERT_EQ(graph->GetLinearOrder().size(), number_of_blocks); diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc index 66660662e46808308afe97cfdf5d025a1cb875e4..0fb90fb370d2c200d67225cc25f0eb6c7316a6ed 100644 --- a/compiler/optimizing/live_ranges_test.cc +++ b/compiler/optimizing/live_ranges_test.cc @@ -14,11 +14,9 @@ * limitations under the License. */ -#include "arch/x86/instruction_set_features_x86.h" #include "base/arena_allocator.h" #include "builder.h" #include "code_generator.h" -#include "code_generator_x86.h" #include "dex/dex_file.h" #include "dex/dex_instruction.h" #include "driver/compiler_options.h" @@ -63,10 +61,8 @@ TEST_F(LiveRangesTest, CFG1) { HGraph* graph = BuildGraph(data); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); - SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); + std::unique_ptr codegen = CodeGenerator::Create(graph, *compiler_options_); + SsaLivenessAnalysis liveness(graph, codegen.get(), GetScopedAllocator()); liveness.Analyze(); LiveInterval* interval = liveness.GetInstructionFromSsaIndex(0)->GetLiveInterval(); @@ -109,10 +105,8 @@ TEST_F(LiveRangesTest, CFG2) { Instruction::RETURN | 0 << 8); HGraph* graph = BuildGraph(data); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); - SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); + std::unique_ptr codegen = CodeGenerator::Create(graph, *compiler_options_); + SsaLivenessAnalysis liveness(graph, codegen.get(), GetScopedAllocator()); liveness.Analyze(); LiveInterval* interval = liveness.GetInstructionFromSsaIndex(0)->GetLiveInterval(); @@ -158,10 +152,8 @@ TEST_F(LiveRangesTest, CFG3) { Instruction::RETURN | 0 << 8); HGraph* graph = BuildGraph(data); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); - SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); + std::unique_ptr codegen = CodeGenerator::Create(graph, *compiler_options_); + SsaLivenessAnalysis liveness(graph, codegen.get(), GetScopedAllocator()); liveness.Analyze(); // Test for the 4 constant. @@ -235,10 +227,8 @@ TEST_F(LiveRangesTest, Loop1) { HGraph* graph = BuildGraph(data); RemoveSuspendChecks(graph); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); - SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); + std::unique_ptr codegen = CodeGenerator::Create(graph, *compiler_options_); + SsaLivenessAnalysis liveness(graph, codegen.get(), GetScopedAllocator()); liveness.Analyze(); // Test for the 0 constant. @@ -312,10 +302,8 @@ TEST_F(LiveRangesTest, Loop2) { Instruction::RETURN | 0 << 8); HGraph* graph = BuildGraph(data); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); - SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); + std::unique_ptr codegen = CodeGenerator::Create(graph, *compiler_options_); + SsaLivenessAnalysis liveness(graph, codegen.get(), GetScopedAllocator()); liveness.Analyze(); // Test for the 0 constant. @@ -388,10 +376,8 @@ TEST_F(LiveRangesTest, CFG4) { Instruction::RETURN); HGraph* graph = BuildGraph(data); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); - SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); + std::unique_ptr codegen = CodeGenerator::Create(graph, *compiler_options_); + SsaLivenessAnalysis liveness(graph, codegen.get(), GetScopedAllocator()); liveness.Analyze(); // Test for the 0 constant. diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc index 6621a03568fadceb8570784d3a81d1a1715cc964..72f995e7730d53c42e3c99b0b54a9e363eccc9e9 100644 --- a/compiler/optimizing/liveness_test.cc +++ b/compiler/optimizing/liveness_test.cc @@ -14,11 +14,9 @@ * limitations under the License. */ -#include "arch/x86/instruction_set_features_x86.h" #include "base/arena_allocator.h" #include "builder.h" #include "code_generator.h" -#include "code_generator_x86.h" #include "dex/dex_file.h" #include "dex/dex_instruction.h" #include "driver/compiler_options.h" @@ -50,10 +48,8 @@ void LivenessTest::TestCode(const std::vector& data, const char* expec HGraph* graph = CreateCFG(data); // `Inline` conditions into ifs. PrepareForRegisterAllocation(graph).Run(); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); - SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); + std::unique_ptr codegen = CodeGenerator::Create(graph, *compiler_options_); + SsaLivenessAnalysis liveness(graph, codegen.get(), GetScopedAllocator()); liveness.Analyze(); std::ostringstream buffer; diff --git a/compiler/optimizing/load_store_analysis.cc b/compiler/optimizing/load_store_analysis.cc index 8b1812a6de627ccf20e548e1d4e75148d5afdef7..7d7bb94933f54dc9b5cc515dcc4d9a450c553206 100644 --- a/compiler/optimizing/load_store_analysis.cc +++ b/compiler/optimizing/load_store_analysis.cc @@ -152,7 +152,7 @@ bool HeapLocationCollector::CanArrayElementsAlias(const HInstruction* idx1, return true; } -void LoadStoreAnalysis::Run() { +bool LoadStoreAnalysis::Run() { for (HBasicBlock* block : graph_->GetReversePostOrder()) { heap_location_collector_.VisitBasicBlock(block); } @@ -160,22 +160,23 @@ void LoadStoreAnalysis::Run() { if (heap_location_collector_.GetNumberOfHeapLocations() > kMaxNumberOfHeapLocations) { // Bail out if there are too many heap locations to deal with. heap_location_collector_.CleanUp(); - return; + return false; } if (!heap_location_collector_.HasHeapStores()) { // Without heap stores, this pass would act mostly as GVN on heap accesses. heap_location_collector_.CleanUp(); - return; + return false; } if (heap_location_collector_.HasVolatile() || heap_location_collector_.HasMonitorOps()) { // Don't do load/store elimination if the method has volatile field accesses or // monitor operations, for now. // TODO: do it right. heap_location_collector_.CleanUp(); - return; + return false; } heap_location_collector_.BuildAliasingMatrix(); + return true; } } // namespace art diff --git a/compiler/optimizing/load_store_analysis.h b/compiler/optimizing/load_store_analysis.h index 437e6be41842c123632f520698dbe6e15210a65f..769a3f1b59a075a6dcf170ea2b901988f32e6d60 100644 --- a/compiler/optimizing/load_store_analysis.h +++ b/compiler/optimizing/load_store_analysis.h @@ -94,11 +94,13 @@ class HeapLocation : public ArenaObject { static constexpr int16_t kDeclaringClassDefIndexForArrays = -1; HeapLocation(ReferenceInfo* ref_info, + DataType::Type type, size_t offset, HInstruction* index, size_t vector_length, int16_t declaring_class_def_index) : ref_info_(ref_info), + type_(DataType::ToSigned(type)), offset_(offset), index_(index), vector_length_(vector_length), @@ -116,6 +118,7 @@ class HeapLocation : public ArenaObject { } ReferenceInfo* GetReferenceInfo() const { return ref_info_; } + DataType::Type GetType() const { return type_; } size_t GetOffset() const { return offset_; } HInstruction* GetIndex() const { return index_; } size_t GetVectorLength() const { return vector_length_; } @@ -149,6 +152,10 @@ class HeapLocation : public ArenaObject { private: // Reference for instance/static field, array element or vector data. ReferenceInfo* const ref_info_; + // Type of data residing at HeapLocation (always signed for integral + // data since e.g. a[i] and a[i] & 0xff are represented by differently + // signed types; char vs short are disambiguated through the reference). + const DataType::Type type_; // Offset of static/instance field. // Invalid when this HeapLocation is not field. const size_t offset_; @@ -237,19 +244,31 @@ class HeapLocationCollector : public HGraphVisitor { DCHECK(object != nullptr); DCHECK(field != nullptr); return FindHeapLocationIndex(FindReferenceInfoOf(HuntForOriginalReference(object)), + field->GetFieldType(), field->GetFieldOffset().SizeValue(), nullptr, HeapLocation::kScalar, field->GetDeclaringClassDefIndex()); } - size_t GetArrayHeapLocation(HInstruction* array, - HInstruction* index, - size_t vector_length = HeapLocation::kScalar) const { - DCHECK(array != nullptr); - DCHECK(index != nullptr); - DCHECK_GE(vector_length, HeapLocation::kScalar); + size_t GetArrayHeapLocation(HInstruction* instruction) const { + DCHECK(instruction != nullptr); + HInstruction* array = instruction->InputAt(0); + HInstruction* index = instruction->InputAt(1); + DataType::Type type = instruction->GetType(); + size_t vector_length = HeapLocation::kScalar; + if (instruction->IsArraySet()) { + type = instruction->AsArraySet()->GetComponentType(); + } else if (instruction->IsVecStore() || + instruction->IsVecLoad()) { + HVecOperation* vec_op = instruction->AsVecOperation(); + type = vec_op->GetPackedType(); + vector_length = vec_op->GetVectorLength(); + } else { + DCHECK(instruction->IsArrayGet()); + } return FindHeapLocationIndex(FindReferenceInfoOf(HuntForOriginalReference(array)), + type, HeapLocation::kInvalidFieldOffset, index, vector_length, @@ -279,13 +298,16 @@ class HeapLocationCollector : public HGraphVisitor { // In later analysis, ComputeMayAlias() and MayAlias() compute and tell whether // these indexes alias. size_t FindHeapLocationIndex(ReferenceInfo* ref_info, + DataType::Type type, size_t offset, HInstruction* index, size_t vector_length, int16_t declaring_class_def_index) const { + DataType::Type lookup_type = DataType::ToSigned(type); for (size_t i = 0; i < heap_locations_.size(); i++) { HeapLocation* loc = heap_locations_[i]; if (loc->GetReferenceInfo() == ref_info && + loc->GetType() == lookup_type && loc->GetOffset() == offset && loc->GetIndex() == index && loc->GetVectorLength() == vector_length && @@ -425,6 +447,7 @@ class HeapLocationCollector : public HGraphVisitor { } HeapLocation* GetOrCreateHeapLocation(HInstruction* ref, + DataType::Type type, size_t offset, HInstruction* index, size_t vector_length, @@ -432,10 +455,10 @@ class HeapLocationCollector : public HGraphVisitor { HInstruction* original_ref = HuntForOriginalReference(ref); ReferenceInfo* ref_info = GetOrCreateReferenceInfo(original_ref); size_t heap_location_idx = FindHeapLocationIndex( - ref_info, offset, index, vector_length, declaring_class_def_index); + ref_info, type, offset, index, vector_length, declaring_class_def_index); if (heap_location_idx == kHeapLocationNotFound) { HeapLocation* heap_loc = new (GetGraph()->GetAllocator()) - HeapLocation(ref_info, offset, index, vector_length, declaring_class_def_index); + HeapLocation(ref_info, type, offset, index, vector_length, declaring_class_def_index); heap_locations_.push_back(heap_loc); return heap_loc; } @@ -446,17 +469,23 @@ class HeapLocationCollector : public HGraphVisitor { if (field_info.IsVolatile()) { has_volatile_ = true; } + DataType::Type type = field_info.GetFieldType(); const uint16_t declaring_class_def_index = field_info.GetDeclaringClassDefIndex(); const size_t offset = field_info.GetFieldOffset().SizeValue(); return GetOrCreateHeapLocation(ref, + type, offset, nullptr, HeapLocation::kScalar, declaring_class_def_index); } - void VisitArrayAccess(HInstruction* array, HInstruction* index, size_t vector_length) { + void VisitArrayAccess(HInstruction* array, + HInstruction* index, + DataType::Type type, + size_t vector_length) { GetOrCreateHeapLocation(array, + type, HeapLocation::kInvalidFieldOffset, index, vector_length, @@ -510,28 +539,32 @@ class HeapLocationCollector : public HGraphVisitor { void VisitArrayGet(HArrayGet* instruction) OVERRIDE { HInstruction* array = instruction->InputAt(0); HInstruction* index = instruction->InputAt(1); - VisitArrayAccess(array, index, HeapLocation::kScalar); + DataType::Type type = instruction->GetType(); + VisitArrayAccess(array, index, type, HeapLocation::kScalar); CreateReferenceInfoForReferenceType(instruction); } void VisitArraySet(HArraySet* instruction) OVERRIDE { HInstruction* array = instruction->InputAt(0); HInstruction* index = instruction->InputAt(1); - VisitArrayAccess(array, index, HeapLocation::kScalar); + DataType::Type type = instruction->GetComponentType(); + VisitArrayAccess(array, index, type, HeapLocation::kScalar); has_heap_stores_ = true; } void VisitVecLoad(HVecLoad* instruction) OVERRIDE { HInstruction* array = instruction->InputAt(0); HInstruction* index = instruction->InputAt(1); - VisitArrayAccess(array, index, instruction->GetVectorLength()); + DataType::Type type = instruction->GetPackedType(); + VisitArrayAccess(array, index, type, instruction->GetVectorLength()); CreateReferenceInfoForReferenceType(instruction); } void VisitVecStore(HVecStore* instruction) OVERRIDE { HInstruction* array = instruction->InputAt(0); HInstruction* index = instruction->InputAt(1); - VisitArrayAccess(array, index, instruction->GetVectorLength()); + DataType::Type type = instruction->GetPackedType(); + VisitArrayAccess(array, index, type, instruction->GetVectorLength()); has_heap_stores_ = true; } @@ -572,7 +605,7 @@ class LoadStoreAnalysis : public HOptimization { return heap_location_collector_; } - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kLoadStoreAnalysisPassName = "load_store_analysis"; diff --git a/compiler/optimizing/load_store_analysis_test.cc b/compiler/optimizing/load_store_analysis_test.cc index 56361a8c9002bb693d00128cbab81cc0120e1fb4..bfe7a4f72fc639f0509e2cde85c6dc4046d96053 100644 --- a/compiler/optimizing/load_store_analysis_test.cc +++ b/compiler/optimizing/load_store_analysis_test.cc @@ -78,12 +78,16 @@ TEST_F(LoadStoreAnalysisTest, ArrayHeapLocations) { // Test queries on HeapLocationCollector's ref info and index records. ReferenceInfo* ref = heap_location_collector.FindReferenceInfoOf(array); + DataType::Type type = DataType::Type::kInt32; size_t field = HeapLocation::kInvalidFieldOffset; size_t vec = HeapLocation::kScalar; size_t class_def = HeapLocation::kDeclaringClassDefIndexForArrays; - size_t loc1 = heap_location_collector.FindHeapLocationIndex(ref, field, c1, vec, class_def); - size_t loc2 = heap_location_collector.FindHeapLocationIndex(ref, field, c2, vec, class_def); - size_t loc3 = heap_location_collector.FindHeapLocationIndex(ref, field, index, vec, class_def); + size_t loc1 = heap_location_collector.FindHeapLocationIndex( + ref, type, field, c1, vec, class_def); + size_t loc2 = heap_location_collector.FindHeapLocationIndex( + ref, type, field, c2, vec, class_def); + size_t loc3 = heap_location_collector.FindHeapLocationIndex( + ref, type, field, index, vec, class_def); // must find this reference info for array in HeapLocationCollector. ASSERT_TRUE(ref != nullptr); // must find these heap locations; @@ -246,28 +250,28 @@ TEST_F(LoadStoreAnalysisTest, ArrayIndexAliasingTest) { size_t loc2 = HeapLocationCollector::kHeapLocationNotFound; // Test alias: array[0] and array[1] - loc1 = heap_location_collector.GetArrayHeapLocation(array, c0); - loc2 = heap_location_collector.GetArrayHeapLocation(array, c1); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set1); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set2); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+0] and array[i-0] - loc1 = heap_location_collector.GetArrayHeapLocation(array, add0); - loc2 = heap_location_collector.GetArrayHeapLocation(array, sub0); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set3); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set5); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+1] and array[i-1] - loc1 = heap_location_collector.GetArrayHeapLocation(array, add1); - loc2 = heap_location_collector.GetArrayHeapLocation(array, sub1); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set4); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set6); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+1] and array[1-i] - loc1 = heap_location_collector.GetArrayHeapLocation(array, add1); - loc2 = heap_location_collector.GetArrayHeapLocation(array, rev_sub1); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set4); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set7); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+1] and array[i-(-1)] - loc1 = heap_location_collector.GetArrayHeapLocation(array, add1); - loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_neg1); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set4); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set8); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); } @@ -409,70 +413,75 @@ TEST_F(LoadStoreAnalysisTest, ArrayAliasingTest) { size_t loc1, loc2; // Test alias: array[0] and array[0,1,2,3] - loc1 = heap_location_collector.GetArrayHeapLocation(array, c0); - loc2 = heap_location_collector.GetArrayHeapLocation(array, c0, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_0); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_0); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); + // Test alias: array[0] and array[1,2,3,4] + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_0); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_1); + ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); + // Test alias: array[0] and array[8,9,10,11] - loc1 = heap_location_collector.GetArrayHeapLocation(array, c0); - loc2 = heap_location_collector.GetArrayHeapLocation(array, c8, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_0); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_8); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[1] and array[8,9,10,11] - loc1 = heap_location_collector.GetArrayHeapLocation(array, c1); - loc2 = heap_location_collector.GetArrayHeapLocation(array, c8, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_1); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_8); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[1] and array[0,1,2,3] - loc1 = heap_location_collector.GetArrayHeapLocation(array, c1); - loc2 = heap_location_collector.GetArrayHeapLocation(array, c0, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_1); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_0); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[0,1,2,3] and array[8,9,10,11] - loc1 = heap_location_collector.GetArrayHeapLocation(array, c0, 4); - loc2 = heap_location_collector.GetArrayHeapLocation(array, c8, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(vstore_0); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_8); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[0,1,2,3] and array[1,2,3,4] - loc1 = heap_location_collector.GetArrayHeapLocation(array, c1, 4); - loc2 = heap_location_collector.GetArrayHeapLocation(array, c0, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(vstore_0); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_1); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[0] and array[i,i+1,i+2,i+3] - loc1 = heap_location_collector.GetArrayHeapLocation(array, c0); - loc2 = heap_location_collector.GetArrayHeapLocation(array, index, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_0); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_i); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i] and array[0,1,2,3] - loc1 = heap_location_collector.GetArrayHeapLocation(array, index); - loc2 = heap_location_collector.GetArrayHeapLocation(array, c0, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_0); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i] and array[i,i+1,i+2,i+3] - loc1 = heap_location_collector.GetArrayHeapLocation(array, index); - loc2 = heap_location_collector.GetArrayHeapLocation(array, index, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_i); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i] and array[i+8,i+9,i+10,i+11] - loc1 = heap_location_collector.GetArrayHeapLocation(array, index); - loc2 = heap_location_collector.GetArrayHeapLocation(array, i_add8, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_i_add8); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+6,i+7,i+8,i+9] and array[i+8,i+9,i+10,i+11] // Test partial overlap. - loc1 = heap_location_collector.GetArrayHeapLocation(array, i_add6, 4); - loc2 = heap_location_collector.GetArrayHeapLocation(array, i_add8, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(vstore_i_add6); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_i_add8); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+6,i+7] and array[i,i+1,i+2,i+3] // Test different vector lengths. - loc1 = heap_location_collector.GetArrayHeapLocation(array, i_add6, 2); - loc2 = heap_location_collector.GetArrayHeapLocation(array, index, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(vstore_i_add6_vlen2); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_i); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+6,i+7] and array[i+8,i+9,i+10,i+11] - loc1 = heap_location_collector.GetArrayHeapLocation(array, i_add6, 2); - loc2 = heap_location_collector.GetArrayHeapLocation(array, i_add8, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(vstore_i_add6_vlen2); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_i_add8); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); } @@ -563,33 +572,33 @@ TEST_F(LoadStoreAnalysisTest, ArrayIndexCalculationOverflowTest) { size_t loc2 = HeapLocationCollector::kHeapLocationNotFound; // Test alias: array[i+0x80000000] and array[i-0x80000000] - loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0x80000000); - loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000000); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_1); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_2); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+0x10] and array[i-0xFFFFFFF0] - loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0x10); - loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0xFFFFFFF0); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_3); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_4); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+0x7FFFFFFF] and array[i-0x80000001] - loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0x7FFFFFFF); - loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000001); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_5); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_6); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+0] and array[i-0] - loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0); - loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_7); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_8); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Should not alias: - loc1 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000000); - loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000001); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_2); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_6); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Should not alias: - loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0); - loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000000); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_7); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_2); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); } @@ -647,10 +656,10 @@ TEST_F(LoadStoreAnalysisTest, TestHuntOriginalRef) { // times the original reference has been transformed by BoundType, // NullCheck, IntermediateAddress, etc. ASSERT_EQ(heap_location_collector.GetNumberOfHeapLocations(), 1U); - size_t loc1 = heap_location_collector.GetArrayHeapLocation(array, c1); - size_t loc2 = heap_location_collector.GetArrayHeapLocation(bound_type, c1); - size_t loc3 = heap_location_collector.GetArrayHeapLocation(null_check, c1); - size_t loc4 = heap_location_collector.GetArrayHeapLocation(inter_addr, c1); + size_t loc1 = heap_location_collector.GetArrayHeapLocation(array_get1); + size_t loc2 = heap_location_collector.GetArrayHeapLocation(array_get2); + size_t loc3 = heap_location_collector.GetArrayHeapLocation(array_get3); + size_t loc4 = heap_location_collector.GetArrayHeapLocation(array_get4); ASSERT_TRUE(loc1 != HeapLocationCollector::kHeapLocationNotFound); ASSERT_EQ(loc1, loc2); ASSERT_EQ(loc1, loc3); diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 237ecd3c10e09ecddb25cf88a332335d1bb07993..28ac94273c4fca68d39353a584093b1f028865ec 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -160,7 +160,7 @@ class LSEVisitor : public HGraphDelegateVisitor { // Scan the list of removed loads to see if we can reuse `type_conversion`, if // the other removed load has the same substitute and type and is dominated - // by `type_conversioni`. + // by `type_conversion`. void TryToReuseTypeConversion(HInstruction* type_conversion, size_t index) { size_t size = removed_loads_.size(); HInstruction* load = removed_loads_[index]; @@ -458,8 +458,13 @@ class LSEVisitor : public HGraphDelegateVisitor { } if (from_all_predecessors) { if (ref_info->IsSingletonAndRemovable() && - block->IsSingleReturnOrReturnVoidAllowingPhis()) { - // Values in the singleton are not needed anymore. + (block->IsSingleReturnOrReturnVoidAllowingPhis() || + (block->EndsWithReturn() && (merged_value != kUnknownHeapValue || + merged_store_value != kUnknownHeapValue)))) { + // Values in the singleton are not needed anymore: + // (1) if this block consists of a sole return, or + // (2) if this block returns and a usable merged value is obtained + // (loads prior to the return will always use that value). } else if (!IsStore(merged_value)) { // We don't track merged value as a store anymore. We have to // hold the stores in predecessors live here. @@ -542,16 +547,7 @@ class LSEVisitor : public HGraphDelegateVisitor { } } - void VisitGetLocation(HInstruction* instruction, - HInstruction* ref, - size_t offset, - HInstruction* index, - size_t vector_length, - int16_t declaring_class_def_index) { - HInstruction* original_ref = heap_location_collector_.HuntForOriginalReference(ref); - ReferenceInfo* ref_info = heap_location_collector_.FindReferenceInfoOf(original_ref); - size_t idx = heap_location_collector_.FindHeapLocationIndex( - ref_info, offset, index, vector_length, declaring_class_def_index); + void VisitGetLocation(HInstruction* instruction, size_t idx) { DCHECK_NE(idx, HeapLocationCollector::kHeapLocationNotFound); ScopedArenaVector& heap_values = heap_values_for_[instruction->GetBlock()->GetBlockId()]; @@ -569,23 +565,7 @@ class LSEVisitor : public HGraphDelegateVisitor { heap_values[idx] = instruction; KeepStoresIfAliasedToLocation(heap_values, idx); } else { - if (DataType::Kind(heap_value->GetType()) != DataType::Kind(instruction->GetType())) { - // The only situation where the same heap location has different type is when - // we do an array get on an instruction that originates from the null constant - // (the null could be behind a field access, an array access, a null check or - // a bound type). - // In order to stay properly typed on primitive types, we do not eliminate - // the array gets. - if (kIsDebugBuild) { - DCHECK(heap_value->IsArrayGet()) << heap_value->DebugName(); - DCHECK(instruction->IsArrayGet()) << instruction->DebugName(); - } - // Load isn't eliminated. Put the load as the value into the HeapLocation. - // This acts like GVN but with better aliasing analysis. - heap_values[idx] = instruction; - KeepStoresIfAliasedToLocation(heap_values, idx); - return; - } + // Load is eliminated. AddRemovedLoad(instruction, heap_value); TryRemovingNullCheck(instruction); } @@ -610,21 +590,11 @@ class LSEVisitor : public HGraphDelegateVisitor { return false; } - void VisitSetLocation(HInstruction* instruction, - HInstruction* ref, - size_t offset, - HInstruction* index, - size_t vector_length, - int16_t declaring_class_def_index, - HInstruction* value) { + void VisitSetLocation(HInstruction* instruction, size_t idx, HInstruction* value) { + DCHECK_NE(idx, HeapLocationCollector::kHeapLocationNotFound); DCHECK(!IsStore(value)) << value->DebugName(); // value may already have a substitute. value = FindSubstitute(value); - HInstruction* original_ref = heap_location_collector_.HuntForOriginalReference(ref); - ReferenceInfo* ref_info = heap_location_collector_.FindReferenceInfoOf(original_ref); - size_t idx = heap_location_collector_.FindHeapLocationIndex( - ref_info, offset, index, vector_length, declaring_class_def_index); - DCHECK_NE(idx, HeapLocationCollector::kHeapLocationNotFound); ScopedArenaVector& heap_values = heap_values_for_[instruction->GetBlock()->GetBlockId()]; HInstruction* heap_value = heap_values[idx]; @@ -644,7 +614,8 @@ class LSEVisitor : public HGraphDelegateVisitor { } else if (!loop_info->IsIrreducible()) { // instruction is a store in the loop so the loop must do write. DCHECK(side_effects_.GetLoopEffects(loop_info->GetHeader()).DoesAnyWrite()); - if (ref_info->IsSingleton() && !loop_info->IsDefinedOutOfTheLoop(original_ref)) { + ReferenceInfo* ref_info = heap_location_collector_.GetHeapLocation(idx)->GetReferenceInfo(); + if (ref_info->IsSingleton() && !loop_info->IsDefinedOutOfTheLoop(ref_info->GetReference())) { // original_ref is created inside the loop. Value stored to it isn't needed at // the loop header. This is true for outer loops also. possibly_redundant = true; @@ -686,79 +657,39 @@ class LSEVisitor : public HGraphDelegateVisitor { } void VisitInstanceFieldGet(HInstanceFieldGet* instruction) OVERRIDE { - HInstruction* obj = instruction->InputAt(0); - size_t offset = instruction->GetFieldInfo().GetFieldOffset().SizeValue(); - int16_t declaring_class_def_index = instruction->GetFieldInfo().GetDeclaringClassDefIndex(); - VisitGetLocation(instruction, - obj, - offset, - nullptr, - HeapLocation::kScalar, - declaring_class_def_index); + HInstruction* object = instruction->InputAt(0); + const FieldInfo& field = instruction->GetFieldInfo(); + VisitGetLocation(instruction, heap_location_collector_.GetFieldHeapLocation(object, &field)); } void VisitInstanceFieldSet(HInstanceFieldSet* instruction) OVERRIDE { - HInstruction* obj = instruction->InputAt(0); - size_t offset = instruction->GetFieldInfo().GetFieldOffset().SizeValue(); - int16_t declaring_class_def_index = instruction->GetFieldInfo().GetDeclaringClassDefIndex(); + HInstruction* object = instruction->InputAt(0); + const FieldInfo& field = instruction->GetFieldInfo(); HInstruction* value = instruction->InputAt(1); - VisitSetLocation(instruction, - obj, - offset, - nullptr, - HeapLocation::kScalar, - declaring_class_def_index, - value); + size_t idx = heap_location_collector_.GetFieldHeapLocation(object, &field); + VisitSetLocation(instruction, idx, value); } void VisitStaticFieldGet(HStaticFieldGet* instruction) OVERRIDE { HInstruction* cls = instruction->InputAt(0); - size_t offset = instruction->GetFieldInfo().GetFieldOffset().SizeValue(); - int16_t declaring_class_def_index = instruction->GetFieldInfo().GetDeclaringClassDefIndex(); - VisitGetLocation(instruction, - cls, - offset, - nullptr, - HeapLocation::kScalar, - declaring_class_def_index); + const FieldInfo& field = instruction->GetFieldInfo(); + VisitGetLocation(instruction, heap_location_collector_.GetFieldHeapLocation(cls, &field)); } void VisitStaticFieldSet(HStaticFieldSet* instruction) OVERRIDE { HInstruction* cls = instruction->InputAt(0); - size_t offset = instruction->GetFieldInfo().GetFieldOffset().SizeValue(); - int16_t declaring_class_def_index = instruction->GetFieldInfo().GetDeclaringClassDefIndex(); - HInstruction* value = instruction->InputAt(1); - VisitSetLocation(instruction, - cls, - offset, - nullptr, - HeapLocation::kScalar, - declaring_class_def_index, - value); + const FieldInfo& field = instruction->GetFieldInfo(); + size_t idx = heap_location_collector_.GetFieldHeapLocation(cls, &field); + VisitSetLocation(instruction, idx, instruction->InputAt(1)); } void VisitArrayGet(HArrayGet* instruction) OVERRIDE { - HInstruction* array = instruction->InputAt(0); - HInstruction* index = instruction->InputAt(1); - VisitGetLocation(instruction, - array, - HeapLocation::kInvalidFieldOffset, - index, - HeapLocation::kScalar, - HeapLocation::kDeclaringClassDefIndexForArrays); + VisitGetLocation(instruction, heap_location_collector_.GetArrayHeapLocation(instruction)); } void VisitArraySet(HArraySet* instruction) OVERRIDE { - HInstruction* array = instruction->InputAt(0); - HInstruction* index = instruction->InputAt(1); - HInstruction* value = instruction->InputAt(2); - VisitSetLocation(instruction, - array, - HeapLocation::kInvalidFieldOffset, - index, - HeapLocation::kScalar, - HeapLocation::kDeclaringClassDefIndexForArrays, - value); + size_t idx = heap_location_collector_.GetArrayHeapLocation(instruction); + VisitSetLocation(instruction, idx, instruction->InputAt(2)); } void VisitDeoptimize(HDeoptimize* instruction) { @@ -948,22 +879,22 @@ class LSEVisitor : public HGraphDelegateVisitor { DISALLOW_COPY_AND_ASSIGN(LSEVisitor); }; -void LoadStoreElimination::Run() { +bool LoadStoreElimination::Run() { if (graph_->IsDebuggable() || graph_->HasTryCatch()) { // Debugger may set heap values or trigger deoptimization of callers. // Try/catch support not implemented yet. // Skip this optimization. - return; + return false; } const HeapLocationCollector& heap_location_collector = lsa_.GetHeapLocationCollector(); if (heap_location_collector.GetNumberOfHeapLocations() == 0) { // No HeapLocation information from LSA, skip this optimization. - return; + return false; } // TODO: analyze VecLoad/VecStore better. if (graph_->HasSIMD()) { - return; + return false; } LSEVisitor lse_visitor(graph_, heap_location_collector, side_effects_, stats_); @@ -971,6 +902,8 @@ void LoadStoreElimination::Run() { lse_visitor.VisitBasicBlock(block); } lse_visitor.RemoveInstructions(); + + return true; } } // namespace art diff --git a/compiler/optimizing/load_store_elimination.h b/compiler/optimizing/load_store_elimination.h index 7153541bafc2a904de55f98de00e80502b1079e4..408386bd827f617d5ab6bb5cd9a92a161a42c5e8 100644 --- a/compiler/optimizing/load_store_elimination.h +++ b/compiler/optimizing/load_store_elimination.h @@ -35,7 +35,7 @@ class LoadStoreElimination : public HOptimization { side_effects_(side_effects), lsa_(lsa) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kLoadStoreEliminationPassName = "load_store_elimination"; diff --git a/compiler/optimizing/loop_analysis.cc b/compiler/optimizing/loop_analysis.cc new file mode 100644 index 0000000000000000000000000000000000000000..d355cedb35ec54145c77bbb8f7d8fa6b6fd86b60 --- /dev/null +++ b/compiler/optimizing/loop_analysis.cc @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loop_analysis.h" + +#include "base/bit_vector-inl.h" +#include "induction_var_range.h" + +namespace art { + +void LoopAnalysis::CalculateLoopBasicProperties(HLoopInformation* loop_info, + LoopAnalysisInfo* analysis_results, + int64_t trip_count) { + analysis_results->trip_count_ = trip_count; + + for (HBlocksInLoopIterator block_it(*loop_info); + !block_it.Done(); + block_it.Advance()) { + HBasicBlock* block = block_it.Current(); + + // Check whether one of the successor is loop exit. + for (HBasicBlock* successor : block->GetSuccessors()) { + if (!loop_info->Contains(*successor)) { + analysis_results->exits_num_++; + + // We track number of invariant loop exits which correspond to HIf instruction and + // can be eliminated by loop peeling; other control flow instruction are ignored and will + // not cause loop peeling to happen as they either cannot be inside a loop, or by + // definition cannot be loop exits (unconditional instructions), or are not beneficial for + // the optimization. + HIf* hif = block->GetLastInstruction()->AsIf(); + if (hif != nullptr && !loop_info->Contains(*hif->InputAt(0)->GetBlock())) { + analysis_results->invariant_exits_num_++; + } + } + } + + for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { + HInstruction* instruction = it.Current(); + if (it.Current()->GetType() == DataType::Type::kInt64) { + analysis_results->has_long_type_instructions_ = true; + } + if (MakesScalarPeelingUnrollingNonBeneficial(instruction)) { + analysis_results->has_instructions_preventing_scalar_peeling_ = true; + analysis_results->has_instructions_preventing_scalar_unrolling_ = true; + } + analysis_results->instr_num_++; + } + analysis_results->bb_num_++; + } +} + +int64_t LoopAnalysis::GetLoopTripCount(HLoopInformation* loop_info, + const InductionVarRange* induction_range) { + int64_t trip_count; + if (!induction_range->HasKnownTripCount(loop_info, &trip_count)) { + trip_count = LoopAnalysisInfo::kUnknownTripCount; + } + return trip_count; +} + +// Default implementation of loop helper; used for all targets unless a custom implementation +// is provided. Enables scalar loop peeling and unrolling with the most conservative heuristics. +class ArchDefaultLoopHelper : public ArchNoOptsLoopHelper { + public: + // Scalar loop unrolling parameters and heuristics. + // + // Maximum possible unrolling factor. + static constexpr uint32_t kScalarMaxUnrollFactor = 2; + // Loop's maximum instruction count. Loops with higher count will not be peeled/unrolled. + static constexpr uint32_t kScalarHeuristicMaxBodySizeInstr = 17; + // Loop's maximum basic block count. Loops with higher count will not be peeled/unrolled. + static constexpr uint32_t kScalarHeuristicMaxBodySizeBlocks = 6; + // Maximum number of instructions to be created as a result of full unrolling. + static constexpr uint32_t kScalarHeuristicFullyUnrolledMaxInstrThreshold = 35; + + bool IsLoopNonBeneficialForScalarOpts(LoopAnalysisInfo* analysis_info) const OVERRIDE { + return analysis_info->HasLongTypeInstructions() || + IsLoopTooBig(analysis_info, + kScalarHeuristicMaxBodySizeInstr, + kScalarHeuristicMaxBodySizeBlocks); + } + + uint32_t GetScalarUnrollingFactor(const LoopAnalysisInfo* analysis_info) const OVERRIDE { + int64_t trip_count = analysis_info->GetTripCount(); + // Unroll only loops with known trip count. + if (trip_count == LoopAnalysisInfo::kUnknownTripCount) { + return LoopAnalysisInfo::kNoUnrollingFactor; + } + uint32_t desired_unrolling_factor = kScalarMaxUnrollFactor; + if (trip_count < desired_unrolling_factor || trip_count % desired_unrolling_factor != 0) { + return LoopAnalysisInfo::kNoUnrollingFactor; + } + + return desired_unrolling_factor; + } + + bool IsLoopPeelingEnabled() const OVERRIDE { return true; } + + bool IsFullUnrollingBeneficial(LoopAnalysisInfo* analysis_info) const OVERRIDE { + int64_t trip_count = analysis_info->GetTripCount(); + // We assume that trip count is known. + DCHECK_NE(trip_count, LoopAnalysisInfo::kUnknownTripCount); + size_t instr_num = analysis_info->GetNumberOfInstructions(); + return (trip_count * instr_num < kScalarHeuristicFullyUnrolledMaxInstrThreshold); + } + + protected: + bool IsLoopTooBig(LoopAnalysisInfo* loop_analysis_info, + size_t instr_threshold, + size_t bb_threshold) const { + size_t instr_num = loop_analysis_info->GetNumberOfInstructions(); + size_t bb_num = loop_analysis_info->GetNumberOfBasicBlocks(); + return (instr_num >= instr_threshold || bb_num >= bb_threshold); + } +}; + +// Custom implementation of loop helper for arm64 target. Enables heuristics for scalar loop +// peeling and unrolling and supports SIMD loop unrolling. +class Arm64LoopHelper : public ArchDefaultLoopHelper { + public: + // SIMD loop unrolling parameters and heuristics. + // + // Maximum possible unrolling factor. + static constexpr uint32_t kArm64SimdMaxUnrollFactor = 8; + // Loop's maximum instruction count. Loops with higher count will not be unrolled. + static constexpr uint32_t kArm64SimdHeuristicMaxBodySizeInstr = 50; + + // Loop's maximum instruction count. Loops with higher count will not be peeled/unrolled. + static constexpr uint32_t kArm64ScalarHeuristicMaxBodySizeInstr = 40; + // Loop's maximum basic block count. Loops with higher count will not be peeled/unrolled. + static constexpr uint32_t kArm64ScalarHeuristicMaxBodySizeBlocks = 8; + + bool IsLoopNonBeneficialForScalarOpts(LoopAnalysisInfo* loop_analysis_info) const OVERRIDE { + return IsLoopTooBig(loop_analysis_info, + kArm64ScalarHeuristicMaxBodySizeInstr, + kArm64ScalarHeuristicMaxBodySizeBlocks); + } + + uint32_t GetSIMDUnrollingFactor(HBasicBlock* block, + int64_t trip_count, + uint32_t max_peel, + uint32_t vector_length) const OVERRIDE { + // Don't unroll with insufficient iterations. + // TODO: Unroll loops with unknown trip count. + DCHECK_NE(vector_length, 0u); + if (trip_count < (2 * vector_length + max_peel)) { + return LoopAnalysisInfo::kNoUnrollingFactor; + } + // Don't unroll for large loop body size. + uint32_t instruction_count = block->GetInstructions().CountSize(); + if (instruction_count >= kArm64SimdHeuristicMaxBodySizeInstr) { + return LoopAnalysisInfo::kNoUnrollingFactor; + } + // Find a beneficial unroll factor with the following restrictions: + // - At least one iteration of the transformed loop should be executed. + // - The loop body shouldn't be "too big" (heuristic). + + uint32_t uf1 = kArm64SimdHeuristicMaxBodySizeInstr / instruction_count; + uint32_t uf2 = (trip_count - max_peel) / vector_length; + uint32_t unroll_factor = + TruncToPowerOfTwo(std::min({uf1, uf2, kArm64SimdMaxUnrollFactor})); + DCHECK_GE(unroll_factor, 1u); + return unroll_factor; + } +}; + +ArchNoOptsLoopHelper* ArchNoOptsLoopHelper::Create(InstructionSet isa, + ArenaAllocator* allocator) { + switch (isa) { + case InstructionSet::kArm64: { + return new (allocator) Arm64LoopHelper; + } + default: { + return new (allocator) ArchDefaultLoopHelper; + } + } +} + +} // namespace art diff --git a/compiler/optimizing/loop_analysis.h b/compiler/optimizing/loop_analysis.h new file mode 100644 index 0000000000000000000000000000000000000000..57509ee410019e079d1863c1c7818ab8660ca15a --- /dev/null +++ b/compiler/optimizing/loop_analysis.h @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_OPTIMIZING_LOOP_ANALYSIS_H_ +#define ART_COMPILER_OPTIMIZING_LOOP_ANALYSIS_H_ + +#include "nodes.h" + +namespace art { + +class InductionVarRange; +class LoopAnalysis; + +// Class to hold cached information on properties of the loop. +class LoopAnalysisInfo : public ValueObject { + public: + // No loop unrolling factor (just one copy of the loop-body). + static constexpr uint32_t kNoUnrollingFactor = 1; + // Used for unknown and non-constant trip counts (see InductionVarRange::HasKnownTripCount). + static constexpr int64_t kUnknownTripCount = -1; + + explicit LoopAnalysisInfo(HLoopInformation* loop_info) + : trip_count_(kUnknownTripCount), + bb_num_(0), + instr_num_(0), + exits_num_(0), + invariant_exits_num_(0), + has_instructions_preventing_scalar_peeling_(false), + has_instructions_preventing_scalar_unrolling_(false), + has_long_type_instructions_(false), + loop_info_(loop_info) {} + + int64_t GetTripCount() const { return trip_count_; } + size_t GetNumberOfBasicBlocks() const { return bb_num_; } + size_t GetNumberOfInstructions() const { return instr_num_; } + size_t GetNumberOfExits() const { return exits_num_; } + size_t GetNumberOfInvariantExits() const { return invariant_exits_num_; } + + bool HasInstructionsPreventingScalarPeeling() const { + return has_instructions_preventing_scalar_peeling_; + } + + bool HasInstructionsPreventingScalarUnrolling() const { + return has_instructions_preventing_scalar_unrolling_; + } + + bool HasInstructionsPreventingScalarOpts() const { + return HasInstructionsPreventingScalarPeeling() || HasInstructionsPreventingScalarUnrolling(); + } + + bool HasLongTypeInstructions() const { + return has_long_type_instructions_; + } + + HLoopInformation* GetLoopInfo() const { return loop_info_; } + + private: + // Trip count of the loop if known, kUnknownTripCount otherwise. + int64_t trip_count_; + // Number of basic blocks in the loop body. + size_t bb_num_; + // Number of instructions in the loop body. + size_t instr_num_; + // Number of loop's exits. + size_t exits_num_; + // Number of "if" loop exits (with HIf instruction) whose condition is loop-invariant. + size_t invariant_exits_num_; + // Whether the loop has instructions which make scalar loop peeling non-beneficial. + bool has_instructions_preventing_scalar_peeling_; + // Whether the loop has instructions which make scalar loop unrolling non-beneficial. + bool has_instructions_preventing_scalar_unrolling_; + // Whether the loop has instructions of primitive long type; unrolling these loop will + // likely introduce spill/fills on 32-bit targets. + bool has_long_type_instructions_; + + // Corresponding HLoopInformation. + HLoopInformation* loop_info_; + + friend class LoopAnalysis; +}; + +// Placeholder class for methods and routines used to analyse loops, calculate loop properties +// and characteristics. +class LoopAnalysis : public ValueObject { + public: + // Calculates loops basic properties like body size, exits number, etc. and fills + // 'analysis_results' with this information. + static void CalculateLoopBasicProperties(HLoopInformation* loop_info, + LoopAnalysisInfo* analysis_results, + int64_t trip_count); + + // Returns the trip count of the loop if it is known and kUnknownTripCount otherwise. + static int64_t GetLoopTripCount(HLoopInformation* loop_info, + const InductionVarRange* induction_range); + + private: + // Returns whether an instruction makes scalar loop peeling/unrolling non-beneficial. + // + // If in the loop body we have a dex/runtime call then its contribution to the whole + // loop performance will probably prevail. So peeling/unrolling optimization will not bring + // any noticeable performance improvement. It will increase the code size. + static bool MakesScalarPeelingUnrollingNonBeneficial(HInstruction* instruction) { + return (instruction->IsNewArray() || + instruction->IsNewInstance() || + instruction->IsUnresolvedInstanceFieldGet() || + instruction->IsUnresolvedInstanceFieldSet() || + instruction->IsUnresolvedStaticFieldGet() || + instruction->IsUnresolvedStaticFieldSet() || + // TODO: Support loops with intrinsified invokes. + instruction->IsInvoke()); + } +}; + +// +// Helper class which holds target-dependent methods and constants needed for loop optimizations. +// +// To support peeling/unrolling for a new architecture one needs to create new helper class, +// inherit it from this and add implementation for the following methods. +// +class ArchNoOptsLoopHelper : public ArenaObject { + public: + virtual ~ArchNoOptsLoopHelper() {} + + // Creates an instance of specialised helper for the target or default helper if the target + // doesn't support loop peeling and unrolling. + static ArchNoOptsLoopHelper* Create(InstructionSet isa, ArenaAllocator* allocator); + + // Returns whether the loop is not beneficial for loop peeling/unrolling. + // + // For example, if the loop body has too many instructions then peeling/unrolling optimization + // will not bring any noticeable performance improvement however will increase the code size. + // + // Returns 'true' by default, should be overridden by particular target loop helper. + virtual bool IsLoopNonBeneficialForScalarOpts( + LoopAnalysisInfo* loop_analysis_info ATTRIBUTE_UNUSED) const { return true; } + + // Returns optimal scalar unrolling factor for the loop. + // + // Returns kNoUnrollingFactor by default, should be overridden by particular target loop helper. + virtual uint32_t GetScalarUnrollingFactor( + const LoopAnalysisInfo* analysis_info ATTRIBUTE_UNUSED) const { + return LoopAnalysisInfo::kNoUnrollingFactor; + } + + // Returns whether scalar loop peeling is enabled, + // + // Returns 'false' by default, should be overridden by particular target loop helper. + virtual bool IsLoopPeelingEnabled() const { return false; } + + // Returns whether it is beneficial to fully unroll the loop. + // + // Returns 'false' by default, should be overridden by particular target loop helper. + virtual bool IsFullUnrollingBeneficial(LoopAnalysisInfo* analysis_info ATTRIBUTE_UNUSED) const { + return false; + } + + // Returns optimal SIMD unrolling factor for the loop. + // + // Returns kNoUnrollingFactor by default, should be overridden by particular target loop helper. + virtual uint32_t GetSIMDUnrollingFactor(HBasicBlock* block ATTRIBUTE_UNUSED, + int64_t trip_count ATTRIBUTE_UNUSED, + uint32_t max_peel ATTRIBUTE_UNUSED, + uint32_t vector_length ATTRIBUTE_UNUSED) const { + return LoopAnalysisInfo::kNoUnrollingFactor; + } +}; + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_LOOP_ANALYSIS_H_ diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 899496328ebb8df61239de5e197ba30cceda553c..7d66155b39e397ba82ccda419e1a6fe737cac92a 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -23,7 +23,7 @@ #include "arch/mips64/instruction_set_features_mips64.h" #include "arch/x86/instruction_set_features_x86.h" #include "arch/x86_64/instruction_set_features_x86_64.h" -#include "driver/compiler_driver.h" +#include "driver/compiler_options.h" #include "linear_order.h" #include "mirror/array-inl.h" #include "mirror/string.h" @@ -33,9 +33,6 @@ namespace art { // Enables vectorization (SIMDization) in the loop optimizer. static constexpr bool kEnableVectorization = true; -// No loop unrolling factor (just one copy of the loop-body). -static constexpr uint32_t kNoUnrollingFactor = 1; - // // Static helpers. // @@ -227,6 +224,7 @@ static bool IsNarrowerOperands(HInstruction* a, /*out*/ HInstruction** r, /*out*/ HInstruction** s, /*out*/ bool* is_unsigned) { + DCHECK(a != nullptr && b != nullptr); // Look for a matching sign extension. DataType::Type stype = HVecOperation::ToSignedType(type); if (IsSignExtensionAndGet(a, stype, r) && IsSignExtensionAndGet(b, stype, s)) { @@ -247,6 +245,7 @@ static bool IsNarrowerOperand(HInstruction* a, DataType::Type type, /*out*/ HInstruction** r, /*out*/ bool* is_unsigned) { + DCHECK(a != nullptr); // Look for a matching sign extension. DataType::Type stype = HVecOperation::ToSignedType(type); if (IsSignExtensionAndGet(a, stype, r)) { @@ -270,20 +269,28 @@ static uint32_t GetOtherVL(DataType::Type other_type, DataType::Type vector_type return vl >> (DataType::SizeShift(other_type) - DataType::SizeShift(vector_type)); } -// Detect up to two instructions a and b, and an acccumulated constant c. -static bool IsAddConstHelper(HInstruction* instruction, - /*out*/ HInstruction** a, - /*out*/ HInstruction** b, - /*out*/ int64_t* c, - int32_t depth) { - static constexpr int32_t kMaxDepth = 8; // don't search too deep +// Detect up to two added operands a and b and an acccumulated constant c. +static bool IsAddConst(HInstruction* instruction, + /*out*/ HInstruction** a, + /*out*/ HInstruction** b, + /*out*/ int64_t* c, + int32_t depth = 8) { // don't search too deep int64_t value = 0; + // Enter add/sub while still within reasonable depth. + if (depth > 0) { + if (instruction->IsAdd()) { + return IsAddConst(instruction->InputAt(0), a, b, c, depth - 1) && + IsAddConst(instruction->InputAt(1), a, b, c, depth - 1); + } else if (instruction->IsSub() && + IsInt64AndGet(instruction->InputAt(1), &value)) { + *c -= value; + return IsAddConst(instruction->InputAt(0), a, b, c, depth - 1); + } + } + // Otherwise, deal with leaf nodes. if (IsInt64AndGet(instruction, &value)) { *c += value; return true; - } else if (instruction->IsAdd() && depth <= kMaxDepth) { - return IsAddConstHelper(instruction->InputAt(0), a, b, c, depth + 1) && - IsAddConstHelper(instruction->InputAt(1), a, b, c, depth + 1); } else if (*a == nullptr) { *a = instruction; return true; @@ -291,42 +298,40 @@ static bool IsAddConstHelper(HInstruction* instruction, *b = instruction; return true; } - return false; // too many non-const operands + return false; // too many operands } -// Detect a + b + c for an optional constant c. -static bool IsAddConst(HInstruction* instruction, - /*out*/ HInstruction** a, - /*out*/ HInstruction** b, - /*out*/ int64_t* c) { - if (instruction->IsAdd()) { - // Try to find a + b and accumulated c. - if (IsAddConstHelper(instruction->InputAt(0), a, b, c, /*depth*/ 0) && - IsAddConstHelper(instruction->InputAt(1), a, b, c, /*depth*/ 0) && - *b != nullptr) { - return true; +// Detect a + b + c with optional constant c. +static bool IsAddConst2(HGraph* graph, + HInstruction* instruction, + /*out*/ HInstruction** a, + /*out*/ HInstruction** b, + /*out*/ int64_t* c) { + if (IsAddConst(instruction, a, b, c) && *a != nullptr) { + if (*b == nullptr) { + // Constant is usually already present, unless accumulated. + *b = graph->GetConstant(instruction->GetType(), (*c)); + *c = 0; } - // Found a + b. - *a = instruction->InputAt(0); - *b = instruction->InputAt(1); - *c = 0; return true; } return false; } -// Detect a + c for constant c. -static bool IsAddConst(HInstruction* instruction, - /*out*/ HInstruction** a, - /*out*/ int64_t* c) { - if (instruction->IsAdd()) { - if (IsInt64AndGet(instruction->InputAt(0), c)) { - *a = instruction->InputAt(1); - return true; - } else if (IsInt64AndGet(instruction->InputAt(1), c)) { - *a = instruction->InputAt(0); - return true; - } +// Detect a direct a - b or a hidden a - (-c). +static bool IsSubConst2(HGraph* graph, + HInstruction* instruction, + /*out*/ HInstruction** a, + /*out*/ HInstruction** b) { + int64_t c = 0; + if (instruction->IsSub()) { + *a = instruction->InputAt(0); + *b = instruction->InputAt(1); + return true; + } else if (IsAddConst(instruction, a, b, &c) && *a != nullptr && *b == nullptr) { + // Constant for the hidden subtraction. + *b = graph->GetConstant(instruction->GetType(), -c); + return true; } return false; } @@ -334,29 +339,12 @@ static bool IsAddConst(HInstruction* instruction, // Detect reductions of the following forms, // x = x_phi + .. // x = x_phi - .. -// x = max(x_phi, ..) -// x = min(x_phi, ..) static bool HasReductionFormat(HInstruction* reduction, HInstruction* phi) { if (reduction->IsAdd()) { return (reduction->InputAt(0) == phi && reduction->InputAt(1) != phi) || (reduction->InputAt(0) != phi && reduction->InputAt(1) == phi); } else if (reduction->IsSub()) { return (reduction->InputAt(0) == phi && reduction->InputAt(1) != phi); - } else if (reduction->IsInvokeStaticOrDirect()) { - switch (reduction->AsInvokeStaticOrDirect()->GetIntrinsic()) { - case Intrinsics::kMathMinIntInt: - case Intrinsics::kMathMinLongLong: - case Intrinsics::kMathMinFloatFloat: - case Intrinsics::kMathMinDoubleDouble: - case Intrinsics::kMathMaxIntInt: - case Intrinsics::kMathMaxLongLong: - case Intrinsics::kMathMaxFloatFloat: - case Intrinsics::kMathMaxDoubleDouble: - return (reduction->InputAt(0) == phi && reduction->InputAt(1) != phi) || - (reduction->InputAt(0) != phi && reduction->InputAt(1) == phi); - default: - return false; - } } return false; } @@ -365,10 +353,6 @@ static bool HasReductionFormat(HInstruction* reduction, HInstruction* phi) { static HVecReduce::ReductionKind GetReductionKind(HVecOperation* reduction) { if (reduction->IsVecAdd() || reduction->IsVecSub() || reduction->IsVecSADAccumulate()) { return HVecReduce::kSum; - } else if (reduction->IsVecMin()) { - return HVecReduce::kMin; - } else if (reduction->IsVecMax()) { - return HVecReduce::kMax; } LOG(FATAL) << "Unsupported SIMD reduction " << reduction->GetId(); UNREACHABLE(); @@ -401,17 +385,63 @@ static bool CheckInductionSetFullyRemoved(ScopedArenaSet* iset) { return true; } +// Tries to statically evaluate condition of the specified "HIf" for other condition checks. +static void TryToEvaluateIfCondition(HIf* instruction, HGraph* graph) { + HInstruction* cond = instruction->InputAt(0); + + // If a condition 'cond' is evaluated in an HIf instruction then in the successors of the + // IF_BLOCK we statically know the value of the condition 'cond' (TRUE in TRUE_SUCC, FALSE in + // FALSE_SUCC). Using that we can replace another evaluation (use) EVAL of the same 'cond' + // with TRUE value (FALSE value) if every path from the ENTRY_BLOCK to EVAL_BLOCK contains the + // edge HIF_BLOCK->TRUE_SUCC (HIF_BLOCK->FALSE_SUCC). + // if (cond) { if(cond) { + // if (cond) {} if (1) {} + // } else { =======> } else { + // if (cond) {} if (0) {} + // } } + if (!cond->IsConstant()) { + HBasicBlock* true_succ = instruction->IfTrueSuccessor(); + HBasicBlock* false_succ = instruction->IfFalseSuccessor(); + + DCHECK_EQ(true_succ->GetPredecessors().size(), 1u); + DCHECK_EQ(false_succ->GetPredecessors().size(), 1u); + + const HUseList& uses = cond->GetUses(); + for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) { + HInstruction* user = it->GetUser(); + size_t index = it->GetIndex(); + HBasicBlock* user_block = user->GetBlock(); + // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput(). + ++it; + if (true_succ->Dominates(user_block)) { + user->ReplaceInput(graph->GetIntConstant(1), index); + } else if (false_succ->Dominates(user_block)) { + user->ReplaceInput(graph->GetIntConstant(0), index); + } + } + } +} + +// Peel the first 'count' iterations of the loop. +static void PeelByCount(HLoopInformation* loop_info, int count) { + for (int i = 0; i < count; i++) { + // Perform peeling. + PeelUnrollSimpleHelper helper(loop_info); + helper.DoPeeling(); + } +} + // // Public methods. // HLoopOptimization::HLoopOptimization(HGraph* graph, - CompilerDriver* compiler_driver, + const CompilerOptions* compiler_options, HInductionVarAnalysis* induction_analysis, OptimizingCompilerStats* stats, const char* name) : HOptimization(graph, name, stats), - compiler_driver_(compiler_driver), + compiler_options_(compiler_options), induction_range_(induction_analysis), loop_allocator_(nullptr), global_allocator_(graph_->GetAllocator()), @@ -432,14 +462,18 @@ HLoopOptimization::HLoopOptimization(HGraph* graph, vector_preheader_(nullptr), vector_header_(nullptr), vector_body_(nullptr), - vector_index_(nullptr) { + vector_index_(nullptr), + arch_loop_helper_(ArchNoOptsLoopHelper::Create(compiler_options_ != nullptr + ? compiler_options_->GetInstructionSet() + : InstructionSet::kNone, + global_allocator_)) { } -void HLoopOptimization::Run() { +bool HLoopOptimization::Run() { // Skip if there is no loop or the graph has try-catch/irreducible loops. // TODO: make this less of a sledgehammer. if (!graph_->HasLoops() || graph_->HasTryCatch() || graph_->HasIrreducibleLoops()) { - return; + return false; } // Phase-local allocator. @@ -447,7 +481,7 @@ void HLoopOptimization::Run() { loop_allocator_ = &allocator; // Perform loop optimizations. - LocalRun(); + bool didLoopOpt = LocalRun(); if (top_loop_ == nullptr) { graph_->SetHasLoops(false); // no more loops } @@ -455,13 +489,16 @@ void HLoopOptimization::Run() { // Detach. loop_allocator_ = nullptr; last_loop_ = top_loop_ = nullptr; + + return didLoopOpt; } // // Loop setup and traversal. // -void HLoopOptimization::LocalRun() { +bool HLoopOptimization::LocalRun() { + bool didLoopOpt = false; // Build the linear order using the phase-local allocator. This step enables building // a loop hierarchy that properly reflects the outer-inner and previous-next relation. ScopedArenaVector linear_order(loop_allocator_->Adapter(kArenaAllocLinearOrder)); @@ -493,7 +530,7 @@ void HLoopOptimization::LocalRun() { vector_map_ = ↦ vector_permanent_map_ = &perm; // Traverse. - TraverseLoopsInnerToOuter(top_loop_); + didLoopOpt = TraverseLoopsInnerToOuter(top_loop_); // Detach. iset_ = nullptr; reductions_ = nullptr; @@ -501,6 +538,7 @@ void HLoopOptimization::LocalRun() { vector_map_ = nullptr; vector_permanent_map_ = nullptr; } + return didLoopOpt; } void HLoopOptimization::AddLoop(HLoopInformation* loop_info) { @@ -557,6 +595,7 @@ bool HLoopOptimization::TraverseLoopsInnerToOuter(LoopNode* node) { // loop if the induction of any inner loop has changed. if (TraverseLoopsInnerToOuter(node->inner)) { induction_range_.ReVisit(node->loop_info); + changed = true; } // Repeat simplifications in the loop-body until no more changes occur. // Note that since each simplification consists of eliminating code (without @@ -643,7 +682,7 @@ void HLoopOptimization::SimplifyBlocks(LoopNode* node) { } } -bool HLoopOptimization::OptimizeInnerLoop(LoopNode* node) { +bool HLoopOptimization::TryOptimizeInnerLoopFinite(LoopNode* node) { HBasicBlock* header = node->loop_info->GetHeader(); HBasicBlock* preheader = node->loop_info->GetPreHeader(); // Ensure loop header logic is finite. @@ -713,6 +752,146 @@ bool HLoopOptimization::OptimizeInnerLoop(LoopNode* node) { return false; } +bool HLoopOptimization::OptimizeInnerLoop(LoopNode* node) { + return TryOptimizeInnerLoopFinite(node) || TryPeelingAndUnrolling(node); +} + + + +// +// Scalar loop peeling and unrolling: generic part methods. +// + +bool HLoopOptimization::TryUnrollingForBranchPenaltyReduction(LoopAnalysisInfo* analysis_info, + bool generate_code) { + if (analysis_info->GetNumberOfExits() > 1) { + return false; + } + + uint32_t unrolling_factor = arch_loop_helper_->GetScalarUnrollingFactor(analysis_info); + if (unrolling_factor == LoopAnalysisInfo::kNoUnrollingFactor) { + return false; + } + + if (generate_code) { + // TODO: support other unrolling factors. + DCHECK_EQ(unrolling_factor, 2u); + + // Perform unrolling. + HLoopInformation* loop_info = analysis_info->GetLoopInfo(); + PeelUnrollSimpleHelper helper(loop_info); + helper.DoUnrolling(); + + // Remove the redundant loop check after unrolling. + HIf* copy_hif = + helper.GetBasicBlockMap()->Get(loop_info->GetHeader())->GetLastInstruction()->AsIf(); + int32_t constant = loop_info->Contains(*copy_hif->IfTrueSuccessor()) ? 1 : 0; + copy_hif->ReplaceInput(graph_->GetIntConstant(constant), 0u); + } + return true; +} + +bool HLoopOptimization::TryPeelingForLoopInvariantExitsElimination(LoopAnalysisInfo* analysis_info, + bool generate_code) { + HLoopInformation* loop_info = analysis_info->GetLoopInfo(); + if (!arch_loop_helper_->IsLoopPeelingEnabled()) { + return false; + } + + if (analysis_info->GetNumberOfInvariantExits() == 0) { + return false; + } + + if (generate_code) { + // Perform peeling. + PeelUnrollSimpleHelper helper(loop_info); + helper.DoPeeling(); + + // Statically evaluate loop check after peeling for loop invariant condition. + const SuperblockCloner::HInstructionMap* hir_map = helper.GetInstructionMap(); + for (auto entry : *hir_map) { + HInstruction* copy = entry.second; + if (copy->IsIf()) { + TryToEvaluateIfCondition(copy->AsIf(), graph_); + } + } + } + + return true; +} + +bool HLoopOptimization::TryFullUnrolling(LoopAnalysisInfo* analysis_info, bool generate_code) { + // Fully unroll loops with a known and small trip count. + int64_t trip_count = analysis_info->GetTripCount(); + if (!arch_loop_helper_->IsLoopPeelingEnabled() || + trip_count == LoopAnalysisInfo::kUnknownTripCount || + !arch_loop_helper_->IsFullUnrollingBeneficial(analysis_info)) { + return false; + } + + if (generate_code) { + // Peeling of the N first iterations (where N equals to the trip count) will effectively + // eliminate the loop: after peeling we will have N sequential iterations copied into the loop + // preheader and the original loop. The trip count of this loop will be 0 as the sequential + // iterations are executed first and there are exactly N of them. Thus we can statically + // evaluate the loop exit condition to 'false' and fully eliminate it. + // + // Here is an example of full unrolling of a loop with a trip count 2: + // + // loop_cond_1 + // loop_body_1 <- First iteration. + // | + // \ v + // ==\ loop_cond_2 + // ==/ loop_body_2 <- Second iteration. + // / | + // <- v <- + // loop_cond \ loop_cond \ <- This cond is always false. + // loop_body _/ loop_body _/ + // + HLoopInformation* loop_info = analysis_info->GetLoopInfo(); + PeelByCount(loop_info, trip_count); + HIf* loop_hif = loop_info->GetHeader()->GetLastInstruction()->AsIf(); + int32_t constant = loop_info->Contains(*loop_hif->IfTrueSuccessor()) ? 0 : 1; + loop_hif->ReplaceInput(graph_->GetIntConstant(constant), 0u); + } + + return true; +} + +bool HLoopOptimization::TryPeelingAndUnrolling(LoopNode* node) { + // Don't run peeling/unrolling if compiler_options_ is nullptr (i.e., running under tests) + // as InstructionSet is needed. + if (compiler_options_ == nullptr) { + return false; + } + + HLoopInformation* loop_info = node->loop_info; + int64_t trip_count = LoopAnalysis::GetLoopTripCount(loop_info, &induction_range_); + LoopAnalysisInfo analysis_info(loop_info); + LoopAnalysis::CalculateLoopBasicProperties(loop_info, &analysis_info, trip_count); + + if (analysis_info.HasInstructionsPreventingScalarOpts() || + arch_loop_helper_->IsLoopNonBeneficialForScalarOpts(&analysis_info)) { + return false; + } + + if (!TryFullUnrolling(&analysis_info, /*generate_code*/ false) && + !TryPeelingForLoopInvariantExitsElimination(&analysis_info, /*generate_code*/ false) && + !TryUnrollingForBranchPenaltyReduction(&analysis_info, /*generate_code*/ false)) { + return false; + } + + // Run 'IsLoopClonable' the last as it might be time-consuming. + if (!PeelUnrollHelper::IsLoopClonable(loop_info)) { + return false; + } + + return TryFullUnrolling(&analysis_info) || + TryPeelingForLoopInvariantExitsElimination(&analysis_info) || + TryUnrollingForBranchPenaltyReduction(&analysis_info); +} + // // Loop vectorization. The implementation is based on the book by Aart J.C. Bik: // "The Software Vectorization Handbook. Applying Multimedia Extensions for Maximum Performance." @@ -843,7 +1022,8 @@ void HLoopOptimization::Vectorize(LoopNode* node, HBasicBlock* preheader = node->loop_info->GetPreHeader(); // Pick a loop unrolling factor for the vector loop. - uint32_t unroll = GetUnrollingFactor(block, trip_count); + uint32_t unroll = arch_loop_helper_->GetSIMDUnrollingFactor( + block, trip_count, MaxNumberPeeled(), vector_length_); uint32_t chunk = vector_length_ * unroll; DCHECK(trip_count == 0 || (trip_count >= MaxNumberPeeled() + chunk)); @@ -948,7 +1128,7 @@ void HLoopOptimization::Vectorize(LoopNode* node, vector_index_, ptc, graph_->GetConstant(induc_type, 1), - kNoUnrollingFactor); + LoopAnalysisInfo::kNoUnrollingFactor); } // Generate vector loop, possibly further unrolled: @@ -975,7 +1155,7 @@ void HLoopOptimization::Vectorize(LoopNode* node, vector_index_, stc, graph_->GetConstant(induc_type, 1), - kNoUnrollingFactor); + LoopAnalysisInfo::kNoUnrollingFactor); } // Link reductions to their final uses. @@ -1082,6 +1262,11 @@ bool HLoopOptimization::VectorizeDef(LoopNode* node, HInstruction* index = instruction->InputAt(1); HInstruction* value = instruction->InputAt(2); HInstruction* offset = nullptr; + // For narrow types, explicit type conversion may have been + // optimized way, so set the no hi bits restriction here. + if (DataType::Size(type) <= 2) { + restrictions |= kNoHiBits; + } if (TrySetVectorType(type, &restrictions) && node->loop_info->IsDefinedOutOfTheLoop(base) && induction_range_.IsUnitStride(instruction, index, graph_, &offset) && @@ -1124,7 +1309,6 @@ bool HLoopOptimization::VectorizeDef(LoopNode* node, return !IsUsedOutsideLoop(node->loop_info, instruction) && !instruction->DoesAnyWrite(); } -// TODO: saturation arithmetic. bool HLoopOptimization::VectorizeUse(LoopNode* node, HInstruction* instruction, bool generate_code, @@ -1297,86 +1481,37 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node, return true; } } - } else if (instruction->IsInvokeStaticOrDirect()) { - // Accept particular intrinsics. - HInvokeStaticOrDirect* invoke = instruction->AsInvokeStaticOrDirect(); - switch (invoke->GetIntrinsic()) { - case Intrinsics::kMathAbsInt: - case Intrinsics::kMathAbsLong: - case Intrinsics::kMathAbsFloat: - case Intrinsics::kMathAbsDouble: { - // Deal with vector restrictions. - HInstruction* opa = instruction->InputAt(0); - HInstruction* r = opa; - bool is_unsigned = false; - if (HasVectorRestrictions(restrictions, kNoAbs)) { - return false; - } else if (HasVectorRestrictions(restrictions, kNoHiBits) && - (!IsNarrowerOperand(opa, type, &r, &is_unsigned) || is_unsigned)) { - return false; // reject, unless operand is sign-extension narrower - } - // Accept ABS(x) for vectorizable operand. - DCHECK(r != nullptr); - if (generate_code && vector_mode_ != kVector) { // de-idiom - r = opa; - } - if (VectorizeUse(node, r, generate_code, type, restrictions)) { - if (generate_code) { - GenerateVecOp(instruction, - vector_map_->Get(r), - nullptr, - HVecOperation::ToProperType(type, is_unsigned)); - } - return true; - } - return false; - } - case Intrinsics::kMathMinIntInt: - case Intrinsics::kMathMinLongLong: - case Intrinsics::kMathMinFloatFloat: - case Intrinsics::kMathMinDoubleDouble: - case Intrinsics::kMathMaxIntInt: - case Intrinsics::kMathMaxLongLong: - case Intrinsics::kMathMaxFloatFloat: - case Intrinsics::kMathMaxDoubleDouble: { - // Deal with vector restrictions. - HInstruction* opa = instruction->InputAt(0); - HInstruction* opb = instruction->InputAt(1); - HInstruction* r = opa; - HInstruction* s = opb; - bool is_unsigned = false; - if (HasVectorRestrictions(restrictions, kNoMinMax)) { - return false; - } else if (HasVectorRestrictions(restrictions, kNoHiBits) && - !IsNarrowerOperands(opa, opb, type, &r, &s, &is_unsigned)) { - return false; // reject, unless all operands are same-extension narrower - } - // Accept MIN/MAX(x, y) for vectorizable operands. - DCHECK(r != nullptr); - DCHECK(s != nullptr); - if (generate_code && vector_mode_ != kVector) { // de-idiom - r = opa; - s = opb; - } - if (VectorizeUse(node, r, generate_code, type, restrictions) && - VectorizeUse(node, s, generate_code, type, restrictions)) { - if (generate_code) { - GenerateVecOp( - instruction, vector_map_->Get(r), vector_map_->Get(s), type, is_unsigned); - } - return true; - } - return false; + } else if (instruction->IsAbs()) { + // Deal with vector restrictions. + HInstruction* opa = instruction->InputAt(0); + HInstruction* r = opa; + bool is_unsigned = false; + if (HasVectorRestrictions(restrictions, kNoAbs)) { + return false; + } else if (HasVectorRestrictions(restrictions, kNoHiBits) && + (!IsNarrowerOperand(opa, type, &r, &is_unsigned) || is_unsigned)) { + return false; // reject, unless operand is sign-extension narrower + } + // Accept ABS(x) for vectorizable operand. + DCHECK(r != nullptr); + if (generate_code && vector_mode_ != kVector) { // de-idiom + r = opa; + } + if (VectorizeUse(node, r, generate_code, type, restrictions)) { + if (generate_code) { + GenerateVecOp(instruction, + vector_map_->Get(r), + nullptr, + HVecOperation::ToProperType(type, is_unsigned)); } - default: - return false; - } // switch + return true; + } } return false; } uint32_t HLoopOptimization::GetVectorSizeInBytes() { - switch (compiler_driver_->GetInstructionSet()) { + switch (compiler_options_->GetInstructionSet()) { case InstructionSet::kArm: case InstructionSet::kThumb2: return 8; // 64-bit SIMD @@ -1386,8 +1521,8 @@ uint32_t HLoopOptimization::GetVectorSizeInBytes() { } bool HLoopOptimization::TrySetVectorType(DataType::Type type, uint64_t* restrictions) { - const InstructionSetFeatures* features = compiler_driver_->GetInstructionSetFeatures(); - switch (compiler_driver_->GetInstructionSet()) { + const InstructionSetFeatures* features = compiler_options_->GetInstructionSetFeatures(); + switch (compiler_options_->GetInstructionSet()) { case InstructionSet::kArm: case InstructionSet::kThumb2: // Allow vectorization for all ARM devices, because Android assumes that @@ -1426,7 +1561,7 @@ bool HLoopOptimization::TrySetVectorType(DataType::Type type, uint64_t* restrict *restrictions |= kNoDiv; return TrySetVectorLength(4); case DataType::Type::kInt64: - *restrictions |= kNoDiv | kNoMul | kNoMinMax; + *restrictions |= kNoDiv | kNoMul; return TrySetVectorLength(2); case DataType::Type::kFloat32: *restrictions |= kNoReduction; @@ -1456,13 +1591,13 @@ bool HLoopOptimization::TrySetVectorType(DataType::Type type, uint64_t* restrict *restrictions |= kNoDiv | kNoSAD; return TrySetVectorLength(4); case DataType::Type::kInt64: - *restrictions |= kNoMul | kNoDiv | kNoShr | kNoAbs | kNoMinMax | kNoSAD; + *restrictions |= kNoMul | kNoDiv | kNoShr | kNoAbs | kNoSAD; return TrySetVectorLength(2); case DataType::Type::kFloat32: - *restrictions |= kNoMinMax | kNoReduction; // minmax: -0.0 vs +0.0 + *restrictions |= kNoReduction; return TrySetVectorLength(4); case DataType::Type::kFloat64: - *restrictions |= kNoMinMax | kNoReduction; // minmax: -0.0 vs +0.0 + *restrictions |= kNoReduction; return TrySetVectorLength(2); default: break; @@ -1488,10 +1623,10 @@ bool HLoopOptimization::TrySetVectorType(DataType::Type type, uint64_t* restrict *restrictions |= kNoDiv; return TrySetVectorLength(2); case DataType::Type::kFloat32: - *restrictions |= kNoMinMax | kNoReduction; // min/max(x, NaN) + *restrictions |= kNoReduction; return TrySetVectorLength(4); case DataType::Type::kFloat64: - *restrictions |= kNoMinMax | kNoReduction; // min/max(x, NaN) + *restrictions |= kNoReduction; return TrySetVectorLength(2); default: break; @@ -1517,10 +1652,10 @@ bool HLoopOptimization::TrySetVectorType(DataType::Type type, uint64_t* restrict *restrictions |= kNoDiv; return TrySetVectorLength(2); case DataType::Type::kFloat32: - *restrictions |= kNoMinMax | kNoReduction; // min/max(x, NaN) + *restrictions |= kNoReduction; return TrySetVectorLength(4); case DataType::Type::kFloat64: - *restrictions |= kNoMinMax | kNoReduction; // min/max(x, NaN) + *restrictions |= kNoReduction; return TrySetVectorLength(2); default: break; @@ -1745,8 +1880,7 @@ HInstruction* HLoopOptimization::ReduceAndExtractIfNeeded(HInstruction* instruct void HLoopOptimization::GenerateVecOp(HInstruction* org, HInstruction* opa, HInstruction* opb, - DataType::Type type, - bool is_unsigned) { + DataType::Type type) { uint32_t dex_pc = org->GetDexPc(); HInstruction* vector = nullptr; DataType::Type org_type = org->GetType(); @@ -1811,83 +1945,11 @@ void HLoopOptimization::GenerateVecOp(HInstruction* org, GENERATE_VEC( new (global_allocator_) HVecUShr(global_allocator_, opa, opb, type, vector_length_, dex_pc), new (global_allocator_) HUShr(org_type, opa, opb, dex_pc)); - case HInstruction::kInvokeStaticOrDirect: { - HInvokeStaticOrDirect* invoke = org->AsInvokeStaticOrDirect(); - if (vector_mode_ == kVector) { - switch (invoke->GetIntrinsic()) { - case Intrinsics::kMathAbsInt: - case Intrinsics::kMathAbsLong: - case Intrinsics::kMathAbsFloat: - case Intrinsics::kMathAbsDouble: - DCHECK(opb == nullptr); - vector = new (global_allocator_) - HVecAbs(global_allocator_, opa, type, vector_length_, dex_pc); - break; - case Intrinsics::kMathMinIntInt: - case Intrinsics::kMathMinLongLong: - case Intrinsics::kMathMinFloatFloat: - case Intrinsics::kMathMinDoubleDouble: { - vector = new (global_allocator_) - HVecMin(global_allocator_, - opa, - opb, - HVecOperation::ToProperType(type, is_unsigned), - vector_length_, - dex_pc); - break; - } - case Intrinsics::kMathMaxIntInt: - case Intrinsics::kMathMaxLongLong: - case Intrinsics::kMathMaxFloatFloat: - case Intrinsics::kMathMaxDoubleDouble: { - vector = new (global_allocator_) - HVecMax(global_allocator_, - opa, - opb, - HVecOperation::ToProperType(type, is_unsigned), - vector_length_, - dex_pc); - break; - } - default: - LOG(FATAL) << "Unsupported SIMD intrinsic " << org->GetId(); - UNREACHABLE(); - } // switch invoke - } else { - // In scalar code, simply clone the method invoke, and replace its operands with the - // corresponding new scalar instructions in the loop. The instruction will get an - // environment while being inserted from the instruction map in original program order. - DCHECK(vector_mode_ == kSequential); - size_t num_args = invoke->GetNumberOfArguments(); - HInvokeStaticOrDirect* new_invoke = new (global_allocator_) HInvokeStaticOrDirect( - global_allocator_, - num_args, - invoke->GetType(), - invoke->GetDexPc(), - invoke->GetDexMethodIndex(), - invoke->GetResolvedMethod(), - invoke->GetDispatchInfo(), - invoke->GetInvokeType(), - invoke->GetTargetMethod(), - invoke->GetClinitCheckRequirement()); - HInputsRef inputs = invoke->GetInputs(); - size_t num_inputs = inputs.size(); - DCHECK_LE(num_args, num_inputs); - DCHECK_EQ(num_inputs, new_invoke->GetInputs().size()); // both invokes agree - for (size_t index = 0; index < num_inputs; ++index) { - HInstruction* new_input = index < num_args - ? vector_map_->Get(inputs[index]) - : inputs[index]; // beyond arguments: just pass through - new_invoke->SetArgumentAt(index, new_input); - } - new_invoke->SetIntrinsic(invoke->GetIntrinsic(), - kNeedsEnvironmentOrCache, - kNoSideEffects, - kNoThrow); - vector = new_invoke; - } - break; - } + case HInstruction::kAbs: + DCHECK(opb == nullptr); + GENERATE_VEC( + new (global_allocator_) HVecAbs(global_allocator_, opa, type, vector_length_, dex_pc), + new (global_allocator_) HAbs(org_type, opa, dex_pc)); default: break; } // switch @@ -1924,8 +1986,7 @@ bool HLoopOptimization::VectorizeHalvingAddIdiom(LoopNode* node, HInstruction* a = nullptr; HInstruction* b = nullptr; int64_t c = 0; - if (IsAddConst(instruction->InputAt(0), /*out*/ &a, /*out*/ &b, /*out*/ &c)) { - DCHECK(a != nullptr && b != nullptr); + if (IsAddConst2(graph_, instruction->InputAt(0), /*out*/ &a, /*out*/ &b, /*out*/ &c)) { // Accept c == 1 (rounded) or c == 0 (not rounded). bool is_rounded = false; if (c == 1) { @@ -1947,8 +2008,7 @@ bool HLoopOptimization::VectorizeHalvingAddIdiom(LoopNode* node, } // Accept recognized halving add for vectorizable operands. Vectorized code uses the // shorthand idiomatic operation. Sequential code uses the original scalar expressions. - DCHECK(r != nullptr); - DCHECK(s != nullptr); + DCHECK(r != nullptr && s != nullptr); if (generate_code && vector_mode_ != kVector) { // de-idiom r = instruction->InputAt(0); s = instruction->InputAt(1); @@ -1998,21 +2058,11 @@ bool HLoopOptimization::VectorizeSADIdiom(LoopNode* node, HInstruction* v = instruction->InputAt(1); HInstruction* a = nullptr; HInstruction* b = nullptr; - if (v->IsInvokeStaticOrDirect() && - (v->AsInvokeStaticOrDirect()->GetIntrinsic() == Intrinsics::kMathAbsInt || - v->AsInvokeStaticOrDirect()->GetIntrinsic() == Intrinsics::kMathAbsLong)) { - HInstruction* x = v->InputAt(0); - if (x->GetType() == reduction_type) { - int64_t c = 0; - if (x->IsSub()) { - a = x->InputAt(0); - b = x->InputAt(1); - } else if (IsAddConst(x, /*out*/ &a, /*out*/ &c)) { - b = graph_->GetConstant(reduction_type, -c); // hidden SUB! - } - } - } - if (a == nullptr || b == nullptr) { + if (v->IsAbs() && + v->GetType() == reduction_type && + IsSubConst2(graph_, v->InputAt(0), /*out*/ &a, /*out*/ &b)) { + DCHECK(a != nullptr && b != nullptr); + } else { return false; } // Accept same-type or consistent sign extension for narrower-type on operands a and b. @@ -2045,8 +2095,7 @@ bool HLoopOptimization::VectorizeSADIdiom(LoopNode* node, } // Accept SAD idiom for vectorizable operands. Vectorized code uses the shorthand // idiomatic operation. Sequential code uses the original scalar expressions. - DCHECK(r != nullptr); - DCHECK(s != nullptr); + DCHECK(r != nullptr && s != nullptr); if (generate_code && vector_mode_ != kVector) { // de-idiom r = s = v->InputAt(0); } @@ -2054,14 +2103,13 @@ bool HLoopOptimization::VectorizeSADIdiom(LoopNode* node, VectorizeUse(node, r, generate_code, sub_type, restrictions) && VectorizeUse(node, s, generate_code, sub_type, restrictions)) { if (generate_code) { - reduction_type = HVecOperation::ToProperType(reduction_type, is_unsigned); if (vector_mode_ == kVector) { vector_map_->Put(instruction, new (global_allocator_) HVecSADAccumulate( global_allocator_, vector_map_->Get(q), vector_map_->Get(r), vector_map_->Get(s), - reduction_type, + HVecOperation::ToProperType(reduction_type, is_unsigned), GetOtherVL(reduction_type, sub_type, vector_length_), kNoDexPc)); MaybeRecordStat(stats_, MethodCompilationStat::kLoopVectorizedIdiom); @@ -2134,41 +2182,6 @@ bool HLoopOptimization::IsVectorizationProfitable(int64_t trip_count) { return true; } -static constexpr uint32_t ARM64_SIMD_MAXIMUM_UNROLL_FACTOR = 8; -static constexpr uint32_t ARM64_SIMD_HEURISTIC_MAX_BODY_SIZE = 50; - -uint32_t HLoopOptimization::GetUnrollingFactor(HBasicBlock* block, int64_t trip_count) { - uint32_t max_peel = MaxNumberPeeled(); - switch (compiler_driver_->GetInstructionSet()) { - case InstructionSet::kArm64: { - // Don't unroll with insufficient iterations. - // TODO: Unroll loops with unknown trip count. - DCHECK_NE(vector_length_, 0u); - if (trip_count < (2 * vector_length_ + max_peel)) { - return kNoUnrollingFactor; - } - // Don't unroll for large loop body size. - uint32_t instruction_count = block->GetInstructions().CountSize(); - if (instruction_count >= ARM64_SIMD_HEURISTIC_MAX_BODY_SIZE) { - return kNoUnrollingFactor; - } - // Find a beneficial unroll factor with the following restrictions: - // - At least one iteration of the transformed loop should be executed. - // - The loop body shouldn't be "too big" (heuristic). - uint32_t uf1 = ARM64_SIMD_HEURISTIC_MAX_BODY_SIZE / instruction_count; - uint32_t uf2 = (trip_count - max_peel) / vector_length_; - uint32_t unroll_factor = - TruncToPowerOfTwo(std::min({uf1, uf2, ARM64_SIMD_MAXIMUM_UNROLL_FACTOR})); - DCHECK_GE(unroll_factor, 1u); - return unroll_factor; - } - case InstructionSet::kX86: - case InstructionSet::kX86_64: - default: - return kNoUnrollingFactor; - } -} - // // Helpers. // diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h index a707ad13580fc341de6ed5d1eb4327822189b146..644b740ed40450dcbc3151707a9e9d725be775c7 100644 --- a/compiler/optimizing/loop_optimization.h +++ b/compiler/optimizing/loop_optimization.h @@ -20,12 +20,15 @@ #include "base/scoped_arena_allocator.h" #include "base/scoped_arena_containers.h" #include "induction_var_range.h" +#include "loop_analysis.h" #include "nodes.h" #include "optimization.h" +#include "superblock_cloner.h" namespace art { -class CompilerDriver; +class CompilerOptions; +class ArchNoOptsLoopHelper; /** * Loop optimizations. Builds a loop hierarchy and applies optimizations to @@ -35,12 +38,12 @@ class CompilerDriver; class HLoopOptimization : public HOptimization { public: HLoopOptimization(HGraph* graph, - CompilerDriver* compiler_driver, + const CompilerOptions* compiler_options, HInductionVarAnalysis* induction_analysis, OptimizingCompilerStats* stats, const char* name = kLoopOptimizationPassName); - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kLoopOptimizationPassName = "loop_optimization"; @@ -75,11 +78,10 @@ class HLoopOptimization : public HOptimization { kNoSignedHAdd = 1 << 5, // no signed halving add kNoUnroundedHAdd = 1 << 6, // no unrounded halving add kNoAbs = 1 << 7, // no absolute value - kNoMinMax = 1 << 8, // no min/max - kNoStringCharAt = 1 << 9, // no StringCharAt - kNoReduction = 1 << 10, // no reduction - kNoSAD = 1 << 11, // no sum of absolute differences (SAD) - kNoWideSAD = 1 << 12, // no sum of absolute differences (SAD) with operand widening + kNoStringCharAt = 1 << 8, // no StringCharAt + kNoReduction = 1 << 9, // no reduction + kNoSAD = 1 << 10, // no sum of absolute differences (SAD) + kNoWideSAD = 1 << 11, // no sum of absolute differences (SAD) with operand widening }; /* @@ -119,7 +121,7 @@ class HLoopOptimization : public HOptimization { // Loop setup and traversal. // - void LocalRun(); + bool LocalRun(); void AddLoop(HLoopInformation* loop_info); void RemoveLoop(LoopNode* node); @@ -134,10 +136,34 @@ class HLoopOptimization : public HOptimization { void SimplifyInduction(LoopNode* node); void SimplifyBlocks(LoopNode* node); - // Performs optimizations specific to inner loop (empty loop removal, + // Performs optimizations specific to inner loop with finite header logic (empty loop removal, // unrolling, vectorization). Returns true if anything changed. + bool TryOptimizeInnerLoopFinite(LoopNode* node); + + // Performs optimizations specific to inner loop. Returns true if anything changed. bool OptimizeInnerLoop(LoopNode* node); + // Tries to apply loop unrolling for branch penalty reduction and better instruction scheduling + // opportunities. Returns whether transformation happened. 'generate_code' determines whether the + // optimization should be actually applied. + bool TryUnrollingForBranchPenaltyReduction(LoopAnalysisInfo* analysis_info, + bool generate_code = true); + + // Tries to apply loop peeling for loop invariant exits elimination. Returns whether + // transformation happened. 'generate_code' determines whether the optimization should be + // actually applied. + bool TryPeelingForLoopInvariantExitsElimination(LoopAnalysisInfo* analysis_info, + bool generate_code = true); + + // Tries to perform whole loop unrolling for a small loop with a small trip count to eliminate + // the loop check overhead and to have more opportunities for inter-iteration optimizations. + // Returns whether transformation happened. 'generate_code' determines whether the optimization + // should be actually applied. + bool TryFullUnrolling(LoopAnalysisInfo* analysis_info, bool generate_code = true); + + // Tries to apply scalar loop peeling and unrolling. + bool TryPeelingAndUnrolling(LoopNode* node); + // // Vectorization analysis and synthesis. // @@ -173,10 +199,14 @@ class HLoopOptimization : public HOptimization { void GenerateVecOp(HInstruction* org, HInstruction* opa, HInstruction* opb, - DataType::Type type, - bool is_unsigned = false); + DataType::Type type); // Vectorization idioms. + bool VectorizeSaturationIdiom(LoopNode* node, + HInstruction* instruction, + bool generate_code, + DataType::Type type, + uint64_t restrictions); bool VectorizeHalvingAddIdiom(LoopNode* node, HInstruction* instruction, bool generate_code, @@ -197,7 +227,6 @@ class HLoopOptimization : public HOptimization { const ArrayReference* peeling_candidate); uint32_t MaxNumberPeeled(); bool IsVectorizationProfitable(int64_t trip_count); - uint32_t GetUnrollingFactor(HBasicBlock* block, int64_t trip_count); // // Helpers. @@ -227,8 +256,8 @@ class HLoopOptimization : public HOptimization { void RemoveDeadInstructions(const HInstructionList& list); bool CanRemoveCycle(); // Whether the current 'iset_' is removable. - // Compiler driver (to query ISA features). - const CompilerDriver* compiler_driver_; + // Compiler options (to query ISA features). + const CompilerOptions* compiler_options_; // Range information based on prior induction variable analysis. InductionVarRange induction_range_; @@ -291,6 +320,9 @@ class HLoopOptimization : public HOptimization { HBasicBlock* vector_body_; // body of the new loop HInstruction* vector_index_; // normalized index of the new loop + // Helper for target-specific behaviour for loop optimizations. + ArchNoOptsLoopHelper* arch_loop_helper_; + friend class LoopOptimizationTest; DISALLOW_COPY_AND_ASSIGN(HLoopOptimization); diff --git a/compiler/optimizing/loop_optimization_test.cc b/compiler/optimizing/loop_optimization_test.cc index db8368986c96a66aaa9bb920295055005ef3e9ec..c7cc661303a9f44032ad097df73d2abe79575d09 100644 --- a/compiler/optimizing/loop_optimization_test.cc +++ b/compiler/optimizing/loop_optimization_test.cc @@ -29,7 +29,8 @@ class LoopOptimizationTest : public OptimizingUnitTest { LoopOptimizationTest() : graph_(CreateGraph()), iva_(new (GetAllocator()) HInductionVarAnalysis(graph_)), - loop_opt_(new (GetAllocator()) HLoopOptimization(graph_, nullptr, iva_, nullptr)) { + loop_opt_(new (GetAllocator()) HLoopOptimization( + graph_, /* compiler_options */ nullptr, iva_, /* stats */ nullptr)) { BuildGraph(); } @@ -227,11 +228,14 @@ TEST_F(LoopOptimizationTest, SimplifyLoopReoderPredecessors) { graph_->ClearDominanceInformation(); graph_->BuildDominatorTree(); + // BuildDominatorTree inserts a block beetween loop header and entry block. + EXPECT_EQ(header->GetPredecessors()[0]->GetSinglePredecessor(), entry_block_); + // Check that after optimizations in BuildDominatorTree()/SimplifyCFG() phi inputs // are still mapped correctly to the block predecessors. for (size_t i = 0, e = phi->InputCount(); i < e; i++) { HInstruction* input = phi->InputAt(i); - ASSERT_TRUE(input->GetBlock()->Dominates(header->GetPredecessors()[i])); + EXPECT_TRUE(input->GetBlock()->Dominates(header->GetPredecessors()[i])); } } diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index f6ba19f22a862f7ef16de3f663224600bbb8412f..50ce7559f5d26a0e8b69caa397ed23def929fa42 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -22,6 +22,7 @@ #include "base/bit_vector-inl.h" #include "base/stl_util.h" #include "class_linker-inl.h" +#include "class_root.h" #include "code_generator.h" #include "common_dominator.h" #include "intrinsics.h" @@ -40,9 +41,8 @@ static constexpr bool kEnableFloatingPointStaticEvaluation = (FLT_EVAL_METHOD == void HGraph::InitializeInexactObjectRTI(VariableSizedHandleScope* handles) { ScopedObjectAccess soa(Thread::Current()); // Create the inexact Object reference type and store it in the HGraph. - ClassLinker* linker = Runtime::Current()->GetClassLinker(); inexact_object_rti_ = ReferenceTypeInfo::Create( - handles->NewHandle(linker->GetClassRoot(ClassLinker::kJavaLangObject)), + handles->NewHandle(GetClassRoot()), /* is_exact */ false); } @@ -1121,6 +1121,23 @@ void HEnvironment::RemoveAsUserOfInput(size_t index) const { user->FixUpUserRecordsAfterEnvUseRemoval(before_env_use_node); } +void HEnvironment::ReplaceInput(HInstruction* replacement, size_t index) { + const HUserRecord& env_use_record = vregs_[index]; + HInstruction* orig_instr = env_use_record.GetInstruction(); + + DCHECK(orig_instr != replacement); + + HUseList::iterator before_use_node = env_use_record.GetBeforeUseNode(); + // Note: fixup_end remains valid across splice_after(). + auto fixup_end = replacement->env_uses_.empty() ? replacement->env_uses_.begin() + : ++replacement->env_uses_.begin(); + replacement->env_uses_.splice_after(replacement->env_uses_.before_begin(), + env_use_record.GetInstruction()->env_uses_, + before_use_node); + replacement->FixUpUserRecordsAfterEnvUseInsertion(fixup_end); + orig_instr->FixUpUserRecordsAfterEnvUseRemoval(before_use_node); +} + HInstruction* HInstruction::GetNextDisregardingMoves() const { HInstruction* next = GetNext(); while (next != nullptr && next->IsParallelMove()) { @@ -1288,6 +1305,19 @@ void HInstruction::ReplaceUsesDominatedBy(HInstruction* dominator, HInstruction* } } +void HInstruction::ReplaceEnvUsesDominatedBy(HInstruction* dominator, HInstruction* replacement) { + const HUseList& uses = GetEnvUses(); + for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) { + HEnvironment* user = it->GetUser(); + size_t index = it->GetIndex(); + // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput(). + ++it; + if (dominator->StrictlyDominates(user->GetHolder())) { + user->ReplaceInput(replacement, index); + } + } +} + void HInstruction::ReplaceInput(HInstruction* replacement, size_t index) { HUserRecord input_use = InputRecordAt(index); if (input_use.GetInstruction() == replacement) { @@ -1680,10 +1710,9 @@ bool HCondition::IsBeforeWhenDisregardMoves(HInstruction* instruction) const { } bool HInstruction::Equals(const HInstruction* other) const { - if (!InstructionTypeEquals(other)) return false; - DCHECK_EQ(GetKind(), other->GetKind()); - if (!InstructionDataEquals(other)) return false; + if (GetKind() != other->GetKind()) return false; if (GetType() != other->GetType()) return false; + if (!InstructionDataEquals(other)) return false; HConstInputsRef inputs = GetInputs(); HConstInputsRef other_inputs = other->GetInputs(); if (inputs.size() != other_inputs.size()) return false; @@ -1698,7 +1727,7 @@ bool HInstruction::Equals(const HInstruction* other) const { std::ostream& operator<<(std::ostream& os, const HInstruction::InstructionKind& rhs) { #define DECLARE_CASE(type, super) case HInstruction::k##type: os << #type; break; switch (rhs) { - FOR_EACH_INSTRUCTION(DECLARE_CASE) + FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_CASE) default: os << "Unknown instruction kind " << static_cast(rhs); break; @@ -1952,6 +1981,11 @@ bool HBasicBlock::EndsWithControlFlowInstruction() const { return !GetInstructions().IsEmpty() && GetLastInstruction()->IsControlFlow(); } +bool HBasicBlock::EndsWithReturn() const { + return !GetInstructions().IsEmpty() && + (GetLastInstruction()->IsReturn() || GetLastInstruction()->IsReturnVoid()); +} + bool HBasicBlock::EndsWithIf() const { return !GetInstructions().IsEmpty() && GetLastInstruction()->IsIf(); } @@ -2765,6 +2799,14 @@ void HInstruction::SetReferenceTypeInfo(ReferenceTypeInfo rti) { SetPackedFlag(rti.IsExact()); } +bool HBoundType::InstructionDataEquals(const HInstruction* other) const { + const HBoundType* other_bt = other->AsBoundType(); + ScopedObjectAccess soa(Thread::Current()); + return GetUpperBound().IsEqual(other_bt->GetUpperBound()) && + GetUpperCanBeNull() == other_bt->GetUpperCanBeNull() && + CanBeNull() == other_bt->CanBeNull(); +} + void HBoundType::SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be_null) { if (kIsDebugBuild) { ScopedObjectAccess soa(Thread::Current()); @@ -2850,8 +2892,7 @@ void HInvoke::SetIntrinsic(Intrinsics intrinsic, } bool HNewInstance::IsStringAlloc() const { - ScopedObjectAccess soa(Thread::Current()); - return GetReferenceTypeInfo().IsStringClass(); + return GetEntrypoint() == kQuickAllocStringObject; } bool HInvoke::NeedsEnvironment() const { @@ -2891,6 +2932,8 @@ std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::MethodLoadKind return os << "BootImageLinkTimePcRelative"; case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: return os << "DirectAddress"; + case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: + return os << "BootImageRelRo"; case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: return os << "BssEntry"; case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: @@ -2925,7 +2968,7 @@ bool HLoadClass::InstructionDataEquals(const HInstruction* other) const { } switch (GetLoadKind()) { case LoadKind::kBootImageAddress: - case LoadKind::kBootImageClassTable: + case LoadKind::kBootImageRelRo: case LoadKind::kJitTableAddress: { ScopedObjectAccess soa(Thread::Current()); return GetClass().Get() == other_load_class->GetClass().Get(); @@ -2944,8 +2987,8 @@ std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs) { return os << "BootImageLinkTimePcRelative"; case HLoadClass::LoadKind::kBootImageAddress: return os << "BootImageAddress"; - case HLoadClass::LoadKind::kBootImageClassTable: - return os << "BootImageClassTable"; + case HLoadClass::LoadKind::kBootImageRelRo: + return os << "BootImageRelRo"; case HLoadClass::LoadKind::kBssEntry: return os << "BssEntry"; case HLoadClass::LoadKind::kJitTableAddress: @@ -2968,7 +3011,7 @@ bool HLoadString::InstructionDataEquals(const HInstruction* other) const { } switch (GetLoadKind()) { case LoadKind::kBootImageAddress: - case LoadKind::kBootImageInternTable: + case LoadKind::kBootImageRelRo: case LoadKind::kJitTableAddress: { ScopedObjectAccess soa(Thread::Current()); return GetString().Get() == other_load_string->GetString().Get(); @@ -2984,8 +3027,8 @@ std::ostream& operator<<(std::ostream& os, HLoadString::LoadKind rhs) { return os << "BootImageLinkTimePcRelative"; case HLoadString::LoadKind::kBootImageAddress: return os << "BootImageAddress"; - case HLoadString::LoadKind::kBootImageInternTable: - return os << "BootImageInternTable"; + case HLoadString::LoadKind::kBootImageRelRo: + return os << "BootImageRelRo"; case HLoadString::LoadKind::kBssEntry: return os << "BssEntry"; case HLoadString::LoadKind::kJitTableAddress: @@ -3101,6 +3144,8 @@ std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs) { return os << "array_object_check"; case TypeCheckKind::kArrayCheck: return os << "array_check"; + case TypeCheckKind::kBitstringCheck: + return os << "bitstring_check"; default: LOG(FATAL) << "Unknown TypeCheckKind: " << static_cast(rhs); UNREACHABLE(); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index fe992a7f3991279681b510809536a1835ff68c53..cd8d07a17a77eddb6d702a0eef6670048811d53f 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -41,6 +41,7 @@ #include "intrinsics_enum.h" #include "locations.h" #include "mirror/class.h" +#include "mirror/method_type.h" #include "offsets.h" #include "utils/intrusive_forward_list.h" @@ -1284,6 +1285,7 @@ class HBasicBlock : public ArenaObject { void SetLifetimeEnd(size_t end) { lifetime_end_ = end; } bool EndsWithControlFlowInstruction() const; + bool EndsWithReturn() const; bool EndsWithIf() const; bool EndsWithTryBoundary() const; bool HasSinglePhi() const; @@ -1338,6 +1340,7 @@ class HLoopInformationOutwardIterator : public ValueObject { #define FOR_EACH_CONCRETE_INSTRUCTION_COMMON(M) \ M(Above, Condition) \ M(AboveOrEqual, Condition) \ + M(Abs, UnaryOperation) \ M(Add, BinaryOperation) \ M(And, BinaryOperation) \ M(ArrayGet, Instruction) \ @@ -1377,13 +1380,18 @@ class HLoopInformationOutwardIterator : public ValueObject { M(InvokeStaticOrDirect, Invoke) \ M(InvokeVirtual, Invoke) \ M(InvokePolymorphic, Invoke) \ + M(InvokeCustom, Invoke) \ M(LessThan, Condition) \ M(LessThanOrEqual, Condition) \ M(LoadClass, Instruction) \ M(LoadException, Instruction) \ + M(LoadMethodHandle, Instruction) \ + M(LoadMethodType, Instruction) \ M(LoadString, Instruction) \ M(LongConstant, Constant) \ + M(Max, Instruction) \ M(MemoryBarrier, Instruction) \ + M(Min, BinaryOperation) \ M(MonitorOperation, Instruction) \ M(Mul, BinaryOperation) \ M(NativeDebugInfo, Instruction) \ @@ -1437,6 +1445,8 @@ class HLoopInformationOutwardIterator : public ValueObject { M(VecAndNot, VecBinaryOperation) \ M(VecOr, VecBinaryOperation) \ M(VecXor, VecBinaryOperation) \ + M(VecSaturationAdd, VecBinaryOperation) \ + M(VecSaturationSub, VecBinaryOperation) \ M(VecShl, VecBinaryOperation) \ M(VecShr, VecBinaryOperation) \ M(VecUShr, VecBinaryOperation) \ @@ -1520,9 +1530,6 @@ FOR_EACH_INSTRUCTION(FORWARD_DECLARATION) H##type& operator=(const H##type&) = delete; \ public: \ const char* DebugName() const OVERRIDE { return #type; } \ - bool InstructionTypeEquals(const HInstruction* other) const OVERRIDE { \ - return other->Is##type(); \ - } \ HInstruction* Clone(ArenaAllocator* arena) const OVERRIDE { \ DCHECK(IsClonable()); \ return new (arena) H##type(*this->As##type()); \ @@ -1532,10 +1539,7 @@ FOR_EACH_INSTRUCTION(FORWARD_DECLARATION) #define DECLARE_ABSTRACT_INSTRUCTION(type) \ private: \ H##type& operator=(const H##type&) = delete; \ - public: \ - bool Is##type() const { return As##type() != nullptr; } \ - const H##type* As##type() const { return this; } \ - H##type* As##type() { return this; } + public: #define DEFAULT_COPY_CONSTRUCTOR(type) \ explicit H##type(const H##type& other) = default; @@ -1906,6 +1910,11 @@ class HEnvironment : public ArenaObject { void RemoveAsUserOfInput(size_t index) const; + // Replaces the input at the position 'index' with the replacement; the replacement and old + // input instructions' env_uses_ lists are adjusted. The function works similar to + // HInstruction::ReplaceInput. + void ReplaceInput(HInstruction* replacement, size_t index); + size_t Size() const { return vregs_.size(); } HEnvironment* GetParent() const { return parent_; } @@ -1954,12 +1963,15 @@ class HInstruction : public ArenaObject { public: #define DECLARE_KIND(type, super) k##type, enum InstructionKind { - FOR_EACH_INSTRUCTION(DECLARE_KIND) + FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_KIND) kLastInstructionKind }; #undef DECLARE_KIND HInstruction(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc) + : HInstruction(kind, DataType::Type::kVoid, side_effects, dex_pc) {} + + HInstruction(InstructionKind kind, DataType::Type type, SideEffects side_effects, uint32_t dex_pc) : previous_(nullptr), next_(nullptr), block_(nullptr), @@ -1974,6 +1986,7 @@ class HInstruction : public ArenaObject { side_effects_(side_effects), reference_type_handle_(ReferenceTypeInfo::CreateInvalid().GetTypeHandle()) { SetPackedField(kind); + SetPackedField(type); SetPackedFlag(ReferenceTypeInfo::CreateInvalid().IsExact()); } @@ -2031,7 +2044,9 @@ class HInstruction : public ArenaObject { virtual void Accept(HGraphVisitor* visitor) = 0; virtual const char* DebugName() const = 0; - virtual DataType::Type GetType() const { return DataType::Type::kVoid; } + DataType::Type GetType() const { + return TypeField::Decode(GetPackedFields()); + } virtual bool NeedsEnvironment() const { return false; } @@ -2202,6 +2217,7 @@ class HInstruction : public ArenaObject { void ReplaceWith(HInstruction* instruction); void ReplaceUsesDominatedBy(HInstruction* dominator, HInstruction* replacement); + void ReplaceEnvUsesDominatedBy(HInstruction* dominator, HInstruction* replacement); void ReplaceInput(HInstruction* replacement, size_t index); // This is almost the same as doing `ReplaceWith()`. But in this helper, the @@ -2223,19 +2239,17 @@ class HInstruction : public ArenaObject { void MoveBeforeFirstUserAndOutOfLoops(); #define INSTRUCTION_TYPE_CHECK(type, super) \ - bool Is##type() const; \ - const H##type* As##type() const; \ - H##type* As##type(); + bool Is##type() const; - FOR_EACH_CONCRETE_INSTRUCTION(INSTRUCTION_TYPE_CHECK) + FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CHECK) #undef INSTRUCTION_TYPE_CHECK -#define INSTRUCTION_TYPE_CHECK(type, super) \ - bool Is##type() const { return (As##type() != nullptr); } \ - virtual const H##type* As##type() const { return nullptr; } \ - virtual H##type* As##type() { return nullptr; } - FOR_EACH_ABSTRACT_INSTRUCTION(INSTRUCTION_TYPE_CHECK) -#undef INSTRUCTION_TYPE_CHECK +#define INSTRUCTION_TYPE_CAST(type, super) \ + const H##type* As##type() const; \ + H##type* As##type(); + + FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CAST) +#undef INSTRUCTION_TYPE_CAST // Return a clone of the instruction if it is clonable (shallow copy by default, custom copy // if a custom copy-constructor is provided for a particular type). If IsClonable() is false for @@ -2261,11 +2275,6 @@ class HInstruction : public ArenaObject { // meanings? split and rename? virtual bool CanBeMoved() const { return false; } - // Returns whether the two instructions are of the same kind. - virtual bool InstructionTypeEquals(const HInstruction* other ATTRIBUTE_UNUSED) const { - return false; - } - // Returns whether any data encoded in the two instructions is equal. // This method does not look at the inputs. Both instructions must be // of the same type, otherwise the method has undefined behavior. @@ -2337,13 +2346,18 @@ class HInstruction : public ArenaObject { static constexpr size_t kFieldInstructionKind = kFlagReferenceTypeIsExact + 1; static constexpr size_t kFieldInstructionKindSize = MinimumBitsToStore(static_cast(InstructionKind::kLastInstructionKind - 1)); - static constexpr size_t kNumberOfGenericPackedBits = + static constexpr size_t kFieldType = kFieldInstructionKind + kFieldInstructionKindSize; + static constexpr size_t kFieldTypeSize = + MinimumBitsToStore(static_cast(DataType::Type::kLast)); + static constexpr size_t kNumberOfGenericPackedBits = kFieldType + kFieldTypeSize; static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte; static_assert(kNumberOfGenericPackedBits <= kMaxNumberOfPackedBits, "Too many generic packed fields"); + using TypeField = BitField; + const HUserRecord InputRecordAt(size_t i) const { return GetInputRecords()[i]; } @@ -2590,6 +2604,15 @@ class HVariableInputSizeInstruction : public HInstruction { ArenaAllocKind kind) : HInstruction(inst_kind, side_effects, dex_pc), inputs_(number_of_inputs, allocator->Adapter(kind)) {} + HVariableInputSizeInstruction(InstructionKind inst_kind, + DataType::Type type, + SideEffects side_effects, + uint32_t dex_pc, + ArenaAllocator* allocator, + size_t number_of_inputs, + ArenaAllocKind kind) + : HInstruction(inst_kind, type, side_effects, dex_pc), + inputs_(number_of_inputs, allocator->Adapter(kind)) {} DEFAULT_COPY_CONSTRUCTOR(VariableInputSizeInstruction); @@ -2597,11 +2620,16 @@ class HVariableInputSizeInstruction : public HInstruction { }; template -class HTemplateInstruction: public HInstruction { +class HExpression : public HInstruction { public: - HTemplateInstruction(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc) + HExpression(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc) : HInstruction(kind, side_effects, dex_pc), inputs_() {} - virtual ~HTemplateInstruction() {} + HExpression(InstructionKind kind, + DataType::Type type, + SideEffects side_effects, + uint32_t dex_pc) + : HInstruction(kind, type, side_effects, dex_pc), inputs_() {} + virtual ~HExpression() {} using HInstruction::GetInputRecords; // Keep the const version visible. ArrayRef> GetInputRecords() OVERRIDE FINAL { @@ -2609,7 +2637,7 @@ class HTemplateInstruction: public HInstruction { } protected: - DEFAULT_COPY_CONSTRUCTOR(TemplateInstruction); + DEFAULT_COPY_CONSTRUCTOR(Expression); private: std::array, N> inputs_; @@ -2617,14 +2645,13 @@ class HTemplateInstruction: public HInstruction { friend class SsaBuilder; }; -// HTemplateInstruction specialization for N=0. +// HExpression specialization for N=0. template<> -class HTemplateInstruction<0>: public HInstruction { +class HExpression<0> : public HInstruction { public: - explicit HTemplateInstruction<0>(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc) - : HInstruction(kind, side_effects, dex_pc) {} + using HInstruction::HInstruction; - virtual ~HTemplateInstruction() {} + virtual ~HExpression() {} using HInstruction::GetInputRecords; // Keep the const version visible. ArrayRef> GetInputRecords() OVERRIDE FINAL { @@ -2632,46 +2659,18 @@ class HTemplateInstruction<0>: public HInstruction { } protected: - DEFAULT_COPY_CONSTRUCTOR(TemplateInstruction<0>); + DEFAULT_COPY_CONSTRUCTOR(Expression<0>); private: friend class SsaBuilder; }; -template -class HExpression : public HTemplateInstruction { - public: - using HInstruction::InstructionKind; - HExpression(InstructionKind kind, - DataType::Type type, - SideEffects side_effects, - uint32_t dex_pc) - : HTemplateInstruction(kind, side_effects, dex_pc) { - this->template SetPackedField(type); - } - virtual ~HExpression() {} - - DataType::Type GetType() const OVERRIDE { - return TypeField::Decode(this->GetPackedFields()); - } - - protected: - static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits; - static constexpr size_t kFieldTypeSize = - MinimumBitsToStore(static_cast(DataType::Type::kLast)); - static constexpr size_t kNumberOfExpressionPackedBits = kFieldType + kFieldTypeSize; - static_assert(kNumberOfExpressionPackedBits <= HInstruction::kMaxNumberOfPackedBits, - "Too many packed fields."); - using TypeField = BitField; - DEFAULT_COPY_CONSTRUCTOR(Expression); -}; - // Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow // instruction that branches to the exit block. -class HReturnVoid FINAL : public HTemplateInstruction<0> { +class HReturnVoid FINAL : public HExpression<0> { public: explicit HReturnVoid(uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kReturnVoid, SideEffects::None(), dex_pc) { + : HExpression(kReturnVoid, SideEffects::None(), dex_pc) { } bool IsControlFlow() const OVERRIDE { return true; } @@ -2684,10 +2683,10 @@ class HReturnVoid FINAL : public HTemplateInstruction<0> { // Represents dex's RETURN opcodes. A HReturn is a control flow // instruction that branches to the exit block. -class HReturn FINAL : public HTemplateInstruction<1> { +class HReturn FINAL : public HExpression<1> { public: explicit HReturn(HInstruction* value, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kReturn, SideEffects::None(), dex_pc) { + : HExpression(kReturn, SideEffects::None(), dex_pc) { SetRawInputAt(0, value); } @@ -2708,13 +2707,13 @@ class HPhi FINAL : public HVariableInputSizeInstruction { uint32_t dex_pc = kNoDexPc) : HVariableInputSizeInstruction( kPhi, + ToPhiType(type), SideEffects::None(), dex_pc, allocator, number_of_inputs, kArenaAllocPhiInputs), reg_number_(reg_number) { - SetPackedField(ToPhiType(type)); DCHECK_NE(GetType(), DataType::Type::kVoid); // Phis are constructed live and marked dead if conflicting or unused. // Individual steps of SsaBuilder should assume that if a phi has been @@ -2732,7 +2731,6 @@ class HPhi FINAL : public HVariableInputSizeInstruction { bool IsCatchPhi() const { return GetBlock()->IsCatchBlock(); } - DataType::Type GetType() const OVERRIDE { return GetPackedField(); } void SetType(DataType::Type new_type) { // Make sure that only valid type changes occur. The following are allowed: // (1) int -> float/ref (primitive type propagation), @@ -2791,14 +2789,10 @@ class HPhi FINAL : public HVariableInputSizeInstruction { DEFAULT_COPY_CONSTRUCTOR(Phi); private: - static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits; - static constexpr size_t kFieldTypeSize = - MinimumBitsToStore(static_cast(DataType::Type::kLast)); - static constexpr size_t kFlagIsLive = kFieldType + kFieldTypeSize; + static constexpr size_t kFlagIsLive = HInstruction::kNumberOfGenericPackedBits; static constexpr size_t kFlagCanBeNull = kFlagIsLive + 1; static constexpr size_t kNumberOfPhiPackedBits = kFlagCanBeNull + 1; static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); - using TypeField = BitField; const uint32_t reg_number_; }; @@ -2806,10 +2800,10 @@ class HPhi FINAL : public HVariableInputSizeInstruction { // The exit instruction is the only instruction of the exit block. // Instructions aborting the method (HThrow and HReturn) must branch to the // exit block. -class HExit FINAL : public HTemplateInstruction<0> { +class HExit FINAL : public HExpression<0> { public: explicit HExit(uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kExit, SideEffects::None(), dex_pc) { + : HExpression(kExit, SideEffects::None(), dex_pc) { } bool IsControlFlow() const OVERRIDE { return true; } @@ -2821,10 +2815,10 @@ class HExit FINAL : public HTemplateInstruction<0> { }; // Jumps from one block to another. -class HGoto FINAL : public HTemplateInstruction<0> { +class HGoto FINAL : public HExpression<0> { public: explicit HGoto(uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kGoto, SideEffects::None(), dex_pc) { + : HExpression(kGoto, SideEffects::None(), dex_pc) { } bool IsClonable() const OVERRIDE { return true; } @@ -3091,10 +3085,10 @@ class HDoubleConstant FINAL : public HConstant { // Conditional branch. A block ending with an HIf instruction must have // two successors. -class HIf FINAL : public HTemplateInstruction<1> { +class HIf FINAL : public HExpression<1> { public: explicit HIf(HInstruction* input, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kIf, SideEffects::None(), dex_pc) { + : HExpression(kIf, SideEffects::None(), dex_pc) { SetRawInputAt(0, input); } @@ -3121,7 +3115,7 @@ class HIf FINAL : public HTemplateInstruction<1> { // non-exceptional control flow. // Normal-flow successor is stored at index zero, exception handlers under // higher indices in no particular order. -class HTryBoundary FINAL : public HTemplateInstruction<0> { +class HTryBoundary FINAL : public HExpression<0> { public: enum class BoundaryKind { kEntry, @@ -3130,7 +3124,7 @@ class HTryBoundary FINAL : public HTemplateInstruction<0> { }; explicit HTryBoundary(BoundaryKind kind, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kTryBoundary, SideEffects::None(), dex_pc) { + : HExpression(kTryBoundary, SideEffects::None(), dex_pc) { SetPackedField(kind); } @@ -3214,6 +3208,7 @@ class HDeoptimize FINAL : public HVariableInputSizeInstruction { uint32_t dex_pc) : HVariableInputSizeInstruction( kDeoptimize, + guard->GetType(), SideEffects::CanTriggerGC(), dex_pc, allocator, @@ -3237,10 +3232,6 @@ class HDeoptimize FINAL : public HVariableInputSizeInstruction { DeoptimizationKind GetDeoptimizationKind() const { return GetPackedField(); } - DataType::Type GetType() const OVERRIDE { - return GuardsAnInput() ? GuardedInput()->GetType() : DataType::Type::kVoid; - } - bool GuardsAnInput() const { return InputCount() == 2; } @@ -3283,6 +3274,7 @@ class HShouldDeoptimizeFlag FINAL : public HVariableInputSizeInstruction { // with regard to other passes. HShouldDeoptimizeFlag(ArenaAllocator* allocator, uint32_t dex_pc) : HVariableInputSizeInstruction(kShouldDeoptimizeFlag, + DataType::Type::kInt32, SideEffects::None(), dex_pc, allocator, @@ -3290,8 +3282,6 @@ class HShouldDeoptimizeFlag FINAL : public HVariableInputSizeInstruction { kArenaAllocCHA) { } - DataType::Type GetType() const OVERRIDE { return DataType::Type::kInt32; } - // We do all CHA guard elimination/motion in a single pass, after which there is no // further guard elimination/motion since a guard might have been used for justification // of the elimination of another guard. Therefore, we pretend this guard cannot be moved @@ -3355,7 +3345,7 @@ class HClassTableGet FINAL : public HExpression<1> { DEFAULT_COPY_CONSTRUCTOR(ClassTableGet); private: - static constexpr size_t kFieldTableKind = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldTableKind = kNumberOfGenericPackedBits; static constexpr size_t kFieldTableKindSize = MinimumBitsToStore(static_cast(TableKind::kLast)); static constexpr size_t kNumberOfClassTableGetPackedBits = kFieldTableKind + kFieldTableKindSize; @@ -3370,13 +3360,13 @@ class HClassTableGet FINAL : public HExpression<1> { // PackedSwitch (jump table). A block ending with a PackedSwitch instruction will // have one successor for each entry in the switch table, and the final successor // will be the block containing the next Dex opcode. -class HPackedSwitch FINAL : public HTemplateInstruction<1> { +class HPackedSwitch FINAL : public HExpression<1> { public: HPackedSwitch(int32_t start_value, uint32_t num_entries, HInstruction* input, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kPackedSwitch, SideEffects::None(), dex_pc), + : HExpression(kPackedSwitch, SideEffects::None(), dex_pc), start_value_(start_value), num_entries_(num_entries) { SetRawInputAt(0, input); @@ -3606,7 +3596,7 @@ class HCondition : public HBinaryOperation { protected: // Needed if we merge a HCompare into a HCondition. - static constexpr size_t kFieldComparisonBias = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldComparisonBias = kNumberOfGenericPackedBits; static constexpr size_t kFieldComparisonBiasSize = MinimumBitsToStore(static_cast(ComparisonBias::kLast)); static constexpr size_t kNumberOfConditionPackedBits = @@ -4126,7 +4116,7 @@ class HCompare FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Compare); protected: - static constexpr size_t kFieldComparisonBias = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldComparisonBias = kNumberOfGenericPackedBits; static constexpr size_t kFieldComparisonBiasSize = MinimumBitsToStore(static_cast(ComparisonBias::kLast)); static constexpr size_t kNumberOfComparePackedBits = @@ -4205,7 +4195,7 @@ class HNewInstance FINAL : public HExpression<1> { DEFAULT_COPY_CONSTRUCTOR(NewInstance); private: - static constexpr size_t kFlagFinalizable = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagFinalizable = kNumberOfGenericPackedBits; static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1; static_assert(kNumberOfNewInstancePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); @@ -4246,8 +4236,6 @@ class HInvoke : public HVariableInputSizeInstruction { // inputs at the end of their list of inputs. uint32_t GetNumberOfArguments() const { return number_of_arguments_; } - DataType::Type GetType() const OVERRIDE { return GetPackedField(); } - uint32_t GetDexMethodIndex() const { return dex_method_index_; } InvokeType GetInvokeType() const { @@ -4300,16 +4288,11 @@ class HInvoke : public HVariableInputSizeInstruction { static constexpr size_t kFieldInvokeType = kNumberOfGenericPackedBits; static constexpr size_t kFieldInvokeTypeSize = MinimumBitsToStore(static_cast(kMaxInvokeType)); - static constexpr size_t kFieldReturnType = - kFieldInvokeType + kFieldInvokeTypeSize; - static constexpr size_t kFieldReturnTypeSize = - MinimumBitsToStore(static_cast(DataType::Type::kLast)); - static constexpr size_t kFlagCanThrow = kFieldReturnType + kFieldReturnTypeSize; + static constexpr size_t kFlagCanThrow = kFieldInvokeType + kFieldInvokeTypeSize; static constexpr size_t kFlagAlwaysThrows = kFlagCanThrow + 1; static constexpr size_t kNumberOfInvokePackedBits = kFlagAlwaysThrows + 1; static_assert(kNumberOfInvokePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); using InvokeTypeField = BitField; - using ReturnTypeField = BitField; HInvoke(InstructionKind kind, ArenaAllocator* allocator, @@ -4322,6 +4305,7 @@ class HInvoke : public HVariableInputSizeInstruction { InvokeType invoke_type) : HVariableInputSizeInstruction( kind, + return_type, SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays. dex_pc, allocator, @@ -4332,7 +4316,6 @@ class HInvoke : public HVariableInputSizeInstruction { dex_method_index_(dex_method_index), intrinsic_(Intrinsics::kNone), intrinsic_optimizations_(0) { - SetPackedField(return_type); SetPackedField(invoke_type); SetPackedFlag(true); } @@ -4401,6 +4384,38 @@ class HInvokePolymorphic FINAL : public HInvoke { 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 @@ -4428,6 +4443,10 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { // Used for app->boot calls with non-relocatable image and for JIT-compiled calls. kDirectAddress, + // Load from an entry in the .data.bimg.rel.ro using a PC-relative load. + // Used for app->boot calls with relocatable image. + kBootImageRelRo, + // Load from an entry in the .bss section using a PC-relative load. // Used for classes outside boot image when .bss is accessible with a PC-relative load. kBssEntry, @@ -4541,7 +4560,7 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { } bool CanBeNull() const OVERRIDE { - return GetPackedField() == DataType::Type::kReference && !IsStringInit(); + return GetType() == DataType::Type::kReference && !IsStringInit(); } // Get the index of the special input, if any. @@ -4560,6 +4579,7 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { bool HasMethodAddress() const { return GetMethodLoadKind() == MethodLoadKind::kDirectAddress; } bool HasPcRelativeMethodLoadKind() const { return GetMethodLoadKind() == MethodLoadKind::kBootImageLinkTimePcRelative || + GetMethodLoadKind() == MethodLoadKind::kBootImageRelRo || GetMethodLoadKind() == MethodLoadKind::kBssEntry; } bool HasCurrentMethodInput() const { @@ -5016,6 +5036,117 @@ class HRem FINAL : public HBinaryOperation { DEFAULT_COPY_CONSTRUCTOR(Rem); }; +class HMin FINAL : public HBinaryOperation { + public: + HMin(DataType::Type result_type, + HInstruction* left, + HInstruction* right, + uint32_t dex_pc) + : HBinaryOperation(kMin, result_type, left, right, SideEffects::None(), dex_pc) {} + + bool IsCommutative() const OVERRIDE { return true; } + + // Evaluation for integral values. + template static T ComputeIntegral(T x, T y) { + return (x <= y) ? x : y; + } + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant( + ComputeIntegral(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant( + ComputeIntegral(x->GetValue(), y->GetValue()), GetDexPc()); + } + // TODO: Evaluation for floating-point values. + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, + HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { return nullptr; } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, + HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { return nullptr; } + + DECLARE_INSTRUCTION(Min); + + protected: + DEFAULT_COPY_CONSTRUCTOR(Min); +}; + +class HMax FINAL : public HBinaryOperation { + public: + HMax(DataType::Type result_type, + HInstruction* left, + HInstruction* right, + uint32_t dex_pc) + : HBinaryOperation(kMax, result_type, left, right, SideEffects::None(), dex_pc) {} + + bool IsCommutative() const OVERRIDE { return true; } + + // Evaluation for integral values. + template static T ComputeIntegral(T x, T y) { + return (x >= y) ? x : y; + } + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant( + ComputeIntegral(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant( + ComputeIntegral(x->GetValue(), y->GetValue()), GetDexPc()); + } + // TODO: Evaluation for floating-point values. + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, + HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { return nullptr; } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, + HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { return nullptr; } + + DECLARE_INSTRUCTION(Max); + + protected: + DEFAULT_COPY_CONSTRUCTOR(Max); +}; + +class HAbs FINAL : public HUnaryOperation { + public: + HAbs(DataType::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc) + : HUnaryOperation(kAbs, result_type, input, dex_pc) {} + + // Evaluation for integral values. + template static T ComputeIntegral(T x) { + return x < 0 ? -x : x; + } + + // Evaluation for floating-point values. + // Note, as a "quality of implementation", rather than pure "spec compliance", + // we require that Math.abs() clears the sign bit (but changes nothing else) + // for all floating-point numbers, including NaN (signaling NaN may become quiet though). + // http://b/30758343 + template static T ComputeFP(T x) { + S bits = bit_cast(x); + return bit_cast(bits & std::numeric_limits::max()); + } + + HConstant* Evaluate(HIntConstant* x) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(ComputeIntegral(x->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HLongConstant* x) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant(ComputeIntegral(x->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HFloatConstant* x) const OVERRIDE { + return GetBlock()->GetGraph()->GetFloatConstant( + ComputeFP(x->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HDoubleConstant* x) const OVERRIDE { + return GetBlock()->GetGraph()->GetDoubleConstant( + ComputeFP(x->GetValue()), GetDexPc()); + } + + DECLARE_INSTRUCTION(Abs); + + protected: + DEFAULT_COPY_CONSTRUCTOR(Abs); +}; + class HDivZeroCheck FINAL : public HExpression<1> { public: // `HDivZeroCheck` can trigger GC, as it may call the `ArithmeticException` @@ -5025,8 +5156,7 @@ class HDivZeroCheck FINAL : public HExpression<1> { SetRawInputAt(0, value); } - DataType::Type GetType() const OVERRIDE { return InputAt(0)->GetType(); } - + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { @@ -5379,7 +5509,7 @@ class HParameterValue FINAL : public HExpression<0> { private: // Whether or not the parameter value corresponds to 'this' argument. - static constexpr size_t kFlagIsThis = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagIsThis = kNumberOfGenericPackedBits; static constexpr size_t kFlagCanBeNull = kFlagIsThis + 1; static constexpr size_t kNumberOfParameterValuePackedBits = kFlagCanBeNull + 1; static_assert(kNumberOfParameterValuePackedBits <= kMaxNumberOfPackedBits, @@ -5478,6 +5608,7 @@ class HTypeConversion FINAL : public HExpression<1> { DataType::Type GetInputType() const { return GetInput()->GetType(); } DataType::Type GetResultType() const { return GetType(); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; @@ -5621,7 +5752,7 @@ class HInstanceFieldGet FINAL : public HExpression<1> { const FieldInfo field_info_; }; -class HInstanceFieldSet FINAL : public HTemplateInstruction<2> { +class HInstanceFieldSet FINAL : public HExpression<2> { public: HInstanceFieldSet(HInstruction* object, HInstruction* value, @@ -5633,9 +5764,9 @@ class HInstanceFieldSet FINAL : public HTemplateInstruction<2> { uint16_t declaring_class_def_index, const DexFile& dex_file, uint32_t dex_pc) - : HTemplateInstruction(kInstanceFieldSet, - SideEffects::FieldWriteOfType(field_type, is_volatile), - dex_pc), + : HExpression(kInstanceFieldSet, + SideEffects::FieldWriteOfType(field_type, is_volatile), + dex_pc), field_info_(field, field_offset, field_type, @@ -5761,13 +5892,13 @@ class HArrayGet FINAL : public HExpression<2> { // a particular HArrayGet is actually a String.charAt() by looking at the type // of the input but that requires holding the mutator lock, so we prefer to use // a flag, so that code generators don't need to do the locking. - static constexpr size_t kFlagIsStringCharAt = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagIsStringCharAt = kNumberOfGenericPackedBits; static constexpr size_t kNumberOfArrayGetPackedBits = kFlagIsStringCharAt + 1; static_assert(kNumberOfArrayGetPackedBits <= HInstruction::kMaxNumberOfPackedBits, "Too many packed fields."); }; -class HArraySet FINAL : public HTemplateInstruction<3> { +class HArraySet FINAL : public HExpression<3> { public: HArraySet(HInstruction* array, HInstruction* index, @@ -5789,7 +5920,7 @@ class HArraySet FINAL : public HTemplateInstruction<3> { DataType::Type expected_component_type, SideEffects side_effects, uint32_t dex_pc) - : HTemplateInstruction(kArraySet, side_effects, dex_pc) { + : HExpression(kArraySet, side_effects, dex_pc) { SetPackedField(expected_component_type); SetPackedFlag(value->GetType() == DataType::Type::kReference); SetPackedFlag(true); @@ -5918,7 +6049,7 @@ class HArrayLength FINAL : public HExpression<1> { // determine whether a particular HArrayLength is actually a String.length() by // looking at the type of the input but that requires holding the mutator lock, so // we prefer to use a flag, so that code generators don't need to do the locking. - static constexpr size_t kFlagIsStringLength = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagIsStringLength = kNumberOfGenericPackedBits; static constexpr size_t kNumberOfArrayLengthPackedBits = kFlagIsStringLength + 1; static_assert(kNumberOfArrayLengthPackedBits <= HInstruction::kMaxNumberOfPackedBits, "Too many packed fields."); @@ -5959,13 +6090,13 @@ class HBoundsCheck FINAL : public HExpression<2> { DEFAULT_COPY_CONSTRUCTOR(BoundsCheck); private: - static constexpr size_t kFlagIsStringCharAt = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagIsStringCharAt = kNumberOfGenericPackedBits; }; -class HSuspendCheck FINAL : public HTemplateInstruction<0> { +class HSuspendCheck FINAL : public HExpression<0> { public: explicit HSuspendCheck(uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kSuspendCheck, SideEffects::CanTriggerGC(), dex_pc), + : HExpression(kSuspendCheck, SideEffects::CanTriggerGC(), dex_pc), slow_path_(nullptr) { } @@ -5991,10 +6122,10 @@ class HSuspendCheck FINAL : public HTemplateInstruction<0> { // Pseudo-instruction which provides the native debugger with mapping information. // It ensures that we can generate line number and local variables at this point. -class HNativeDebugInfo : public HTemplateInstruction<0> { +class HNativeDebugInfo : public HExpression<0> { public: explicit HNativeDebugInfo(uint32_t dex_pc) - : HTemplateInstruction<0>(kNativeDebugInfo, SideEffects::None(), dex_pc) { + : HExpression<0>(kNativeDebugInfo, SideEffects::None(), dex_pc) { } bool NeedsEnvironment() const OVERRIDE { @@ -6025,12 +6156,12 @@ class HLoadClass FINAL : public HInstruction { kBootImageLinkTimePcRelative, // Use a known boot image Class* address, embedded in the code by the codegen. - // Used for boot image classes referenced by apps in AOT- and JIT-compiled code. + // Used for boot image classes referenced by apps in JIT- and AOT-compiled code (non-PIC). kBootImageAddress, - // Use a PC-relative load from a boot image ClassTable mmapped into the .bss - // of the oat file. - kBootImageClassTable, + // Load from an entry in the .data.bimg.rel.ro using a PC-relative load. + // Used for boot image classes referenced by apps in AOT-compiled code (PIC). + kBootImageRelRo, // Load from an entry in the .bss section using a PC-relative load. // Used for classes outside boot image when .bss is accessible with a PC-relative load. @@ -6053,12 +6184,14 @@ class HLoadClass FINAL : public HInstruction { bool is_referrers_class, uint32_t dex_pc, bool needs_access_check) - : HInstruction(kLoadClass, SideEffectsForArchRuntimeCalls(), dex_pc), + : HInstruction(kLoadClass, + DataType::Type::kReference, + SideEffectsForArchRuntimeCalls(), + dex_pc), special_input_(HUserRecord(current_method)), type_index_(type_index), dex_file_(dex_file), - klass_(klass), - loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) { + klass_(klass) { // Referrers class should not need access check. We never inline unverified // methods so we can't possibly end up in this situation. DCHECK(!is_referrers_class || !needs_access_check); @@ -6068,6 +6201,7 @@ class HLoadClass FINAL : public HInstruction { SetPackedFlag(needs_access_check); SetPackedFlag(false); SetPackedFlag(false); + SetPackedFlag(false); } bool IsClonable() const OVERRIDE { return true; } @@ -6078,6 +6212,12 @@ class HLoadClass FINAL : public HInstruction { return GetPackedField(); } + bool HasPcRelativeLoadKind() const { + return GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative || + GetLoadKind() == LoadKind::kBootImageRelRo || + GetLoadKind() == LoadKind::kBssEntry; + } + bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other) const; @@ -6116,13 +6256,18 @@ class HLoadClass FINAL : public HInstruction { } ReferenceTypeInfo GetLoadedClassRTI() { - return loaded_class_rti_; + if (GetPackedFlag()) { + // Note: The is_exact flag from the return value should not be used. + return ReferenceTypeInfo::CreateUnchecked(klass_, /* is_exact */ true); + } else { + return ReferenceTypeInfo::CreateInvalid(); + } } - void SetLoadedClassRTI(ReferenceTypeInfo rti) { - // Make sure we only set exact types (the loaded class should never be merged). - DCHECK(rti.IsExact()); - loaded_class_rti_ = rti; + // Loaded class RTI is marked as valid by RTP if the klass_ is admissible. + void SetValidLoadedClassRTI() REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(klass_ != nullptr); + SetPackedFlag(true); } dex::TypeIndex GetTypeIndex() const { return type_index_; } @@ -6153,10 +6298,6 @@ class HLoadClass FINAL : public HInstruction { &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u); } - DataType::Type GetType() const OVERRIDE { - return DataType::Type::kReference; - } - Handle GetClass() const { return klass_; } @@ -6175,14 +6316,14 @@ class HLoadClass FINAL : public HInstruction { static constexpr size_t kFieldLoadKind = kFlagGenerateClInitCheck + 1; static constexpr size_t kFieldLoadKindSize = MinimumBitsToStore(static_cast(LoadKind::kLast)); - static constexpr size_t kNumberOfLoadClassPackedBits = kFieldLoadKind + kFieldLoadKindSize; + static constexpr size_t kFlagValidLoadedClassRTI = kFieldLoadKind + kFieldLoadKindSize; + static constexpr size_t kNumberOfLoadClassPackedBits = kFlagValidLoadedClassRTI + 1; static_assert(kNumberOfLoadClassPackedBits < kMaxNumberOfPackedBits, "Too many packed fields."); using LoadKindField = BitField; static bool HasTypeReference(LoadKind load_kind) { return load_kind == LoadKind::kReferrersClass || load_kind == LoadKind::kBootImageLinkTimePcRelative || - load_kind == LoadKind::kBootImageClassTable || load_kind == LoadKind::kBssEntry || load_kind == LoadKind::kRuntimeCall; } @@ -6203,8 +6344,6 @@ class HLoadClass FINAL : public HInstruction { const DexFile& dex_file_; Handle klass_; - - ReferenceTypeInfo loaded_class_rti_; }; std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs); @@ -6228,7 +6367,7 @@ inline void HLoadClass::AddSpecialInput(HInstruction* special_input) { // including literal pool loads, which are PC-relative too. DCHECK(GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative || GetLoadKind() == LoadKind::kBootImageAddress || - GetLoadKind() == LoadKind::kBootImageClassTable || + GetLoadKind() == LoadKind::kBootImageRelRo || GetLoadKind() == LoadKind::kBssEntry) << GetLoadKind(); DCHECK(special_input_.GetInstruction() == nullptr); special_input_ = HUserRecord(special_input); @@ -6244,12 +6383,12 @@ class HLoadString FINAL : public HInstruction { kBootImageLinkTimePcRelative, // Use a known boot image String* address, embedded in the code by the codegen. - // Used for boot image strings referenced by apps in AOT- and JIT-compiled code. + // Used for boot image strings referenced by apps in JIT- and AOT-compiled code (non-PIC). kBootImageAddress, - // Use a PC-relative load from a boot image InternTable mmapped into the .bss - // of the oat file. - kBootImageInternTable, + // Load from an entry in the .data.bimg.rel.ro using a PC-relative load. + // Used for boot image strings referenced by apps in AOT-compiled code (PIC). + kBootImageRelRo, // Load from an entry in the .bss section using a PC-relative load. // Used for strings outside boot image when .bss is accessible with a PC-relative load. @@ -6269,7 +6408,10 @@ class HLoadString FINAL : public HInstruction { dex::StringIndex string_index, const DexFile& dex_file, uint32_t dex_pc) - : HInstruction(kLoadString, SideEffectsForArchRuntimeCalls(), dex_pc), + : HInstruction(kLoadString, + DataType::Type::kReference, + SideEffectsForArchRuntimeCalls(), + dex_pc), special_input_(HUserRecord(current_method)), string_index_(string_index), dex_file_(dex_file) { @@ -6284,6 +6426,12 @@ class HLoadString FINAL : public HInstruction { return GetPackedField(); } + bool HasPcRelativeLoadKind() const { + return GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative || + GetLoadKind() == LoadKind::kBootImageRelRo || + GetLoadKind() == LoadKind::kBssEntry; + } + const DexFile& GetDexFile() const { return dex_file_; } @@ -6312,7 +6460,7 @@ class HLoadString FINAL : public HInstruction { LoadKind load_kind = GetLoadKind(); if (load_kind == LoadKind::kBootImageLinkTimePcRelative || load_kind == LoadKind::kBootImageAddress || - load_kind == LoadKind::kBootImageInternTable || + load_kind == LoadKind::kBootImageRelRo || load_kind == LoadKind::kJitTableAddress) { return false; } @@ -6338,10 +6486,6 @@ class HLoadString FINAL : public HInstruction { &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u); } - DataType::Type GetType() const OVERRIDE { - return DataType::Type::kReference; - } - DECLARE_INSTRUCTION(LoadString); protected: @@ -6390,7 +6534,7 @@ inline void HLoadString::AddSpecialInput(HInstruction* special_input) { // including literal pool loads, which are PC-relative too. DCHECK(GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative || GetLoadKind() == LoadKind::kBootImageAddress || - GetLoadKind() == LoadKind::kBootImageInternTable || + GetLoadKind() == LoadKind::kBootImageRelRo || GetLoadKind() == LoadKind::kBssEntry) << GetLoadKind(); // HLoadString::GetInputRecords() returns an empty array at this point, // so use the GetInputRecords() from the base class to set the input record. @@ -6399,6 +6543,94 @@ inline void HLoadString::AddSpecialInput(HInstruction* special_input) { special_input->AddUseAt(this, 0); } +class HLoadMethodHandle FINAL : public HInstruction { + public: + HLoadMethodHandle(HCurrentMethod* current_method, + uint16_t method_handle_idx, + const DexFile& dex_file, + uint32_t dex_pc) + : HInstruction(kLoadMethodHandle, + DataType::Type::kReference, + SideEffectsForArchRuntimeCalls(), + dex_pc), + special_input_(HUserRecord(current_method)), + method_handle_idx_(method_handle_idx), + dex_file_(dex_file) { + } + + using HInstruction::GetInputRecords; // Keep the const version visible. + ArrayRef> GetInputRecords() OVERRIDE FINAL { + return ArrayRef>( + &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u); + } + + bool IsClonable() const OVERRIDE { return true; } + + uint16_t GetMethodHandleIndex() const { return method_handle_idx_; } + + const DexFile& GetDexFile() const { return dex_file_; } + + static SideEffects SideEffectsForArchRuntimeCalls() { + return SideEffects::CanTriggerGC(); + } + + DECLARE_INSTRUCTION(LoadMethodHandle); + + protected: + DEFAULT_COPY_CONSTRUCTOR(LoadMethodHandle); + + private: + // The special input is the HCurrentMethod for kRuntimeCall. + HUserRecord special_input_; + + const uint16_t method_handle_idx_; + const DexFile& dex_file_; +}; + +class HLoadMethodType FINAL : public HInstruction { + public: + HLoadMethodType(HCurrentMethod* current_method, + dex::ProtoIndex proto_index, + const DexFile& dex_file, + uint32_t dex_pc) + : HInstruction(kLoadMethodType, + DataType::Type::kReference, + SideEffectsForArchRuntimeCalls(), + dex_pc), + special_input_(HUserRecord(current_method)), + proto_index_(proto_index), + dex_file_(dex_file) { + } + + using HInstruction::GetInputRecords; // Keep the const version visible. + ArrayRef> GetInputRecords() OVERRIDE FINAL { + return ArrayRef>( + &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u); + } + + bool IsClonable() const OVERRIDE { return true; } + + dex::ProtoIndex GetProtoIndex() const { return proto_index_; } + + const DexFile& GetDexFile() const { return dex_file_; } + + static SideEffects SideEffectsForArchRuntimeCalls() { + return SideEffects::CanTriggerGC(); + } + + DECLARE_INSTRUCTION(LoadMethodType); + + protected: + DEFAULT_COPY_CONSTRUCTOR(LoadMethodType); + + private: + // The special input is the HCurrentMethod for kRuntimeCall. + HUserRecord special_input_; + + const dex::ProtoIndex proto_index_; + const DexFile& dex_file_; +}; + /** * Performs an initialization check on its Class object input. */ @@ -6412,8 +6644,7 @@ class HClinitCheck FINAL : public HExpression<1> { dex_pc) { SetRawInputAt(0, constant); } - - bool IsClonable() const OVERRIDE { return true; } + // TODO: Make ClinitCheck clonable. bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; @@ -6497,7 +6728,7 @@ class HStaticFieldGet FINAL : public HExpression<1> { const FieldInfo field_info_; }; -class HStaticFieldSet FINAL : public HTemplateInstruction<2> { +class HStaticFieldSet FINAL : public HExpression<2> { public: HStaticFieldSet(HInstruction* cls, HInstruction* value, @@ -6509,9 +6740,9 @@ class HStaticFieldSet FINAL : public HTemplateInstruction<2> { uint16_t declaring_class_def_index, const DexFile& dex_file, uint32_t dex_pc) - : HTemplateInstruction(kStaticFieldSet, - SideEffects::FieldWriteOfType(field_type, is_volatile), - dex_pc), + : HExpression(kStaticFieldSet, + SideEffects::FieldWriteOfType(field_type, is_volatile), + dex_pc), field_info_(field, field_offset, field_type, @@ -6578,16 +6809,14 @@ class HUnresolvedInstanceFieldGet FINAL : public HExpression<1> { const uint32_t field_index_; }; -class HUnresolvedInstanceFieldSet FINAL : public HTemplateInstruction<2> { +class HUnresolvedInstanceFieldSet FINAL : public HExpression<2> { public: HUnresolvedInstanceFieldSet(HInstruction* obj, HInstruction* value, DataType::Type field_type, uint32_t field_index, uint32_t dex_pc) - : HTemplateInstruction(kUnresolvedInstanceFieldSet, - SideEffects::AllExceptGCDependency(), - dex_pc), + : HExpression(kUnresolvedInstanceFieldSet, SideEffects::AllExceptGCDependency(), dex_pc), field_index_(field_index) { SetPackedField(field_type); DCHECK_EQ(DataType::Kind(field_type), DataType::Kind(value->GetType())); @@ -6648,15 +6877,13 @@ class HUnresolvedStaticFieldGet FINAL : public HExpression<0> { const uint32_t field_index_; }; -class HUnresolvedStaticFieldSet FINAL : public HTemplateInstruction<1> { +class HUnresolvedStaticFieldSet FINAL : public HExpression<1> { public: HUnresolvedStaticFieldSet(HInstruction* value, DataType::Type field_type, uint32_t field_index, uint32_t dex_pc) - : HTemplateInstruction(kUnresolvedStaticFieldSet, - SideEffects::AllExceptGCDependency(), - dex_pc), + : HExpression(kUnresolvedStaticFieldSet, SideEffects::AllExceptGCDependency(), dex_pc), field_index_(field_index) { SetPackedField(field_type); DCHECK_EQ(DataType::Kind(field_type), DataType::Kind(value->GetType())); @@ -6705,10 +6932,10 @@ class HLoadException FINAL : public HExpression<0> { // Implicit part of move-exception which clears thread-local exception storage. // Must not be removed because the runtime expects the TLS to get cleared. -class HClearException FINAL : public HTemplateInstruction<0> { +class HClearException FINAL : public HExpression<0> { public: explicit HClearException(uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kClearException, SideEffects::AllWrites(), dex_pc) { + : HExpression(kClearException, SideEffects::AllWrites(), dex_pc) { } DECLARE_INSTRUCTION(ClearException); @@ -6717,10 +6944,10 @@ class HClearException FINAL : public HTemplateInstruction<0> { DEFAULT_COPY_CONSTRUCTOR(ClearException); }; -class HThrow FINAL : public HTemplateInstruction<1> { +class HThrow FINAL : public HExpression<1> { public: HThrow(HInstruction* exception, uint32_t dex_pc) - : HTemplateInstruction(kThrow, SideEffects::CanTriggerGC(), dex_pc) { + : HExpression(kThrow, SideEffects::CanTriggerGC(), dex_pc) { SetRawInputAt(0, exception); } @@ -6750,72 +6977,162 @@ enum class TypeCheckKind { kInterfaceCheck, // No optimization yet when checking against an interface. kArrayObjectCheck, // Can just check if the array is not primitive. kArrayCheck, // No optimization yet when checking against a generic array. + kBitstringCheck, // Compare the type check bitstring. kLast = kArrayCheck }; std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs); -class HInstanceOf FINAL : public HExpression<2> { +// Note: HTypeCheckInstruction is just a helper class, not an abstract instruction with an +// `IsTypeCheckInstruction()`. (New virtual methods in the HInstruction class have a high cost.) +class HTypeCheckInstruction : public HVariableInputSizeInstruction { public: - HInstanceOf(HInstruction* object, - HLoadClass* target_class, - TypeCheckKind check_kind, - uint32_t dex_pc) - : HExpression(kInstanceOf, - DataType::Type::kBool, - SideEffectsForArchRuntimeCalls(check_kind), - dex_pc) { + HTypeCheckInstruction(InstructionKind kind, + DataType::Type type, + HInstruction* object, + HInstruction* target_class_or_null, + TypeCheckKind check_kind, + Handle klass, + uint32_t dex_pc, + ArenaAllocator* allocator, + HIntConstant* bitstring_path_to_root, + HIntConstant* bitstring_mask, + SideEffects side_effects) + : HVariableInputSizeInstruction( + kind, + type, + side_effects, + dex_pc, + allocator, + /* number_of_inputs */ check_kind == TypeCheckKind::kBitstringCheck ? 4u : 2u, + kArenaAllocTypeCheckInputs), + klass_(klass) { SetPackedField(check_kind); SetPackedFlag(true); + SetPackedFlag(false); SetRawInputAt(0, object); - SetRawInputAt(1, target_class); + SetRawInputAt(1, target_class_or_null); + DCHECK_EQ(check_kind == TypeCheckKind::kBitstringCheck, bitstring_path_to_root != nullptr); + DCHECK_EQ(check_kind == TypeCheckKind::kBitstringCheck, bitstring_mask != nullptr); + if (check_kind == TypeCheckKind::kBitstringCheck) { + DCHECK(target_class_or_null->IsNullConstant()); + SetRawInputAt(2, bitstring_path_to_root); + SetRawInputAt(3, bitstring_mask); + } else { + DCHECK(target_class_or_null->IsLoadClass()); + } } HLoadClass* GetTargetClass() const { + DCHECK_NE(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck); HInstruction* load_class = InputAt(1); DCHECK(load_class->IsLoadClass()); return load_class->AsLoadClass(); } - bool IsClonable() const OVERRIDE { return true; } - bool CanBeMoved() const OVERRIDE { return true; } + uint32_t GetBitstringPathToRoot() const { + DCHECK_EQ(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck); + HInstruction* path_to_root = InputAt(2); + DCHECK(path_to_root->IsIntConstant()); + return static_cast(path_to_root->AsIntConstant()->GetValue()); + } - bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { - return true; + uint32_t GetBitstringMask() const { + DCHECK_EQ(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck); + HInstruction* mask = InputAt(3); + DCHECK(mask->IsIntConstant()); + return static_cast(mask->AsIntConstant()->GetValue()); } - bool NeedsEnvironment() const OVERRIDE { - return CanCallRuntime(GetTypeCheckKind()); + bool IsClonable() const OVERRIDE { return true; } + bool CanBeMoved() const OVERRIDE { return true; } + + bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { + DCHECK(other->IsInstanceOf() || other->IsCheckCast()) << other->DebugName(); + return GetPackedFields() == down_cast(other)->GetPackedFields(); } - // Used only in code generation. bool MustDoNullCheck() const { return GetPackedFlag(); } void ClearMustDoNullCheck() { SetPackedFlag(false); } TypeCheckKind GetTypeCheckKind() const { return GetPackedField(); } bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; } - static bool CanCallRuntime(TypeCheckKind check_kind) { - // Mips currently does runtime calls for any other checks. - return check_kind != TypeCheckKind::kExactCheck; + ReferenceTypeInfo GetTargetClassRTI() { + if (GetPackedFlag()) { + // Note: The is_exact flag from the return value should not be used. + return ReferenceTypeInfo::CreateUnchecked(klass_, /* is_exact */ true); + } else { + return ReferenceTypeInfo::CreateInvalid(); + } } - static SideEffects SideEffectsForArchRuntimeCalls(TypeCheckKind check_kind) { - return CanCallRuntime(check_kind) ? SideEffects::CanTriggerGC() : SideEffects::None(); + // Target class RTI is marked as valid by RTP if the klass_ is admissible. + void SetValidTargetClassRTI() REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(klass_ != nullptr); + SetPackedFlag(true); } - DECLARE_INSTRUCTION(InstanceOf); + Handle GetClass() const { + return klass_; + } protected: - DEFAULT_COPY_CONSTRUCTOR(InstanceOf); + DEFAULT_COPY_CONSTRUCTOR(TypeCheckInstruction); private: - static constexpr size_t kFieldTypeCheckKind = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits; static constexpr size_t kFieldTypeCheckKindSize = MinimumBitsToStore(static_cast(TypeCheckKind::kLast)); static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; - static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagMustDoNullCheck + 1; + static constexpr size_t kFlagValidTargetClassRTI = kFlagMustDoNullCheck + 1; + static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagValidTargetClassRTI + 1; static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); using TypeCheckKindField = BitField; + + Handle klass_; +}; + +class HInstanceOf FINAL : public HTypeCheckInstruction { + public: + HInstanceOf(HInstruction* object, + HInstruction* target_class_or_null, + TypeCheckKind check_kind, + Handle klass, + uint32_t dex_pc, + ArenaAllocator* allocator, + HIntConstant* bitstring_path_to_root, + HIntConstant* bitstring_mask) + : HTypeCheckInstruction(kInstanceOf, + DataType::Type::kBool, + object, + target_class_or_null, + check_kind, + klass, + dex_pc, + allocator, + bitstring_path_to_root, + bitstring_mask, + SideEffectsForArchRuntimeCalls(check_kind)) {} + + bool IsClonable() const OVERRIDE { return true; } + + bool NeedsEnvironment() const OVERRIDE { + return CanCallRuntime(GetTypeCheckKind()); + } + + static bool CanCallRuntime(TypeCheckKind check_kind) { + // Mips currently does runtime calls for any other checks. + return check_kind != TypeCheckKind::kExactCheck; + } + + static SideEffects SideEffectsForArchRuntimeCalls(TypeCheckKind check_kind) { + return CanCallRuntime(check_kind) ? SideEffects::CanTriggerGC() : SideEffects::None(); + } + + DECLARE_INSTRUCTION(InstanceOf); + + protected: + DEFAULT_COPY_CONSTRUCTOR(InstanceOf); }; class HBoundType FINAL : public HExpression<1> { @@ -6829,6 +7146,7 @@ class HBoundType FINAL : public HExpression<1> { SetRawInputAt(0, input); } + bool InstructionDataEquals(const HInstruction* other) const OVERRIDE; bool IsClonable() const OVERRIDE { return true; } // {Get,Set}Upper* should only be used in reference type propagation. @@ -6851,7 +7169,7 @@ class HBoundType FINAL : public HExpression<1> { private: // Represents the top constraint that can_be_null_ cannot exceed (i.e. if this // is false then CanBeNull() cannot be true). - static constexpr size_t kFlagUpperCanBeNull = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagUpperCanBeNull = kNumberOfGenericPackedBits; static constexpr size_t kFlagCanBeNull = kFlagUpperCanBeNull + 1; static constexpr size_t kNumberOfBoundTypePackedBits = kFlagCanBeNull + 1; static_assert(kNumberOfBoundTypePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); @@ -6865,32 +7183,29 @@ class HBoundType FINAL : public HExpression<1> { ReferenceTypeInfo upper_bound_; }; -class HCheckCast FINAL : public HTemplateInstruction<2> { +class HCheckCast FINAL : public HTypeCheckInstruction { public: HCheckCast(HInstruction* object, - HLoadClass* target_class, + HInstruction* target_class_or_null, TypeCheckKind check_kind, - uint32_t dex_pc) - : HTemplateInstruction(kCheckCast, SideEffects::CanTriggerGC(), dex_pc) { - SetPackedField(check_kind); - SetPackedFlag(true); - SetRawInputAt(0, object); - SetRawInputAt(1, target_class); - } - - HLoadClass* GetTargetClass() const { - HInstruction* load_class = InputAt(1); - DCHECK(load_class->IsLoadClass()); - return load_class->AsLoadClass(); - } + Handle klass, + uint32_t dex_pc, + ArenaAllocator* allocator, + HIntConstant* bitstring_path_to_root, + HIntConstant* bitstring_mask) + : HTypeCheckInstruction(kCheckCast, + DataType::Type::kVoid, + object, + target_class_or_null, + check_kind, + klass, + dex_pc, + allocator, + bitstring_path_to_root, + bitstring_mask, + SideEffects::CanTriggerGC()) {} bool IsClonable() const OVERRIDE { return true; } - bool CanBeMoved() const OVERRIDE { return true; } - - bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { - return true; - } - bool NeedsEnvironment() const OVERRIDE { // Instruction may throw a CheckCastError. return true; @@ -6898,24 +7213,10 @@ class HCheckCast FINAL : public HTemplateInstruction<2> { bool CanThrow() const OVERRIDE { return true; } - bool MustDoNullCheck() const { return GetPackedFlag(); } - void ClearMustDoNullCheck() { SetPackedFlag(false); } - TypeCheckKind GetTypeCheckKind() const { return GetPackedField(); } - bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; } - DECLARE_INSTRUCTION(CheckCast); protected: DEFAULT_COPY_CONSTRUCTOR(CheckCast); - - private: - static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits; - static constexpr size_t kFieldTypeCheckKindSize = - MinimumBitsToStore(static_cast(TypeCheckKind::kLast)); - static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; - static constexpr size_t kNumberOfCheckCastPackedBits = kFlagMustDoNullCheck + 1; - static_assert(kNumberOfCheckCastPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); - using TypeCheckKindField = BitField; }; /** @@ -6944,13 +7245,12 @@ enum MemBarrierKind { }; std::ostream& operator<<(std::ostream& os, const MemBarrierKind& kind); -class HMemoryBarrier FINAL : public HTemplateInstruction<0> { +class HMemoryBarrier FINAL : public HExpression<0> { public: explicit HMemoryBarrier(MemBarrierKind barrier_kind, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction( - kMemoryBarrier, - SideEffects::AllWritesAndReads(), // Assume write/read on all fields/arrays. - dex_pc) { + : HExpression(kMemoryBarrier, + SideEffects::AllWritesAndReads(), // Assume write/read on all fields/arrays. + dex_pc) { SetPackedField(barrier_kind); } @@ -7127,7 +7427,7 @@ class HConstructorFence FINAL : public HVariableInputSizeInstruction { DEFAULT_COPY_CONSTRUCTOR(ConstructorFence); }; -class HMonitorOperation FINAL : public HTemplateInstruction<1> { +class HMonitorOperation FINAL : public HExpression<1> { public: enum class OperationKind { kEnter, @@ -7136,10 +7436,9 @@ class HMonitorOperation FINAL : public HTemplateInstruction<1> { }; HMonitorOperation(HInstruction* object, OperationKind kind, uint32_t dex_pc) - : HTemplateInstruction( - kMonitorOperation, - SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays. - dex_pc) { + : HExpression(kMonitorOperation, + SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays. + dex_pc) { SetPackedField(kind); SetRawInputAt(0, object); } @@ -7289,10 +7588,10 @@ std::ostream& operator<<(std::ostream& os, const MoveOperands& rhs); static constexpr size_t kDefaultNumberOfMoves = 4; -class HParallelMove FINAL : public HTemplateInstruction<0> { +class HParallelMove FINAL : public HExpression<0> { public: explicit HParallelMove(ArenaAllocator* allocator, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kParallelMove, SideEffects::None(), dex_pc), + : HExpression(kParallelMove, SideEffects::None(), dex_pc), moves_(allocator->Adapter(kArenaAllocMoveOperands)) { moves_.reserve(kDefaultNumberOfMoves); } @@ -7584,8 +7883,30 @@ inline bool IsZeroBitPattern(HInstruction* instruction) { return instruction->IsConstant() && instruction->AsConstant()->IsZeroBitPattern(); } +// Implement HInstruction::Is##type() for concrete instructions. #define INSTRUCTION_TYPE_CHECK(type, super) \ - inline bool HInstruction::Is##type() const { return GetKind() == k##type; } \ + inline bool HInstruction::Is##type() const { return GetKind() == k##type; } + FOR_EACH_CONCRETE_INSTRUCTION(INSTRUCTION_TYPE_CHECK) +#undef INSTRUCTION_TYPE_CHECK + +// Implement HInstruction::Is##type() for abstract instructions. +#define INSTRUCTION_TYPE_CHECK_RESULT(type, super) \ + std::is_base_of::value, +#define INSTRUCTION_TYPE_CHECK(type, super) \ + inline bool HInstruction::Is##type() const { \ + DCHECK_LT(GetKind(), kLastInstructionKind); \ + using BaseType = H##type; \ + static constexpr bool results[] = { \ + FOR_EACH_CONCRETE_INSTRUCTION(INSTRUCTION_TYPE_CHECK_RESULT) \ + }; \ + return results[static_cast(GetKind())]; \ + } + + FOR_EACH_ABSTRACT_INSTRUCTION(INSTRUCTION_TYPE_CHECK) +#undef INSTRUCTION_TYPE_CHECK +#undef INSTRUCTION_TYPE_CHECK_RESULT + +#define INSTRUCTION_TYPE_CAST(type, super) \ inline const H##type* HInstruction::As##type() const { \ return Is##type() ? down_cast(this) : nullptr; \ } \ @@ -7593,8 +7914,9 @@ inline bool IsZeroBitPattern(HInstruction* instruction) { return Is##type() ? static_cast(this) : nullptr; \ } - FOR_EACH_CONCRETE_INSTRUCTION(INSTRUCTION_TYPE_CHECK) -#undef INSTRUCTION_TYPE_CHECK + FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CAST) +#undef INSTRUCTION_TYPE_CAST + // Create space in `blocks` for adding `number_of_new_blocks` entries // starting at location `at`. Blocks after `at` are moved accordingly. diff --git a/compiler/optimizing/nodes_mips.h b/compiler/optimizing/nodes_mips.h index d0e0fef946a983402690891944e7034f11882196..05b27a7810ca465363b287ddef7fbd8f3a0c533b 100644 --- a/compiler/optimizing/nodes_mips.h +++ b/compiler/optimizing/nodes_mips.h @@ -39,14 +39,14 @@ class HMipsComputeBaseMethodAddress : public HExpression<0> { }; // Mips version of HPackedSwitch that holds a pointer to the base method address. -class HMipsPackedSwitch FINAL : public HTemplateInstruction<2> { +class HMipsPackedSwitch FINAL : public HExpression<2> { public: HMipsPackedSwitch(int32_t start_value, int32_t num_entries, HInstruction* input, HMipsComputeBaseMethodAddress* method_base, uint32_t dex_pc) - : HTemplateInstruction(kMipsPackedSwitch, SideEffects::None(), dex_pc), + : HExpression(kMipsPackedSwitch, SideEffects::None(), dex_pc), start_value_(start_value), num_entries_(num_entries) { SetRawInputAt(0, input); diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h index 20f6cf01ed8cd435eb88c53bd9639c41c6b4d396..95fb5ab76acc874b6121ee22c7da53d0914a36c9 100644 --- a/compiler/optimizing/nodes_vector.h +++ b/compiler/optimizing/nodes_vector.h @@ -79,13 +79,14 @@ class HVecOperation : public HVariableInputSizeInstruction { size_t vector_length, uint32_t dex_pc) : HVariableInputSizeInstruction(kind, + kSIMDType, side_effects, dex_pc, allocator, number_of_inputs, kArenaAllocVectorNode), vector_length_(vector_length) { - SetPackedField(packed_type); + SetPackedField(packed_type); DCHECK_LT(1u, vector_length); } @@ -99,14 +100,9 @@ class HVecOperation : public HVariableInputSizeInstruction { return vector_length_ * DataType::Size(GetPackedType()); } - // Returns the type of the vector operation. - DataType::Type GetType() const OVERRIDE { - return kSIMDType; - } - // Returns the true component type packed in a vector. DataType::Type GetPackedType() const { - return GetPackedField(); + return GetPackedField(); } // Assumes vector nodes cannot be moved by default. Each concrete implementation @@ -185,12 +181,12 @@ class HVecOperation : public HVariableInputSizeInstruction { protected: // Additional packed bits. - static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits; - static constexpr size_t kFieldTypeSize = + static constexpr size_t kFieldPackedType = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldPackedTypeSize = MinimumBitsToStore(static_cast(DataType::Type::kLast)); - static constexpr size_t kNumberOfVectorOpPackedBits = kFieldType + kFieldTypeSize; + static constexpr size_t kNumberOfVectorOpPackedBits = kFieldPackedType + kFieldPackedTypeSize; static_assert(kNumberOfVectorOpPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); - using TypeField = BitField; + using PackedTypeField = BitField; DEFAULT_COPY_CONSTRUCTOR(VecOperation); @@ -358,11 +354,9 @@ class HVecExtractScalar FINAL : public HVecUnaryOperation { DCHECK(HasConsistentPackedTypes(input, packed_type)); DCHECK_LT(index, vector_length); DCHECK_EQ(index, 0u); - } - - // Yields a single component in the vector. - DataType::Type GetType() const OVERRIDE { - return GetPackedType(); + // Yields a single component in the vector. + // Overrides the kSIMDType set by the VecOperation constructor. + SetPackedField(packed_type); } // An extract needs to stay in place, since SIMD registers are not @@ -533,6 +527,31 @@ class HVecAdd FINAL : public HVecBinaryOperation { DEFAULT_COPY_CONSTRUCTOR(VecAdd); }; +// Adds every component in the two vectors using saturation arithmetic, +// viz. [ x1, .. , xn ] + [ y1, .. , yn ] = [ x1 +_sat y1, .. , xn +_sat yn ] +// for either both signed or both unsigned operands x, y (reflected in packed_type). +class HVecSaturationAdd FINAL : public HVecBinaryOperation { + public: + HVecSaturationAdd(ArenaAllocator* allocator, + HInstruction* left, + HInstruction* right, + DataType::Type packed_type, + size_t vector_length, + uint32_t dex_pc) + : HVecBinaryOperation( + kVecSaturationAdd, allocator, left, right, packed_type, vector_length, dex_pc) { + DCHECK(HasConsistentPackedTypes(left, packed_type)); + DCHECK(HasConsistentPackedTypes(right, packed_type)); + } + + bool CanBeMoved() const OVERRIDE { return true; } + + DECLARE_INSTRUCTION(VecSaturationAdd); + + protected: + DEFAULT_COPY_CONSTRUCTOR(VecSaturationAdd); +}; + // Performs halving add on every component in the two vectors, viz. // rounded [ x1, .. , xn ] hradd [ y1, .. , yn ] = [ (x1 + y1 + 1) >> 1, .. , (xn + yn + 1) >> 1 ] // truncated [ x1, .. , xn ] hadd [ y1, .. , yn ] = [ (x1 + y1) >> 1, .. , (xn + yn ) >> 1 ] @@ -598,6 +617,31 @@ class HVecSub FINAL : public HVecBinaryOperation { DEFAULT_COPY_CONSTRUCTOR(VecSub); }; +// Subtracts every component in the two vectors using saturation arithmetic, +// viz. [ x1, .. , xn ] + [ y1, .. , yn ] = [ x1 -_sat y1, .. , xn -_sat yn ] +// for either both signed or both unsigned operands x, y (reflected in packed_type). +class HVecSaturationSub FINAL : public HVecBinaryOperation { + public: + HVecSaturationSub(ArenaAllocator* allocator, + HInstruction* left, + HInstruction* right, + DataType::Type packed_type, + size_t vector_length, + uint32_t dex_pc) + : HVecBinaryOperation( + kVecSaturationSub, allocator, left, right, packed_type, vector_length, dex_pc) { + DCHECK(HasConsistentPackedTypes(left, packed_type)); + DCHECK(HasConsistentPackedTypes(right, packed_type)); + } + + bool CanBeMoved() const OVERRIDE { return true; } + + DECLARE_INSTRUCTION(VecSaturationSub); + + protected: + DEFAULT_COPY_CONSTRUCTOR(VecSaturationSub); +}; + // Multiplies every component in the two vectors, // viz. [ x1, .. , xn ] * [ y1, .. , yn ] = [ x1 * y1, .. , xn * yn ]. class HVecMul FINAL : public HVecBinaryOperation { @@ -887,6 +931,9 @@ class HVecSetScalars FINAL : public HVecOperation { // Multiplies every component in the two vectors, adds the result vector to the accumulator vector, // viz. [ a1, .. , an ] + [ x1, .. , xn ] * [ y1, .. , yn ] = [ a1 + x1 * y1, .. , an + xn * yn ]. +// For floating point types, Java rounding behavior must be preserved; the products are rounded to +// the proper precision before being added. "Fused" multiply-add operations available on several +// architectures are not usable since they would violate Java language rules. class HVecMultiplyAccumulate FINAL : public HVecOperation { public: HVecMultiplyAccumulate(ArenaAllocator* allocator, @@ -909,6 +956,9 @@ class HVecMultiplyAccumulate FINAL : public HVecOperation { DCHECK(HasConsistentPackedTypes(accumulator, packed_type)); DCHECK(HasConsistentPackedTypes(mul_left, packed_type)); DCHECK(HasConsistentPackedTypes(mul_right, packed_type)); + // Remove the following if we add an architecture that supports floating point multiply-add + // with Java-compatible rounding. + DCHECK(DataType::IsIntegralType(packed_type)); SetRawInputAt(0, accumulator); SetRawInputAt(1, mul_left); SetRawInputAt(2, mul_right); diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h index 4c32be7d15cdc00de7ee3c6ce167e0b186c2995e..d1e7f68edbb592bc483b023eb6ac39e713a338c1 100644 --- a/compiler/optimizing/nodes_x86.h +++ b/compiler/optimizing/nodes_x86.h @@ -89,14 +89,14 @@ class HX86FPNeg FINAL : public HExpression<2> { }; // X86 version of HPackedSwitch that holds a pointer to the base method address. -class HX86PackedSwitch FINAL : public HTemplateInstruction<2> { +class HX86PackedSwitch FINAL : public HExpression<2> { public: HX86PackedSwitch(int32_t start_value, int32_t num_entries, HInstruction* input, HX86ComputeBaseMethodAddress* method_base, uint32_t dex_pc) - : HTemplateInstruction(kX86PackedSwitch, SideEffects::None(), dex_pc), + : HExpression(kX86PackedSwitch, SideEffects::None(), dex_pc), start_value_(start_value), num_entries_(num_entries) { SetRawInputAt(0, input); diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc index 57db7a634c9c662ed0ae9c69063890d8f10dcb36..142ddb5fbb2dc782404a6c43992061d8b44992db 100644 --- a/compiler/optimizing/optimization.cc +++ b/compiler/optimizing/optimization.cc @@ -40,6 +40,7 @@ #include "constructor_fence_redundancy_elimination.h" #include "dead_code_elimination.h" #include "dex/code_item_accessors-inl.h" +#include "driver/compiler_options.h" #include "driver/dex_compilation_unit.h" #include "gvn.h" #include "induction_var_analysis.h" @@ -121,12 +122,15 @@ const char* OptimizationPassName(OptimizationPass pass) { case OptimizationPass::kX86MemoryOperandGeneration: return x86::X86MemoryOperandGeneration::kX86MemoryOperandGenerationPassName; #endif + case OptimizationPass::kNone: + LOG(FATAL) << "kNone does not represent an actual pass"; + UNREACHABLE(); } } -#define X(x) if (name == OptimizationPassName((x))) return (x) +#define X(x) if (pass_name == OptimizationPassName((x))) return (x) -OptimizationPass OptimizationPassByName(const std::string& name) { +OptimizationPass OptimizationPassByName(const std::string& pass_name) { X(OptimizationPass::kBoundsCheckElimination); X(OptimizationPass::kCHAGuardOptimization); X(OptimizationPass::kCodeSinking); @@ -160,7 +164,7 @@ OptimizationPass OptimizationPassByName(const std::string& name) { X(OptimizationPass::kPcRelativeFixupsX86); X(OptimizationPass::kX86MemoryOperandGeneration); #endif - LOG(FATAL) << "Cannot find optimization " << name; + LOG(FATAL) << "Cannot find optimization " << pass_name; UNREACHABLE(); } @@ -187,9 +191,9 @@ ArenaVector ConstructOptimizations( // Loop over the requested optimizations. for (size_t i = 0; i < length; i++) { - OptimizationPass pass = definitions[i].first; - const char* alt_name = definitions[i].second; - const char* name = alt_name != nullptr + OptimizationPass pass = definitions[i].pass; + const char* alt_name = definitions[i].pass_name; + const char* pass_name = alt_name != nullptr ? alt_name : OptimizationPassName(pass); HOptimization* opt = nullptr; @@ -199,47 +203,48 @@ ArenaVector ConstructOptimizations( // Analysis passes (kept in most recent for subsequent passes). // case OptimizationPass::kSideEffectsAnalysis: - opt = most_recent_side_effects = new (allocator) SideEffectsAnalysis(graph, name); + opt = most_recent_side_effects = new (allocator) SideEffectsAnalysis(graph, pass_name); break; case OptimizationPass::kInductionVarAnalysis: - opt = most_recent_induction = new (allocator) HInductionVarAnalysis(graph, name); + opt = most_recent_induction = new (allocator) HInductionVarAnalysis(graph, pass_name); break; case OptimizationPass::kLoadStoreAnalysis: - opt = most_recent_lsa = new (allocator) LoadStoreAnalysis(graph, name); + opt = most_recent_lsa = new (allocator) LoadStoreAnalysis(graph, pass_name); break; // // Passes that need prior analysis. // case OptimizationPass::kGlobalValueNumbering: CHECK(most_recent_side_effects != nullptr); - opt = new (allocator) GVNOptimization(graph, *most_recent_side_effects, name); + opt = new (allocator) GVNOptimization(graph, *most_recent_side_effects, pass_name); break; case OptimizationPass::kInvariantCodeMotion: CHECK(most_recent_side_effects != nullptr); - opt = new (allocator) LICM(graph, *most_recent_side_effects, stats, name); + opt = new (allocator) LICM(graph, *most_recent_side_effects, stats, pass_name); break; case OptimizationPass::kLoopOptimization: CHECK(most_recent_induction != nullptr); - opt = new (allocator) HLoopOptimization(graph, driver, most_recent_induction, stats, name); + opt = new (allocator) HLoopOptimization( + graph, &codegen->GetCompilerOptions(), most_recent_induction, stats, pass_name); break; case OptimizationPass::kBoundsCheckElimination: CHECK(most_recent_side_effects != nullptr && most_recent_induction != nullptr); opt = new (allocator) BoundsCheckElimination( - graph, *most_recent_side_effects, most_recent_induction, name); + graph, *most_recent_side_effects, most_recent_induction, pass_name); break; case OptimizationPass::kLoadStoreElimination: CHECK(most_recent_side_effects != nullptr && most_recent_induction != nullptr); opt = new (allocator) LoadStoreElimination( - graph, *most_recent_side_effects, *most_recent_lsa, stats, name); + graph, *most_recent_side_effects, *most_recent_lsa, stats, pass_name); break; // // Regular passes. // case OptimizationPass::kConstantFolding: - opt = new (allocator) HConstantFolding(graph, name); + opt = new (allocator) HConstantFolding(graph, pass_name); break; case OptimizationPass::kDeadCodeElimination: - opt = new (allocator) HDeadCodeElimination(graph, stats, name); + opt = new (allocator) HDeadCodeElimination(graph, stats, pass_name); break; case OptimizationPass::kInliner: { CodeItemDataAccessor accessor(*dex_compilation_unit.GetDexFile(), @@ -256,33 +261,33 @@ ArenaVector ConstructOptimizations( /* total_number_of_instructions */ 0, /* parent */ nullptr, /* depth */ 0, - name); + pass_name); break; } case OptimizationPass::kSharpening: - opt = new (allocator) HSharpening(graph, codegen, driver, name); + opt = new (allocator) HSharpening(graph, codegen, pass_name); break; case OptimizationPass::kSelectGenerator: - opt = new (allocator) HSelectGenerator(graph, handles, stats, name); + opt = new (allocator) HSelectGenerator(graph, handles, stats, pass_name); break; case OptimizationPass::kInstructionSimplifier: - opt = new (allocator) InstructionSimplifier(graph, codegen, driver, stats, name); + opt = new (allocator) InstructionSimplifier(graph, codegen, stats, pass_name); break; case OptimizationPass::kIntrinsicsRecognizer: - opt = new (allocator) IntrinsicsRecognizer(graph, stats, name); + opt = new (allocator) IntrinsicsRecognizer(graph, stats, pass_name); break; case OptimizationPass::kCHAGuardOptimization: - opt = new (allocator) CHAGuardOptimization(graph, name); + opt = new (allocator) CHAGuardOptimization(graph, pass_name); break; case OptimizationPass::kCodeSinking: - opt = new (allocator) CodeSinking(graph, stats, name); + opt = new (allocator) CodeSinking(graph, stats, pass_name); break; case OptimizationPass::kConstructorFenceRedundancyElimination: - opt = new (allocator) ConstructorFenceRedundancyElimination(graph, stats, name); + opt = new (allocator) ConstructorFenceRedundancyElimination(graph, stats, pass_name); break; case OptimizationPass::kScheduling: opt = new (allocator) HInstructionScheduling( - graph, driver->GetInstructionSet(), codegen, name); + graph, codegen->GetCompilerOptions().GetInstructionSet(), codegen, pass_name); break; // // Arch-specific passes. @@ -319,11 +324,14 @@ ArenaVector ConstructOptimizations( opt = new (allocator) x86::X86MemoryOperandGeneration(graph, codegen, stats); break; #endif + case OptimizationPass::kNone: + LOG(FATAL) << "kNone does not represent an actual pass"; + UNREACHABLE(); } // switch // Add each next optimization to result vector. CHECK(opt != nullptr); - DCHECK_STREQ(name, opt->GetPassName()); // sanity + DCHECK_STREQ(pass_name, opt->GetPassName()); // sanity optimizations.push_back(opt); } diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h index c170f155fac1ee74ecffc82914315776d7978aeb..88b283cebfd7da05aa7aed0c18484b50f926ff23 100644 --- a/compiler/optimizing/optimization.h +++ b/compiler/optimizing/optimization.h @@ -47,8 +47,9 @@ class HOptimization : public ArenaObject { // 'instruction_simplifier$before_codegen'. const char* GetPassName() const { return pass_name_; } - // Perform the analysis itself. - virtual void Run() = 0; + // Perform the pass or analysis. Returns false if no optimizations occurred or no useful + // information was computed (this is best effort, returning true is always ok). + virtual bool Run() = 0; protected: HGraph* const graph_; @@ -101,21 +102,32 @@ enum class OptimizationPass { #if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64) kX86MemoryOperandGeneration, #endif + kNone, + kLast = kNone }; // Lookup name of optimization pass. const char* OptimizationPassName(OptimizationPass pass); // Lookup optimization pass by name. -OptimizationPass OptimizationPassByName(const std::string& name); +OptimizationPass OptimizationPassByName(const std::string& pass_name); // Optimization definition consisting of an optimization pass -// and an optional alternative name (nullptr denotes default). -typedef std::pair OptimizationDef; +// an optional alternative name (nullptr denotes default), and +// an optional pass dependence (kNone denotes no dependence). +struct OptimizationDef { + OptimizationDef(OptimizationPass p, const char* pn, OptimizationPass d) + : pass(p), pass_name(pn), depends_on(d) {} + OptimizationPass pass; + const char* pass_name; + OptimizationPass depends_on; +}; // Helper method for optimization definition array entries. -inline OptimizationDef OptDef(OptimizationPass pass, const char* name = nullptr) { - return std::make_pair(pass, name); +inline OptimizationDef OptDef(OptimizationPass pass, + const char* pass_name = nullptr, + OptimizationPass depends_on = OptimizationPass::kNone) { + return OptimizationDef(pass, pass_name, depends_on); } // Helper method to construct series of optimization passes. diff --git a/compiler/optimizing/optimizing_cfi_test.cc b/compiler/optimizing/optimizing_cfi_test.cc index d20b681b49aacfd579694f514bcf324d0fa9d980..1c1cf28294520827c9c4b62e73c224669d2ed484 100644 --- a/compiler/optimizing/optimizing_cfi_test.cc +++ b/compiler/optimizing/optimizing_cfi_test.cc @@ -47,25 +47,20 @@ class OptimizingCFITest : public CFITest, public OptimizingUnitTestHelper { static constexpr bool kGenerateExpected = false; OptimizingCFITest() - : pool_and_allocator_(), - opts_(), - isa_features_(), - graph_(nullptr), + : graph_(nullptr), code_gen_(), blocks_(GetAllocator()->Adapter()) {} - ArenaAllocator* GetAllocator() { return pool_and_allocator_.GetAllocator(); } - void SetUpFrame(InstructionSet isa) { + OverrideInstructionSetFeatures(isa, "default"); + // Ensure that slow-debug is off, so that there is no unexpected read-barrier check emitted. SetRuntimeDebugFlagsEnabled(false); // Setup simple context. - std::string error; - isa_features_ = InstructionSetFeatures::FromVariant(isa, "default", &error); graph_ = CreateGraph(); // Generate simple frame with some spills. - code_gen_ = CodeGenerator::Create(graph_, isa, *isa_features_, opts_); + code_gen_ = CodeGenerator::Create(graph_, *compiler_options_); code_gen_->GetAssembler()->cfi().SetEnabled(true); code_gen_->InitializeCodeGenerationData(); const int frame_size = 64; @@ -105,15 +100,15 @@ class OptimizingCFITest : public CFITest, public OptimizingUnitTestHelper { const std::vector& expected_asm, const std::vector& expected_cfi) { // Get the outputs. - const std::vector& actual_asm = code_allocator_.GetMemory(); + ArrayRef actual_asm = code_allocator_.GetMemory(); Assembler* opt_asm = code_gen_->GetAssembler(); - const std::vector& actual_cfi = *(opt_asm->cfi().data()); + ArrayRef actual_cfi(*(opt_asm->cfi().data())); if (kGenerateExpected) { GenerateExpected(stdout, isa, isa_str, actual_asm, actual_cfi); } else { - EXPECT_EQ(expected_asm, actual_asm); - EXPECT_EQ(expected_cfi, actual_cfi); + EXPECT_EQ(ArrayRef(expected_asm), actual_asm); + EXPECT_EQ(ArrayRef(expected_cfi), actual_cfi); } } @@ -140,7 +135,7 @@ class OptimizingCFITest : public CFITest, public OptimizingUnitTestHelper { return memory_.data(); } - const std::vector& GetMemory() { return memory_; } + ArrayRef GetMemory() const OVERRIDE { return ArrayRef(memory_); } private: std::vector memory_; @@ -148,9 +143,6 @@ class OptimizingCFITest : public CFITest, public OptimizingUnitTestHelper { DISALLOW_COPY_AND_ASSIGN(InternalCodeAllocator); }; - ArenaPoolAndAllocator pool_and_allocator_; - CompilerOptions opts_; - std::unique_ptr isa_features_; HGraph* graph_; std::unique_ptr code_gen_; ArenaVector blocks_; diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index e42dfc10ba51d1b0ef5d069bbdbf64c7d619e600..2f530a911a01078c80a165090ae46050ad6b2f52 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -31,6 +31,7 @@ #include "base/scoped_arena_allocator.h" #include "base/timing_logger.h" #include "builder.h" +#include "class_root.h" #include "code_generator.h" #include "compiled_method.h" #include "compiler.h" @@ -60,6 +61,7 @@ #include "ssa_builder.h" #include "ssa_liveness_analysis.h" #include "ssa_phi_elimination.h" +#include "stack_map_stream.h" #include "utils/assembler.h" #include "verifier/verifier_compiler_binding.h" @@ -75,22 +77,18 @@ static constexpr const char* kPassNameSeparator = "$"; class CodeVectorAllocator FINAL : public CodeAllocator { public: explicit CodeVectorAllocator(ArenaAllocator* allocator) - : memory_(allocator->Adapter(kArenaAllocCodeBuffer)), - size_(0) {} + : memory_(allocator->Adapter(kArenaAllocCodeBuffer)) {} virtual uint8_t* Allocate(size_t size) { - size_ = size; memory_.resize(size); return &memory_[0]; } - size_t GetSize() const { return size_; } - const ArenaVector& GetMemory() const { return memory_; } + ArrayRef GetMemory() const OVERRIDE { return ArrayRef(memory_); } uint8_t* GetData() { return memory_.data(); } private: ArenaVector memory_; - size_t size_; DISALLOW_COPY_AND_ASSIGN(CodeVectorAllocator); }; @@ -111,8 +109,9 @@ class PassObserver : public ValueObject { CompilerDriver* compiler_driver, Mutex& dump_mutex) : graph_(graph), + last_seen_graph_size_(0), cached_method_name_(), - timing_logger_enabled_(compiler_driver->GetCompilerOptions().GetDumpTimings()), + timing_logger_enabled_(compiler_driver->GetCompilerOptions().GetDumpPassTimings()), timing_logger_(timing_logger_enabled_ ? GetMethodName() : "", true, true), disasm_info_(graph->GetAllocator()), visualizer_oss_(), @@ -178,7 +177,7 @@ class PassObserver : public ValueObject { visualizer_oss_.clear(); } - void EndPass(const char* pass_name) REQUIRES(!visualizer_dump_mutex_) { + void EndPass(const char* pass_name, bool pass_change) REQUIRES(!visualizer_dump_mutex_) { // Pause timer first, then dump graph. if (timing_logger_enabled_) { timing_logger_.EndTiming(); @@ -192,7 +191,7 @@ class PassObserver : public ValueObject { if (kIsDebugBuild) { if (!graph_in_bad_state_) { GraphChecker checker(graph_); - checker.Run(); + last_seen_graph_size_ = checker.Run(pass_change, last_seen_graph_size_); if (!checker.IsValid()) { LOG(FATAL) << "Error after " << pass_name << ": " << Dumpable(checker); } @@ -218,6 +217,7 @@ class PassObserver : public ValueObject { } HGraph* const graph_; + size_t last_seen_graph_size_; std::string cached_method_name_; @@ -245,16 +245,22 @@ class PassScope : public ValueObject { public: PassScope(const char *pass_name, PassObserver* pass_observer) : pass_name_(pass_name), + pass_change_(true), // assume change pass_observer_(pass_observer) { pass_observer_->StartPass(pass_name_); } + void SetPassNotChanged() { + pass_change_ = false; + } + ~PassScope() { - pass_observer_->EndPass(pass_name_); + pass_observer_->EndPass(pass_name_, pass_change_); } private: const char* const pass_name_; + bool pass_change_; PassObserver* const pass_observer_; }; @@ -282,7 +288,7 @@ class OptimizingCompiler FINAL : public Compiler { uintptr_t GetEntryPointOf(ArtMethod* method) const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { return reinterpret_cast(method->GetEntryPointFromQuickCompiledCodePtrSize( - InstructionSetPointerSize(GetCompilerDriver()->GetInstructionSet()))); + InstructionSetPointerSize(GetCompilerDriver()->GetCompilerOptions().GetInstructionSet()))); } void Init() OVERRIDE; @@ -298,7 +304,7 @@ class OptimizingCompiler FINAL : public Compiler { REQUIRES_SHARED(Locks::mutator_lock_); private: - void RunOptimizations(HGraph* graph, + bool RunOptimizations(HGraph* graph, CodeGenerator* codegen, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, @@ -317,21 +323,38 @@ class OptimizingCompiler FINAL : public Compiler { dex_compilation_unit, handles); DCHECK_EQ(length, optimizations.size()); - // Run the optimization passes one by one. + // Run the optimization passes one by one. Any "depends_on" pass refers back to + // the most recent occurrence of that pass, skipped or executed. + std::bitset(OptimizationPass::kLast) + 1u> pass_changes; + pass_changes[static_cast(OptimizationPass::kNone)] = true; + bool change = false; for (size_t i = 0; i < length; ++i) { - PassScope scope(optimizations[i]->GetPassName(), pass_observer); - optimizations[i]->Run(); + if (pass_changes[static_cast(definitions[i].depends_on)]) { + // Execute the pass and record whether it changed anything. + PassScope scope(optimizations[i]->GetPassName(), pass_observer); + bool pass_change = optimizations[i]->Run(); + pass_changes[static_cast(definitions[i].pass)] = pass_change; + if (pass_change) { + change = true; + } else { + scope.SetPassNotChanged(); + } + } else { + // Skip the pass and record that nothing changed. + pass_changes[static_cast(definitions[i].pass)] = false; + } } + return change; } - template void RunOptimizations( + template bool RunOptimizations( HGraph* graph, CodeGenerator* codegen, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, VariableSizedHandleScope* handles, const OptimizationDef (&definitions)[length]) const { - RunOptimizations( + return RunOptimizations( graph, codegen, dex_compilation_unit, pass_observer, handles, definitions, length); } @@ -370,13 +393,7 @@ class OptimizingCompiler FINAL : public Compiler { ArtMethod* method, VariableSizedHandleScope* handles) const; - void MaybeRunInliner(HGraph* graph, - CodeGenerator* codegen, - const DexCompilationUnit& dex_compilation_unit, - PassObserver* pass_observer, - VariableSizedHandleScope* handles) const; - - void RunArchOptimizations(HGraph* graph, + bool RunArchOptimizations(HGraph* graph, CodeGenerator* codegen, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, @@ -439,33 +456,12 @@ static bool IsInstructionSetSupported(InstructionSet instruction_set) { || instruction_set == InstructionSet::kX86_64; } -void OptimizingCompiler::MaybeRunInliner(HGraph* graph, - CodeGenerator* codegen, - const DexCompilationUnit& dex_compilation_unit, - PassObserver* pass_observer, - VariableSizedHandleScope* handles) const { - const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); - bool should_inline = (compiler_options.GetInlineMaxCodeUnits() > 0); - if (!should_inline) { - return; - } - OptimizationDef optimizations[] = { - OptDef(OptimizationPass::kInliner) - }; - RunOptimizations(graph, - codegen, - dex_compilation_unit, - pass_observer, - handles, - optimizations); -} - -void OptimizingCompiler::RunArchOptimizations(HGraph* graph, +bool OptimizingCompiler::RunArchOptimizations(HGraph* graph, CodeGenerator* codegen, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, VariableSizedHandleScope* handles) const { - switch (GetCompilerDriver()->GetInstructionSet()) { + switch (codegen->GetCompilerOptions().GetInstructionSet()) { #if defined(ART_ENABLE_CODEGEN_arm) case InstructionSet::kThumb2: case InstructionSet::kArm: { @@ -475,13 +471,12 @@ void OptimizingCompiler::RunArchOptimizations(HGraph* graph, OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), OptDef(OptimizationPass::kScheduling) }; - RunOptimizations(graph, - codegen, - dex_compilation_unit, - pass_observer, - handles, - arm_optimizations); - break; + return RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + arm_optimizations); } #endif #ifdef ART_ENABLE_CODEGEN_arm64 @@ -492,13 +487,12 @@ void OptimizingCompiler::RunArchOptimizations(HGraph* graph, OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), OptDef(OptimizationPass::kScheduling) }; - RunOptimizations(graph, - codegen, - dex_compilation_unit, - pass_observer, - handles, - arm64_optimizations); - break; + return RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + arm64_optimizations); } #endif #ifdef ART_ENABLE_CODEGEN_mips @@ -509,13 +503,12 @@ void OptimizingCompiler::RunArchOptimizations(HGraph* graph, OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), OptDef(OptimizationPass::kPcRelativeFixupsMips) }; - RunOptimizations(graph, - codegen, - dex_compilation_unit, - pass_observer, - handles, - mips_optimizations); - break; + return RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + mips_optimizations); } #endif #ifdef ART_ENABLE_CODEGEN_mips64 @@ -524,13 +517,12 @@ void OptimizingCompiler::RunArchOptimizations(HGraph* graph, OptDef(OptimizationPass::kSideEffectsAnalysis), OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch") }; - RunOptimizations(graph, - codegen, - dex_compilation_unit, - pass_observer, - handles, - mips64_optimizations); - break; + return RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + mips64_optimizations); } #endif #ifdef ART_ENABLE_CODEGEN_x86 @@ -541,13 +533,12 @@ void OptimizingCompiler::RunArchOptimizations(HGraph* graph, OptDef(OptimizationPass::kPcRelativeFixupsX86), OptDef(OptimizationPass::kX86MemoryOperandGeneration) }; - RunOptimizations(graph, - codegen, - dex_compilation_unit, - pass_observer, - handles, - x86_optimizations); - break; + return RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + x86_optimizations); } #endif #ifdef ART_ENABLE_CODEGEN_x86_64 @@ -557,17 +548,16 @@ void OptimizingCompiler::RunArchOptimizations(HGraph* graph, OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), OptDef(OptimizationPass::kX86MemoryOperandGeneration) }; - RunOptimizations(graph, - codegen, - dex_compilation_unit, - pass_observer, - handles, - x86_64_optimizations); - break; + return RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + x86_64_optimizations); } #endif default: - break; + return false; } } @@ -614,6 +604,7 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph, if (pass_names != nullptr) { // If passes were defined on command-line, build the optimization // passes and run these instead of the built-in optimizations. + // TODO: a way to define depends_on via command-line? const size_t length = pass_names->size(); std::vector optimizations; for (const std::string& pass_name : *pass_names) { @@ -630,49 +621,64 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph, return; } - OptimizationDef optimizations1[] = { + OptimizationDef optimizations[] = { + // Initial optimizations. OptDef(OptimizationPass::kIntrinsicsRecognizer), OptDef(OptimizationPass::kSharpening), OptDef(OptimizationPass::kConstantFolding), OptDef(OptimizationPass::kInstructionSimplifier), - OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$initial") - }; - RunOptimizations(graph, - codegen, - dex_compilation_unit, - pass_observer, - handles, - optimizations1); - - MaybeRunInliner(graph, codegen, dex_compilation_unit, pass_observer, handles); - - OptimizationDef optimizations2[] = { - // SelectGenerator depends on the InstructionSimplifier removing - // redundant suspend checks to recognize empty blocks. - OptDef(OptimizationPass::kSelectGenerator), - // TODO: if we don't inline we can also skip fold2. - OptDef(OptimizationPass::kConstantFolding, "constant_folding$after_inlining"), - OptDef(OptimizationPass::kInstructionSimplifier, "instruction_simplifier$after_inlining"), - OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$after_inlining"), - OptDef(OptimizationPass::kSideEffectsAnalysis, "side_effects$before_gvn"), + OptDef(OptimizationPass::kDeadCodeElimination, + "dead_code_elimination$initial"), + // Inlining. + OptDef(OptimizationPass::kInliner), + // Simplification (only if inlining occurred). + OptDef(OptimizationPass::kConstantFolding, + "constant_folding$after_inlining", + OptimizationPass::kInliner), + OptDef(OptimizationPass::kInstructionSimplifier, + "instruction_simplifier$after_inlining", + OptimizationPass::kInliner), + OptDef(OptimizationPass::kDeadCodeElimination, + "dead_code_elimination$after_inlining", + OptimizationPass::kInliner), + // GVN. + OptDef(OptimizationPass::kSideEffectsAnalysis, + "side_effects$before_gvn"), OptDef(OptimizationPass::kGlobalValueNumbering), + // Simplification (TODO: only if GVN occurred). + OptDef(OptimizationPass::kSelectGenerator), + OptDef(OptimizationPass::kConstantFolding, + "constant_folding$after_gvn"), + OptDef(OptimizationPass::kInstructionSimplifier, + "instruction_simplifier$after_gvn"), + OptDef(OptimizationPass::kDeadCodeElimination, + "dead_code_elimination$after_gvn"), + // High-level optimizations. + OptDef(OptimizationPass::kSideEffectsAnalysis, + "side_effects$before_licm"), OptDef(OptimizationPass::kInvariantCodeMotion), OptDef(OptimizationPass::kInductionVarAnalysis), OptDef(OptimizationPass::kBoundsCheckElimination), OptDef(OptimizationPass::kLoopOptimization), - // Evaluates code generated by dynamic bce. - OptDef(OptimizationPass::kConstantFolding, "constant_folding$after_bce"), - OptDef(OptimizationPass::kInstructionSimplifier, "instruction_simplifier$after_bce"), - OptDef(OptimizationPass::kSideEffectsAnalysis, "side_effects$before_lse"), + // Simplification. + OptDef(OptimizationPass::kConstantFolding, + "constant_folding$after_bce"), + OptDef(OptimizationPass::kInstructionSimplifier, + "instruction_simplifier$after_bce"), + // Other high-level optimizations. + OptDef(OptimizationPass::kSideEffectsAnalysis, + "side_effects$before_lse"), OptDef(OptimizationPass::kLoadStoreAnalysis), OptDef(OptimizationPass::kLoadStoreElimination), OptDef(OptimizationPass::kCHAGuardOptimization), - OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$final"), + OptDef(OptimizationPass::kDeadCodeElimination, + "dead_code_elimination$final"), OptDef(OptimizationPass::kCodeSinking), // The codegen has a few assumptions that only the instruction simplifier // can satisfy. For example, the code generator does not expect to see a // HTypeConversion from a type to the same type. - OptDef(OptimizationPass::kInstructionSimplifier, "instruction_simplifier$before_codegen"), + OptDef(OptimizationPass::kInstructionSimplifier, + "instruction_simplifier$before_codegen"), // Eliminate constructor fences after code sinking to avoid // complicated sinking logic to split a fence with many inputs. OptDef(OptimizationPass::kConstructorFenceRedundancyElimination) @@ -682,7 +688,7 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph, dex_compilation_unit, pass_observer, handles, - optimizations2); + optimizations); RunArchOptimizations(graph, codegen, dex_compilation_unit, pass_observer, handles); } @@ -719,7 +725,7 @@ CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator, CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod( GetCompilerDriver(), codegen->GetInstructionSet(), - ArrayRef(code_allocator->GetMemory()), + code_allocator->GetMemory(), // Follow Quick's behavior and set the frame size to zero if it is // considered "empty" (see the definition of // art::CodeGenerator::HasEmptyFrame). @@ -731,6 +737,16 @@ CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator, ArrayRef(*codegen->GetAssembler()->cfi().data()), ArrayRef(linker_patches)); + CompiledMethodStorage* storage = GetCompilerDriver()->GetCompiledMethodStorage(); + for (const linker::LinkerPatch& patch : linker_patches) { + if (codegen->NeedsThunkCode(patch) && storage->GetThunkCode(patch).empty()) { + ArenaVector code(allocator->Adapter()); + std::string debug_name; + codegen->EmitThunkCode(patch, &code, &debug_name); + storage->SetThunkCode(patch, ArrayRef(code), debug_name); + } + } + return compiled_method; } @@ -743,7 +759,8 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, VariableSizedHandleScope* handles) const { MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kAttemptBytecodeCompilation); CompilerDriver* compiler_driver = GetCompilerDriver(); - InstructionSet instruction_set = compiler_driver->GetInstructionSet(); + const CompilerOptions& compiler_options = compiler_driver->GetCompilerOptions(); + InstructionSet instruction_set = compiler_options.GetInstructionSet(); const DexFile& dex_file = *dex_compilation_unit.GetDexFile(); uint32_t method_idx = dex_compilation_unit.GetDexMethodIndex(); const DexFile::CodeItem* code_item = dex_compilation_unit.GetCodeItem(); @@ -767,7 +784,6 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, // Implementation of the space filter: do not compile a code item whose size in // code units is bigger than 128. static constexpr size_t kSpaceFilterOptimizingThreshold = 128; - const CompilerOptions& compiler_options = compiler_driver->GetCompilerOptions(); if ((compiler_options.GetCompilerFilter() == CompilerFilter::kSpace) && (CodeItemInstructionAccessor(dex_file, code_item).InsnsSizeInCodeUnits() > kSpaceFilterOptimizingThreshold)) { @@ -781,7 +797,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, arena_stack, dex_file, method_idx, - compiler_driver->GetInstructionSet(), + compiler_options.GetInstructionSet(), kInvalidInvokeType, compiler_driver->GetCompilerOptions().GetDebuggable(), osr); @@ -798,9 +814,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, std::unique_ptr codegen( CodeGenerator::Create(graph, - instruction_set, - *compiler_driver->GetInstructionSetFeatures(), - compiler_driver->GetCompilerOptions(), + compiler_options, compilation_stats_.get())); if (codegen.get() == nullptr) { MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kNotCompiledNoCodegen); @@ -833,23 +847,23 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, case kAnalysisSkipped: { MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kNotCompiledSkipped); - } break; + } case kAnalysisInvalidBytecode: { MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kNotCompiledInvalidBytecode); - } break; + } case kAnalysisFailThrowCatchLoop: { MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kNotCompiledThrowCatchLoop); - } break; + } case kAnalysisFailAmbiguousArrayOp: { MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kNotCompiledAmbiguousArrayOp); - } break; + } case kAnalysisSuccess: UNREACHABLE(); } @@ -888,7 +902,8 @@ CodeGenerator* OptimizingCompiler::TryCompileIntrinsic( VariableSizedHandleScope* handles) const { MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kAttemptIntrinsicCompilation); CompilerDriver* compiler_driver = GetCompilerDriver(); - InstructionSet instruction_set = compiler_driver->GetInstructionSet(); + const CompilerOptions& compiler_options = compiler_driver->GetCompilerOptions(); + InstructionSet instruction_set = compiler_options.GetInstructionSet(); const DexFile& dex_file = *dex_compilation_unit.GetDexFile(); uint32_t method_idx = dex_compilation_unit.GetDexMethodIndex(); @@ -906,7 +921,7 @@ CodeGenerator* OptimizingCompiler::TryCompileIntrinsic( arena_stack, dex_file, method_idx, - compiler_driver->GetInstructionSet(), + compiler_driver->GetCompilerOptions().GetInstructionSet(), kInvalidInvokeType, compiler_driver->GetCompilerOptions().GetDebuggable(), /* osr */ false); @@ -917,15 +932,12 @@ CodeGenerator* OptimizingCompiler::TryCompileIntrinsic( std::unique_ptr codegen( CodeGenerator::Create(graph, - instruction_set, - *compiler_driver->GetInstructionSetFeatures(), - compiler_driver->GetCompilerOptions(), + compiler_options, compilation_stats_.get())); if (codegen.get() == nullptr) { return nullptr; } - codegen->GetAssembler()->cfi().SetEnabled( - compiler_driver->GetCompilerOptions().GenerateAnyDebugInfo()); + codegen->GetAssembler()->cfi().SetEnabled(compiler_options.GenerateAnyDebugInfo()); PassObserver pass_observer(graph, codegen.get(), @@ -1080,7 +1092,7 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, if (kIsDebugBuild && IsCompilingWithCoreImage() && - IsInstructionSetSupported(compiler_driver->GetInstructionSet())) { + IsInstructionSetSupported(compiler_driver->GetCompilerOptions().GetInstructionSet())) { // For testing purposes, we put a special marker on method names // that should be compiled with this compiler (when the // instruction set is supported). This makes sure we're not @@ -1093,13 +1105,35 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, return compiled_method; } +static void CreateJniStackMap(ArenaStack* arena_stack, + const JniCompiledMethod& jni_compiled_method, + /* out */ ArenaVector* stack_map, + /* out */ ArenaVector* method_info) { + ScopedArenaAllocator allocator(arena_stack); + StackMapStream stack_map_stream(&allocator, jni_compiled_method.GetInstructionSet()); + stack_map_stream.BeginMethod( + jni_compiled_method.GetFrameSize(), + jni_compiled_method.GetCoreSpillMask(), + jni_compiled_method.GetFpSpillMask(), + /* num_dex_registers */ 0); + stack_map_stream.EndMethod(); + stack_map->resize(stack_map_stream.PrepareForFillIn()); + method_info->resize(stack_map_stream.ComputeMethodInfoSize()); + stack_map_stream.FillInCodeInfo(MemoryRegion(stack_map->data(), stack_map->size())); + stack_map_stream.FillInMethodInfo(MemoryRegion(method_info->data(), method_info->size())); +} + CompiledMethod* OptimizingCompiler::JniCompile(uint32_t access_flags, uint32_t method_idx, const DexFile& dex_file, Handle dex_cache) const { - if (GetCompilerDriver()->GetCompilerOptions().IsBootImage()) { + Runtime* runtime = Runtime::Current(); + ArenaAllocator allocator(runtime->GetArenaPool()); + ArenaStack arena_stack(runtime->GetArenaPool()); + + const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); + if (compiler_options.IsBootImage()) { ScopedObjectAccess soa(Thread::Current()); - Runtime* runtime = Runtime::Current(); ArtMethod* method = runtime->GetClassLinker()->LookupResolvedMethod( method_idx, dex_cache.Get(), /* class_loader */ nullptr); if (method != nullptr && UNLIKELY(method->IsIntrinsic())) { @@ -1114,8 +1148,6 @@ CompiledMethod* OptimizingCompiler::JniCompile(uint32_t access_flags, access_flags, /* verified_method */ nullptr, dex_cache); - ArenaAllocator allocator(runtime->GetArenaPool()); - ArenaStack arena_stack(runtime->GetArenaPool()); CodeVectorAllocator code_allocator(&allocator); VariableSizedHandleScope handles(soa.Self()); // Go to native so that we don't block GC during compilation. @@ -1139,8 +1171,12 @@ CompiledMethod* OptimizingCompiler::JniCompile(uint32_t access_flags, } JniCompiledMethod jni_compiled_method = ArtQuickJniCompileMethod( - GetCompilerDriver(), access_flags, method_idx, dex_file); + compiler_options, access_flags, method_idx, dex_file); MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kCompiledNativeStub); + + ArenaVector stack_map(allocator.Adapter(kArenaAllocStackMaps)); + ArenaVector method_info(allocator.Adapter(kArenaAllocStackMaps)); + CreateJniStackMap(&arena_stack, jni_compiled_method, &stack_map, &method_info); return CompiledMethod::SwapAllocCompiledMethod( GetCompilerDriver(), jni_compiled_method.GetInstructionSet(), @@ -1148,8 +1184,8 @@ CompiledMethod* OptimizingCompiler::JniCompile(uint32_t access_flags, jni_compiled_method.GetFrameSize(), jni_compiled_method.GetCoreSpillMask(), jni_compiled_method.GetFpSpillMask(), - /* method_info */ ArrayRef(), - /* vmap_table */ ArrayRef(), + ArrayRef(method_info), + ArrayRef(stack_map), jni_compiled_method.GetCfi(), /* patches */ ArrayRef()); } @@ -1203,23 +1239,48 @@ bool OptimizingCompiler::JitCompile(Thread* self, ArenaAllocator allocator(runtime->GetJitArenaPool()); if (UNLIKELY(method->IsNative())) { + const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); JniCompiledMethod jni_compiled_method = ArtQuickJniCompileMethod( - GetCompilerDriver(), access_flags, method_idx, *dex_file); + compiler_options, access_flags, method_idx, *dex_file); ScopedNullHandle> roots; ArenaSet> cha_single_implementation_list( allocator.Adapter(kArenaAllocCHA)); + ArenaVector stack_map(allocator.Adapter(kArenaAllocStackMaps)); + ArenaVector method_info(allocator.Adapter(kArenaAllocStackMaps)); + ArenaStack arena_stack(runtime->GetJitArenaPool()); + // StackMapStream is large and it does not fit into this frame, so we need helper method. + // TODO: Try to avoid the extra memory copy that results from this. + CreateJniStackMap(&arena_stack, jni_compiled_method, &stack_map, &method_info); + uint8_t* stack_map_data = nullptr; + uint8_t* method_info_data = nullptr; + uint8_t* roots_data = nullptr; + uint32_t data_size = code_cache->ReserveData(self, + stack_map.size(), + method_info.size(), + /* number_of_roots */ 0, + method, + &stack_map_data, + &method_info_data, + &roots_data); + if (stack_map_data == nullptr || roots_data == nullptr) { + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kJitOutOfMemoryForCommit); + return false; + } + memcpy(stack_map_data, stack_map.data(), stack_map.size()); + memcpy(method_info_data, method_info.data(), method_info.size()); + const void* code = code_cache->CommitCode( self, method, - /* stack_map_data */ nullptr, - /* method_info_data */ nullptr, - /* roots_data */ nullptr, + stack_map_data, + method_info_data, + roots_data, jni_compiled_method.GetFrameSize(), jni_compiled_method.GetCoreSpillMask(), jni_compiled_method.GetFpSpillMask(), jni_compiled_method.GetCode().data(), jni_compiled_method.GetCode().size(), - /* data_size */ 0u, + data_size, osr, roots, /* has_should_deoptimize_flag */ false, @@ -1228,7 +1289,6 @@ bool OptimizingCompiler::JitCompile(Thread* self, return false; } - const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); if (compiler_options.GenerateAnyDebugInfo()) { const auto* method_header = reinterpret_cast(code); const uintptr_t code_address = reinterpret_cast(method_header->GetCode()); @@ -1295,13 +1355,12 @@ bool OptimizingCompiler::JitCompile(Thread* self, size_t method_info_size = 0; codegen->ComputeStackMapAndMethodInfoSize(&stack_map_size, &method_info_size); size_t number_of_roots = codegen->GetNumberOfJitRoots(); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); // We allocate an object array to ensure the JIT roots that we will collect in EmitJitRoots // will be visible by the GC between EmitLiterals and CommitCode. Once CommitCode is // executed, this array is not needed. Handle> roots( hs.NewHandle(mirror::ObjectArray::Alloc( - self, class_linker->GetClassRoot(ClassLinker::kObjectArrayClass), number_of_roots))); + self, GetClassRoot>(), number_of_roots))); if (roots == nullptr) { // Out of memory, just clear the exception to avoid any Java exception uncaught problems. MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kJitOutOfMemoryForCommit); @@ -1339,7 +1398,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, codegen->GetCoreSpillMask(), codegen->GetFpuSpillMask(), code_allocator.GetMemory().data(), - code_allocator.GetSize(), + code_allocator.GetMemory().size(), data_size, osr, roots, @@ -1369,7 +1428,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, info.is_optimized = true; info.is_code_address_text_relative = false; info.code_address = code_address; - info.code_size = code_allocator.GetSize(); + info.code_size = code_allocator.GetMemory().size(); info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); info.code_info = stack_map_size == 0 ? nullptr : stack_map_data; info.cfi = ArrayRef(*codegen->GetAssembler()->cfi().data()); @@ -1378,7 +1437,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, Runtime::Current()->GetJit()->AddMemoryUsage(method, allocator.BytesUsed()); if (jit_logger != nullptr) { - jit_logger->WriteLog(code, code_allocator.GetSize(), method); + jit_logger->WriteLog(code, code_allocator.GetMemory().size(), method); } if (kArenaAllocatorCountAllocations) { @@ -1406,8 +1465,8 @@ void OptimizingCompiler::GenerateJitDebugInfo(ArtMethod* method, debug::MethodDe // Create entry for the single method that we just compiled. std::vector elf_file = debug::MakeElfFileForJIT( - GetCompilerDriver()->GetInstructionSet(), - GetCompilerDriver()->GetInstructionSetFeatures(), + compiler_options.GetInstructionSet(), + compiler_options.GetInstructionSetFeatures(), mini_debug_info, ArrayRef(&info, 1)); MutexLock mu(Thread::Current(), *Locks::native_debug_interface_lock_); diff --git a/compiler/optimizing/optimizing_compiler.h b/compiler/optimizing/optimizing_compiler.h index d8cea30a6b2d0801092a0f1fd74d1f6e718df5d0..6ee9c70fdb31ccd5eb4574a7f5de5f5be9ef8f80 100644 --- a/compiler/optimizing/optimizing_compiler.h +++ b/compiler/optimizing/optimizing_compiler.h @@ -17,8 +17,8 @@ #ifndef ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_H_ #define ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_H_ +#include "base/globals.h" #include "base/mutex.h" -#include "globals.h" namespace art { diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index 00194ff1fe0343c290a275aaa612569cd86b945b..9a26f2f6c407091fc50bc739b63be1882846b1a3 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -99,6 +99,7 @@ enum class MethodCompilationStat { kConstructorFenceRemovedLSE, kConstructorFenceRemovedPFRA, kConstructorFenceRemovedCFRE, + kBitstringTypeCheck, kJitOutOfMemoryForCommit, kLastStat }; @@ -124,11 +125,6 @@ class OptimizingCompilerStats { } void Log() const { - if (!kIsDebugBuild && !VLOG_IS_ON(compiler)) { - // Log only in debug builds or if the compiler is verbose. - return; - } - uint32_t compiled_intrinsics = GetStat(MethodCompilationStat::kCompiledIntrinsic); uint32_t compiled_native_stubs = GetStat(MethodCompilationStat::kCompiledNativeStub); uint32_t bytecode_attempts = diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h index 6dcbadba6ed13c6f3f27e4202c70ed801a429d50..f903f82d5064465b7e2ab56085a00a108dd097ed 100644 --- a/compiler/optimizing/optimizing_unit_test.h +++ b/compiler/optimizing/optimizing_unit_test.h @@ -20,6 +20,7 @@ #include #include +#include "base/malloc_arena_pool.h" #include "base/scoped_arena_allocator.h" #include "builder.h" #include "common_compiler_test.h" @@ -28,6 +29,7 @@ #include "dex/dex_instruction.h" #include "dex/standard_dex_file.h" #include "driver/dex_compilation_unit.h" +#include "graph_checker.h" #include "handle_scope-inl.h" #include "mirror/class_loader.h" #include "mirror/dex_cache.h" @@ -97,7 +99,7 @@ class ArenaPoolAndAllocator { ScopedArenaAllocator* GetScopedAllocator() { return &scoped_allocator_; } private: - ArenaPool pool_; + MallocArenaPool pool_; ArenaAllocator allocator_; ArenaStack arena_stack_; ScopedArenaAllocator scoped_allocator_; @@ -186,6 +188,77 @@ class OptimizingUnitTestHelper { class OptimizingUnitTest : public CommonCompilerTest, public OptimizingUnitTestHelper {}; +// OptimizingUnitTest with some handy functions to ease the graph creation. +class ImprovedOptimizingUnitTest : public OptimizingUnitTest { + public: + ImprovedOptimizingUnitTest() : graph_(CreateGraph()), + entry_block_(nullptr), + return_block_(nullptr), + exit_block_(nullptr), + parameter_(nullptr) {} + + virtual ~ImprovedOptimizingUnitTest() {} + + void InitGraph() { + entry_block_ = new (GetAllocator()) HBasicBlock(graph_); + graph_->AddBlock(entry_block_); + graph_->SetEntryBlock(entry_block_); + + return_block_ = new (GetAllocator()) HBasicBlock(graph_); + graph_->AddBlock(return_block_); + + exit_block_ = new (GetAllocator()) HBasicBlock(graph_); + graph_->AddBlock(exit_block_); + graph_->SetExitBlock(exit_block_); + + entry_block_->AddSuccessor(return_block_); + return_block_->AddSuccessor(exit_block_); + + parameter_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(), + dex::TypeIndex(0), + 0, + DataType::Type::kInt32); + entry_block_->AddInstruction(parameter_); + return_block_->AddInstruction(new (GetAllocator()) HReturnVoid()); + exit_block_->AddInstruction(new (GetAllocator()) HExit()); + } + + bool CheckGraph() { + GraphChecker checker(graph_); + checker.Run(); + if (!checker.IsValid()) { + for (const std::string& error : checker.GetErrors()) { + std::cout << error << std::endl; + } + return false; + } + return true; + } + + HEnvironment* ManuallyBuildEnvFor(HInstruction* instruction, + ArenaVector* current_locals) { + HEnvironment* environment = new (GetAllocator()) HEnvironment( + (GetAllocator()), + current_locals->size(), + graph_->GetArtMethod(), + instruction->GetDexPc(), + instruction); + + environment->CopyFrom(ArrayRef(*current_locals)); + instruction->SetRawEnvironment(environment); + return environment; + } + + protected: + HGraph* graph_; + + HBasicBlock* entry_block_; + HBasicBlock* return_block_; + HBasicBlock* exit_block_; + + HInstruction* parameter_; +}; + // Naive string diff data type. typedef std::list> diff_t; diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc index cb87cabe1cd24b7dd2bf3b5cb1a69cabeabd2dbc..be3520116686534210ace2e64c30d4e77bf52454 100644 --- a/compiler/optimizing/parallel_move_test.cc +++ b/compiler/optimizing/parallel_move_test.cc @@ -15,6 +15,7 @@ */ #include "base/arena_allocator.h" +#include "base/malloc_arena_pool.h" #include "nodes.h" #include "parallel_move_resolver.h" @@ -180,7 +181,7 @@ TYPED_TEST_CASE(ParallelMoveTest, ParallelMoveResolverTestTypes); TYPED_TEST(ParallelMoveTest, Dependency) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); { @@ -207,7 +208,7 @@ TYPED_TEST(ParallelMoveTest, Dependency) { } TYPED_TEST(ParallelMoveTest, Cycle) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); { @@ -257,7 +258,7 @@ TYPED_TEST(ParallelMoveTest, Cycle) { } TYPED_TEST(ParallelMoveTest, ConstantLast) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); TypeParam resolver(&allocator); HParallelMove* moves = new (&allocator) HParallelMove(&allocator); @@ -276,7 +277,7 @@ TYPED_TEST(ParallelMoveTest, ConstantLast) { } TYPED_TEST(ParallelMoveTest, Pairs) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); { @@ -453,7 +454,7 @@ TYPED_TEST(ParallelMoveTest, Pairs) { } TYPED_TEST(ParallelMoveTest, MultiCycles) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); { @@ -551,7 +552,7 @@ TYPED_TEST(ParallelMoveTest, MultiCycles) { // Test that we do 64bits moves before 32bits moves. TYPED_TEST(ParallelMoveTest, CyclesWith64BitsMoves) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); { @@ -610,7 +611,7 @@ TYPED_TEST(ParallelMoveTest, CyclesWith64BitsMoves) { } TYPED_TEST(ParallelMoveTest, CyclesWith64BitsMoves2) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); { diff --git a/compiler/optimizing/pc_relative_fixups_mips.cc b/compiler/optimizing/pc_relative_fixups_mips.cc index 9d5358514eec1d768d7a0636f70dfee8d199a19c..f18ecc14584bac0be2b732009bcc68754984af95 100644 --- a/compiler/optimizing/pc_relative_fixups_mips.cc +++ b/compiler/optimizing/pc_relative_fixups_mips.cc @@ -75,7 +75,7 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { switch (load_kind) { case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: case HLoadClass::LoadKind::kBootImageAddress: - case HLoadClass::LoadKind::kBootImageClassTable: + case HLoadClass::LoadKind::kBootImageRelRo: case HLoadClass::LoadKind::kBssEntry: // Add a base register for PC-relative literals on R2. InitializePCRelativeBasePointer(); @@ -91,7 +91,7 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { switch (load_kind) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: case HLoadString::LoadKind::kBootImageAddress: - case HLoadString::LoadKind::kBootImageInternTable: + case HLoadString::LoadKind::kBootImageRelRo: case HLoadString::LoadKind::kBssEntry: // Add a base register for PC-relative literals on R2. InitializePCRelativeBasePointer(); @@ -128,20 +128,21 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { HMipsComputeBaseMethodAddress* base_; }; -void PcRelativeFixups::Run() { +bool PcRelativeFixups::Run() { CodeGeneratorMIPS* mips_codegen = down_cast(codegen_); if (mips_codegen->GetInstructionSetFeatures().IsR6()) { // Do nothing for R6 because it has PC-relative addressing. - return; + return false; } if (graph_->HasIrreducibleLoops()) { // Do not run this optimization, as irreducible loops do not work with an instruction // that can be live-in at the irreducible loop header. - return; + return false; } PCRelativeHandlerVisitor visitor(graph_, codegen_); visitor.VisitInsertionOrder(); visitor.MoveBaseIfNeeded(); + return true; } } // namespace mips diff --git a/compiler/optimizing/pc_relative_fixups_mips.h b/compiler/optimizing/pc_relative_fixups_mips.h index ec2c711f8d4afafcc49bd82959086d5f47ed57ee..6dd1ee0db26c1864b1e812758ae74ca2207ef504 100644 --- a/compiler/optimizing/pc_relative_fixups_mips.h +++ b/compiler/optimizing/pc_relative_fixups_mips.h @@ -34,7 +34,7 @@ class PcRelativeFixups : public HOptimization { static constexpr const char* kPcRelativeFixupsMipsPassName = "pc_relative_fixups_mips"; - void Run() OVERRIDE; + bool Run() OVERRIDE; private: CodeGenerator* codegen_; diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc index f92f4b274ae08c06cd62912057ec1ca195238d8a..05ec765b19720f3d1e5f406b13be4a34e1ad2796 100644 --- a/compiler/optimizing/pc_relative_fixups_x86.cc +++ b/compiler/optimizing/pc_relative_fixups_x86.cc @@ -81,20 +81,14 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { } void VisitLoadClass(HLoadClass* load_class) OVERRIDE { - HLoadClass::LoadKind load_kind = load_class->GetLoadKind(); - if (load_kind == HLoadClass::LoadKind::kBootImageLinkTimePcRelative || - load_kind == HLoadClass::LoadKind::kBootImageClassTable || - load_kind == HLoadClass::LoadKind::kBssEntry) { + if (load_class->HasPcRelativeLoadKind()) { HX86ComputeBaseMethodAddress* method_address = GetPCRelativeBasePointer(load_class); load_class->AddSpecialInput(method_address); } } void VisitLoadString(HLoadString* load_string) OVERRIDE { - HLoadString::LoadKind load_kind = load_string->GetLoadKind(); - if (load_kind == HLoadString::LoadKind::kBootImageLinkTimePcRelative || - load_kind == HLoadString::LoadKind::kBootImageInternTable || - load_kind == HLoadString::LoadKind::kBssEntry) { + if (load_string->HasPcRelativeLoadKind()) { HX86ComputeBaseMethodAddress* method_address = GetPCRelativeBasePointer(load_string); load_string->AddSpecialInput(method_address); } @@ -199,18 +193,19 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { } void HandleInvoke(HInvoke* invoke) { - // If this is an invoke-static/-direct with PC-relative dex cache array - // addressing, we need the PC-relative address base. HInvokeStaticOrDirect* invoke_static_or_direct = invoke->AsInvokeStaticOrDirect(); - // We can't add a pointer to the constant area if we already have a current - // method pointer. This may arise when sharpening doesn't remove the current - // method pointer from the invoke. - if (invoke_static_or_direct != nullptr && - invoke_static_or_direct->HasCurrentMethodInput()) { + + // We can't add the method address if we already have a current method pointer. + // This may arise when sharpening doesn't remove the current method pointer from the invoke. + if (invoke_static_or_direct != nullptr && invoke_static_or_direct->HasCurrentMethodInput()) { + // Note: This happens only for recursive calls (including compiling an intrinsic + // by faking a call to itself; we use kRuntimeCall for this case). DCHECK(!invoke_static_or_direct->HasPcRelativeMethodLoadKind()); return; } + // If this is an invoke-static/-direct with PC-relative addressing (within boot image + // or using .bss or .data.bimg.rel.ro), we need the PC-relative address base. bool base_added = false; if (invoke_static_or_direct != nullptr && invoke_static_or_direct->HasPcRelativeMethodLoadKind() && @@ -230,7 +225,6 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { } } - // These intrinsics need the constant area. switch (invoke->GetIntrinsic()) { case Intrinsics::kMathAbsDouble: case Intrinsics::kMathAbsFloat: @@ -238,7 +232,18 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { case Intrinsics::kMathMaxFloatFloat: case Intrinsics::kMathMinDoubleDouble: case Intrinsics::kMathMinFloatFloat: + LOG(FATAL) << "Unreachable min/max/abs: intrinsics should have been lowered " + "to IR nodes by instruction simplifier"; + UNREACHABLE(); + case Intrinsics::kIntegerValueOf: + // This intrinsic can be call free if it loads the address of the boot image object. + // If we're compiling PIC, we need the address base for loading from .data.bimg.rel.ro. + if (!codegen_->GetCompilerOptions().GetCompilePic()) { + break; + } + FALLTHROUGH_INTENDED; case Intrinsics::kMathRoundFloat: + // This intrinsic needs the constant area. if (!base_added) { DCHECK(invoke_static_or_direct != nullptr); DCHECK(!invoke_static_or_direct->HasCurrentMethodInput()); @@ -259,10 +264,11 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { HX86ComputeBaseMethodAddress* base_; }; -void PcRelativeFixups::Run() { +bool PcRelativeFixups::Run() { PCRelativeHandlerVisitor visitor(graph_, codegen_); visitor.VisitInsertionOrder(); visitor.MoveBaseIfNeeded(); + return true; } } // namespace x86 diff --git a/compiler/optimizing/pc_relative_fixups_x86.h b/compiler/optimizing/pc_relative_fixups_x86.h index 72fa71ea9449c8880ceefd818ae6ec0accfde8ea..db56b7f0530483ff7591399c49083cdd87bfe55c 100644 --- a/compiler/optimizing/pc_relative_fixups_x86.h +++ b/compiler/optimizing/pc_relative_fixups_x86.h @@ -34,7 +34,7 @@ class PcRelativeFixups : public HOptimization { static constexpr const char* kPcRelativeFixupsX86PassName = "pc_relative_fixups_x86"; - void Run() OVERRIDE; + bool Run() OVERRIDE; private: CodeGenerator* codegen_; diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index f843c008d8babd9e4ba33f1277fd4aaa0a03da5a..831bccc90a131f7952db0837cb808b8dd4c37a7e 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -17,7 +17,7 @@ #include "prepare_for_register_allocation.h" #include "dex/dex_file_types.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "optimizing_compiler_stats.h" #include "well_known_classes.h" @@ -34,6 +34,20 @@ void PrepareForRegisterAllocation::Run() { } } +void PrepareForRegisterAllocation::VisitCheckCast(HCheckCast* check_cast) { + // Record only those bitstring type checks that make it to the codegen stage. + if (check_cast->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { + MaybeRecordStat(stats_, MethodCompilationStat::kBitstringTypeCheck); + } +} + +void PrepareForRegisterAllocation::VisitInstanceOf(HInstanceOf* instance_of) { + // Record only those bitstring type checks that make it to the codegen stage. + if (instance_of->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { + MaybeRecordStat(stats_, MethodCompilationStat::kBitstringTypeCheck); + } +} + void PrepareForRegisterAllocation::VisitNullCheck(HNullCheck* check) { check->ReplaceWith(check->InputAt(0)); } diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h index 2c64f016c17a64187245a843339be7ca5a8f6753..f6e4d3ef99bc75f2b4a8552f7e6c22a0fb5cb62e 100644 --- a/compiler/optimizing/prepare_for_register_allocation.h +++ b/compiler/optimizing/prepare_for_register_allocation.h @@ -40,6 +40,8 @@ class PrepareForRegisterAllocation : public HGraphDelegateVisitor { "prepare_for_register_allocation"; private: + void VisitCheckCast(HCheckCast* check_cast) OVERRIDE; + void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE; void VisitNullCheck(HNullCheck* check) OVERRIDE; void VisitDivZeroCheck(HDivZeroCheck* check) OVERRIDE; void VisitBoundsCheck(HBoundsCheck* check) OVERRIDE; diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 67a61fc01decb18d60f6dabae87701f7de3c4137..f3fe62561f3518545224ca3d8531341bb6d06578 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -22,6 +22,7 @@ #include "base/scoped_arena_containers.h" #include "base/enums.h" #include "class_linker-inl.h" +#include "class_root.h" #include "handle_scope-inl.h" #include "mirror/class-inl.h" #include "mirror/dex_cache.h" @@ -40,31 +41,40 @@ static inline ObjPtr FindDexCacheWithHint( } static inline ReferenceTypeInfo::TypeHandle GetRootHandle(VariableSizedHandleScope* handles, - ClassLinker::ClassRoot class_root, + ClassRoot class_root, ReferenceTypeInfo::TypeHandle* cache) { if (!ReferenceTypeInfo::IsValidHandle(*cache)) { // Mutator lock is required for NewHandle. - ClassLinker* linker = Runtime::Current()->GetClassLinker(); ScopedObjectAccess soa(Thread::Current()); - *cache = handles->NewHandle(linker->GetClassRoot(class_root)); + *cache = handles->NewHandle(GetClassRoot(class_root)); } return *cache; } ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetObjectClassHandle() { - return GetRootHandle(handles_, ClassLinker::kJavaLangObject, &object_class_handle_); + return GetRootHandle(handles_, ClassRoot::kJavaLangObject, &object_class_handle_); } ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetClassClassHandle() { - return GetRootHandle(handles_, ClassLinker::kJavaLangClass, &class_class_handle_); + return GetRootHandle(handles_, ClassRoot::kJavaLangClass, &class_class_handle_); +} + +ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetMethodHandleClassHandle() { + return GetRootHandle(handles_, + ClassRoot::kJavaLangInvokeMethodHandleImpl, + &method_handle_class_handle_); +} + +ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetMethodTypeClassHandle() { + return GetRootHandle(handles_, ClassRoot::kJavaLangInvokeMethodType, &method_type_class_handle_); } ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetStringClassHandle() { - return GetRootHandle(handles_, ClassLinker::kJavaLangString, &string_class_handle_); + return GetRootHandle(handles_, ClassRoot::kJavaLangString, &string_class_handle_); } ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetThrowableClassHandle() { - return GetRootHandle(handles_, ClassLinker::kJavaLangThrowable, &throwable_class_handle_); + return GetRootHandle(handles_, ClassRoot::kJavaLangThrowable, &throwable_class_handle_); } class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { @@ -87,7 +97,10 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { void VisitDeoptimize(HDeoptimize* deopt) OVERRIDE; void VisitNewInstance(HNewInstance* new_instance) OVERRIDE; void VisitLoadClass(HLoadClass* load_class) OVERRIDE; + void VisitInstanceOf(HInstanceOf* load_class) OVERRIDE; void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE; + void VisitLoadMethodHandle(HLoadMethodHandle* instr) OVERRIDE; + void VisitLoadMethodType(HLoadMethodType* instr) OVERRIDE; void VisitLoadString(HLoadString* instr) OVERRIDE; void VisitLoadException(HLoadException* instr) OVERRIDE; void VisitNewArray(HNewArray* instr) OVERRIDE; @@ -171,6 +184,12 @@ void ReferenceTypePropagation::ValidateTypes() { << "NullCheck " << instr->GetReferenceTypeInfo() << "Input(0) " << instr->InputAt(0)->GetReferenceTypeInfo(); } + } else if (instr->IsInstanceOf()) { + HInstanceOf* iof = instr->AsInstanceOf(); + DCHECK(!iof->GetTargetClassRTI().IsValid() || iof->GetTargetClassRTI().IsExact()); + } else if (instr->IsCheckCast()) { + HCheckCast* check = instr->AsCheckCast(); + DCHECK(!check->GetTargetClassRTI().IsValid() || check->GetTargetClassRTI().IsExact()); } } } @@ -320,8 +339,7 @@ static void BoundTypeForClassCheck(HInstruction* check) { { ScopedObjectAccess soa(Thread::Current()); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - ArtField* field = class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0); + ArtField* field = GetClassRoot()->GetInstanceField(0); DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_"); if (field_get->GetFieldInfo().GetField() != field) { return; @@ -341,7 +359,7 @@ static void BoundTypeForClassCheck(HInstruction* check) { } } -void ReferenceTypePropagation::Run() { +bool ReferenceTypePropagation::Run() { RTPVisitor visitor(graph_, class_loader_, hint_dex_cache_, &handle_cache_, is_first_run_); // To properly propagate type info we need to visit in the dominator-based order. @@ -353,6 +371,7 @@ void ReferenceTypePropagation::Run() { visitor.ProcessWorklist(); ValidateTypes(); + return true; } void ReferenceTypePropagation::RTPVisitor::VisitBasicBlock(HBasicBlock* block) { @@ -499,8 +518,7 @@ void ReferenceTypePropagation::RTPVisitor::BoundTypeForIfInstanceOf(HBasicBlock* return; } - HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass(); - ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI(); + ReferenceTypeInfo class_rti = instanceOf->GetTargetClassRTI(); if (!class_rti.IsValid()) { // He have loaded an unresolved class. Don't bother bounding the type. return; @@ -643,19 +661,35 @@ void ReferenceTypePropagation::RTPVisitor::VisitUnresolvedStaticFieldGet( void ReferenceTypePropagation::RTPVisitor::VisitLoadClass(HLoadClass* instr) { ScopedObjectAccess soa(Thread::Current()); - Handle resolved_class = instr->GetClass(); - if (IsAdmissible(resolved_class.Get())) { - instr->SetLoadedClassRTI(ReferenceTypeInfo::Create( - resolved_class, /* is_exact */ true)); + if (IsAdmissible(instr->GetClass().Get())) { + instr->SetValidLoadedClassRTI(); } instr->SetReferenceTypeInfo( ReferenceTypeInfo::Create(handle_cache_->GetClassClassHandle(), /* is_exact */ true)); } +void ReferenceTypePropagation::RTPVisitor::VisitInstanceOf(HInstanceOf* instr) { + ScopedObjectAccess soa(Thread::Current()); + if (IsAdmissible(instr->GetClass().Get())) { + instr->SetValidTargetClassRTI(); + } +} + void ReferenceTypePropagation::RTPVisitor::VisitClinitCheck(HClinitCheck* instr) { instr->SetReferenceTypeInfo(instr->InputAt(0)->GetReferenceTypeInfo()); } +void ReferenceTypePropagation::RTPVisitor::VisitLoadMethodHandle(HLoadMethodHandle* instr) { + instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create( + handle_cache_->GetMethodHandleClassHandle(), + /* is_exact */ true)); +} + +void ReferenceTypePropagation::RTPVisitor::VisitLoadMethodType(HLoadMethodType* instr) { + instr->SetReferenceTypeInfo( + ReferenceTypeInfo::Create(handle_cache_->GetMethodTypeClassHandle(), /* is_exact */ true)); +} + void ReferenceTypePropagation::RTPVisitor::VisitLoadString(HLoadString* instr) { instr->SetReferenceTypeInfo( ReferenceTypeInfo::Create(handle_cache_->GetStringClassHandle(), /* is_exact */ true)); @@ -719,8 +753,6 @@ void ReferenceTypePropagation::RTPVisitor::VisitBoundType(HBoundType* instr) { } void ReferenceTypePropagation::RTPVisitor::VisitCheckCast(HCheckCast* check_cast) { - HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass(); - ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI(); HBoundType* bound_type = check_cast->GetNext()->AsBoundType(); if (bound_type == nullptr || bound_type->GetUpperBound().IsValid()) { // The next instruction is not an uninitialized BoundType. This must be @@ -729,12 +761,14 @@ void ReferenceTypePropagation::RTPVisitor::VisitCheckCast(HCheckCast* check_cast } DCHECK_EQ(bound_type->InputAt(0), check_cast->InputAt(0)); - if (class_rti.IsValid()) { + ScopedObjectAccess soa(Thread::Current()); + Handle klass = check_cast->GetClass(); + if (IsAdmissible(klass.Get())) { DCHECK(is_first_run_); - ScopedObjectAccess soa(Thread::Current()); + check_cast->SetValidTargetClassRTI(); // This is the first run of RTP and class is resolved. - bool is_exact = class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes(); - bound_type->SetUpperBound(ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), is_exact), + bool is_exact = klass->CannotBeAssignedFromOtherTypes(); + bound_type->SetUpperBound(ReferenceTypeInfo::Create(klass, is_exact), /* CheckCast succeeds for nulls. */ true); } else { // This is the first run of RTP and class is unresolved. Remove the binding. diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h index fd4dad2b4595f0a262eed06ef1e682dec3d99d0d..d36d59270853c903272654a5c8a41620081e9629 100644 --- a/compiler/optimizing/reference_type_propagation.h +++ b/compiler/optimizing/reference_type_propagation.h @@ -40,7 +40,7 @@ class ReferenceTypePropagation : public HOptimization { // Visit a single instruction. void Visit(HInstruction* instruction); - void Run() OVERRIDE; + bool Run() OVERRIDE; // Returns true if klass is admissible to the propagation: non-null and resolved. // For an array type, we also check if the component type is admissible. @@ -75,6 +75,8 @@ class ReferenceTypePropagation : public HOptimization { ReferenceTypeInfo::TypeHandle GetObjectClassHandle(); ReferenceTypeInfo::TypeHandle GetClassClassHandle(); + ReferenceTypeInfo::TypeHandle GetMethodHandleClassHandle(); + ReferenceTypeInfo::TypeHandle GetMethodTypeClassHandle(); ReferenceTypeInfo::TypeHandle GetStringClassHandle(); ReferenceTypeInfo::TypeHandle GetThrowableClassHandle(); @@ -83,6 +85,8 @@ class ReferenceTypePropagation : public HOptimization { ReferenceTypeInfo::TypeHandle object_class_handle_; ReferenceTypeInfo::TypeHandle class_class_handle_; + ReferenceTypeInfo::TypeHandle method_handle_class_handle_; + ReferenceTypeInfo::TypeHandle method_type_class_handle_; ReferenceTypeInfo::TypeHandle string_class_handle_; ReferenceTypeInfo::TypeHandle throwable_class_handle_; }; diff --git a/compiler/optimizing/register_allocator_graph_color.cc b/compiler/optimizing/register_allocator_graph_color.cc index fa7ad82316c34d3259b3a6cb67a8f70bae00a3b3..42e6498148c8d091b5b9933a14de63211932af9c 100644 --- a/compiler/optimizing/register_allocator_graph_color.cc +++ b/compiler/optimizing/register_allocator_graph_color.cc @@ -1183,7 +1183,7 @@ static bool CheckInputOutputCanOverlap(InterferenceNode* in_node, InterferenceNo void ColoringIteration::BuildInterferenceGraph( const ScopedArenaVector& intervals, const ScopedArenaVector& physical_nodes) { - DCHECK(interval_node_map_.Empty() && prunable_nodes_.empty()); + DCHECK(interval_node_map_.empty() && prunable_nodes_.empty()); // Build the interference graph efficiently by ordering range endpoints // by position and doing a linear sweep to find interferences. (That is, we // jump from endpoint to endpoint, maintaining a set of intervals live at each @@ -1208,7 +1208,7 @@ void ColoringIteration::BuildInterferenceGraph( if (range != nullptr) { InterferenceNode* node = new (allocator_) InterferenceNode(sibling, register_allocator_->liveness_); - interval_node_map_.Insert(std::make_pair(sibling, node)); + interval_node_map_.insert(std::make_pair(sibling, node)); if (sibling->HasRegister()) { // Fixed nodes should alias the canonical node for the corresponding register. @@ -1303,7 +1303,7 @@ void ColoringIteration::FindCoalesceOpportunities() { // Coalesce siblings. LiveInterval* next_sibling = interval->GetNextSibling(); if (next_sibling != nullptr && interval->GetEnd() == next_sibling->GetStart()) { - auto it = interval_node_map_.Find(next_sibling); + auto it = interval_node_map_.find(next_sibling); if (it != interval_node_map_.end()) { InterferenceNode* sibling_node = it->second; CreateCoalesceOpportunity(node, @@ -1318,7 +1318,7 @@ void ColoringIteration::FindCoalesceOpportunities() { if (parent->HasRegister() && parent->GetNextSibling() == interval && parent->GetEnd() == interval->GetStart()) { - auto it = interval_node_map_.Find(parent); + auto it = interval_node_map_.find(parent); if (it != interval_node_map_.end()) { InterferenceNode* parent_node = it->second; CreateCoalesceOpportunity(node, @@ -1341,7 +1341,7 @@ void ColoringIteration::FindCoalesceOpportunities() { size_t position = predecessor->GetLifetimeEnd() - 1; LiveInterval* existing = interval->GetParent()->GetSiblingAt(position); if (existing != nullptr) { - auto it = interval_node_map_.Find(existing); + auto it = interval_node_map_.find(existing); if (it != interval_node_map_.end()) { InterferenceNode* existing_node = it->second; CreateCoalesceOpportunity(node, @@ -1364,7 +1364,7 @@ void ColoringIteration::FindCoalesceOpportunities() { size_t position = predecessors[i]->GetLifetimeEnd() - 1; LiveInterval* input_interval = inputs[i]->GetLiveInterval()->GetSiblingAt(position); - auto it = interval_node_map_.Find(input_interval); + auto it = interval_node_map_.find(input_interval); if (it != interval_node_map_.end()) { InterferenceNode* input_node = it->second; CreateCoalesceOpportunity(node, input_node, CoalesceKind::kPhi, position); @@ -1380,7 +1380,7 @@ void ColoringIteration::FindCoalesceOpportunities() { = defined_by->InputAt(0)->GetLiveInterval()->GetSiblingAt(interval->GetStart() - 1); // TODO: Could we consider lifetime holes here? if (input_interval->GetEnd() == interval->GetStart()) { - auto it = interval_node_map_.Find(input_interval); + auto it = interval_node_map_.find(input_interval); if (it != interval_node_map_.end()) { InterferenceNode* input_node = it->second; CreateCoalesceOpportunity(node, @@ -1407,7 +1407,7 @@ void ColoringIteration::FindCoalesceOpportunities() { LiveInterval* input_interval = inputs[i]->GetLiveInterval()->GetSiblingAt(def_point); if (input_interval != nullptr && input_interval->HasHighInterval() == interval->HasHighInterval()) { - auto it = interval_node_map_.Find(input_interval); + auto it = interval_node_map_.find(input_interval); if (it != interval_node_map_.end()) { InterferenceNode* input_node = it->second; CreateCoalesceOpportunity(node, diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc index a70b0664dcb4f67db8ea4a360ae9cfe0fb9b07c7..7144775c2b300153749c2cc4d3511799074abccf 100644 --- a/compiler/optimizing/register_allocator_test.cc +++ b/compiler/optimizing/register_allocator_test.cc @@ -40,6 +40,12 @@ using Strategy = RegisterAllocator::Strategy; class RegisterAllocatorTest : public OptimizingUnitTest { protected: + void SetUp() OVERRIDE { + // This test is using the x86 ISA. + OverrideInstructionSetFeatures(InstructionSet::kX86, "default"); + OptimizingUnitTest::SetUp(); + } + // These functions need to access private variables of LocationSummary, so we declare it // as a member of RegisterAllocatorTest, which we make a friend class. void SameAsFirstInputHint(Strategy strategy); @@ -81,9 +87,7 @@ TEST_F(RegisterAllocatorTest, test_name##_GraphColor) {\ bool RegisterAllocatorTest::Check(const std::vector& data, Strategy strategy) { HGraph* graph = CreateCFG(data); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); + x86::CodeGeneratorX86 codegen(graph, *compiler_options_); SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); liveness.Analyze(); std::unique_ptr register_allocator = @@ -98,9 +102,7 @@ bool RegisterAllocatorTest::Check(const std::vector& data, Strategy st */ TEST_F(RegisterAllocatorTest, ValidateIntervals) { HGraph* graph = CreateGraph(); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); + x86::CodeGeneratorX86 codegen(graph, *compiler_options_); ScopedArenaVector intervals(GetScopedAllocator()->Adapter()); // Test with two intervals of the same range. @@ -324,9 +326,7 @@ void RegisterAllocatorTest::Loop3(Strategy strategy) { Instruction::GOTO | 0xF900); HGraph* graph = CreateCFG(data); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); + x86::CodeGeneratorX86 codegen(graph, *compiler_options_); SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); liveness.Analyze(); std::unique_ptr register_allocator = @@ -359,9 +359,7 @@ TEST_F(RegisterAllocatorTest, FirstRegisterUse) { Instruction::RETURN_VOID); HGraph* graph = CreateCFG(data); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); + x86::CodeGeneratorX86 codegen(graph, *compiler_options_); SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); liveness.Analyze(); @@ -412,9 +410,7 @@ void RegisterAllocatorTest::DeadPhi(Strategy strategy) { HGraph* graph = CreateCFG(data); SsaDeadPhiElimination(graph).Run(); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); + x86::CodeGeneratorX86 codegen(graph, *compiler_options_); SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); liveness.Analyze(); std::unique_ptr register_allocator = @@ -438,9 +434,7 @@ TEST_F(RegisterAllocatorTest, FreeUntil) { HGraph* graph = CreateCFG(data); SsaDeadPhiElimination(graph).Run(); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); + x86::CodeGeneratorX86 codegen(graph, *compiler_options_); SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); liveness.Analyze(); RegisterAllocatorLinearScan register_allocator(GetScopedAllocator(), &codegen, liveness); @@ -566,9 +560,7 @@ void RegisterAllocatorTest::PhiHint(Strategy strategy) { { HGraph* graph = BuildIfElseWithPhi(&phi, &input1, &input2); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); + x86::CodeGeneratorX86 codegen(graph, *compiler_options_); SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); liveness.Analyze(); @@ -584,9 +576,7 @@ void RegisterAllocatorTest::PhiHint(Strategy strategy) { { HGraph* graph = BuildIfElseWithPhi(&phi, &input1, &input2); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); + x86::CodeGeneratorX86 codegen(graph, *compiler_options_); SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); liveness.Analyze(); @@ -604,9 +594,7 @@ void RegisterAllocatorTest::PhiHint(Strategy strategy) { { HGraph* graph = BuildIfElseWithPhi(&phi, &input1, &input2); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); + x86::CodeGeneratorX86 codegen(graph, *compiler_options_); SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); liveness.Analyze(); @@ -624,9 +612,7 @@ void RegisterAllocatorTest::PhiHint(Strategy strategy) { { HGraph* graph = BuildIfElseWithPhi(&phi, &input1, &input2); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); + x86::CodeGeneratorX86 codegen(graph, *compiler_options_); SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); liveness.Analyze(); @@ -689,9 +675,7 @@ void RegisterAllocatorTest::ExpectedInRegisterHint(Strategy strategy) { { HGraph* graph = BuildFieldReturn(&field, &ret); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); + x86::CodeGeneratorX86 codegen(graph, *compiler_options_); SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); liveness.Analyze(); @@ -705,9 +689,7 @@ void RegisterAllocatorTest::ExpectedInRegisterHint(Strategy strategy) { { HGraph* graph = BuildFieldReturn(&field, &ret); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); + x86::CodeGeneratorX86 codegen(graph, *compiler_options_); SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); liveness.Analyze(); @@ -761,9 +743,7 @@ void RegisterAllocatorTest::SameAsFirstInputHint(Strategy strategy) { { HGraph* graph = BuildTwoSubs(&first_sub, &second_sub); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); + x86::CodeGeneratorX86 codegen(graph, *compiler_options_); SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); liveness.Analyze(); @@ -778,9 +758,7 @@ void RegisterAllocatorTest::SameAsFirstInputHint(Strategy strategy) { { HGraph* graph = BuildTwoSubs(&first_sub, &second_sub); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); + x86::CodeGeneratorX86 codegen(graph, *compiler_options_); SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); liveness.Analyze(); @@ -834,9 +812,7 @@ HGraph* RegisterAllocatorTest::BuildDiv(HInstruction** div) { void RegisterAllocatorTest::ExpectedExactInRegisterAndSameOutputHint(Strategy strategy) { HInstruction *div; HGraph* graph = BuildDiv(&div); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); + x86::CodeGeneratorX86 codegen(graph, *compiler_options_); SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); liveness.Analyze(); @@ -934,9 +910,7 @@ TEST_F(RegisterAllocatorTest, SpillInactive) { new (GetAllocator()) LocationSummary(fourth->GetDefinedBy(), LocationSummary::kNoCall); locations->SetOut(Location::RequiresRegister()); - std::unique_ptr features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); + x86::CodeGeneratorX86 codegen(graph, *compiler_options_); SsaLivenessAnalysis liveness(graph, &codegen, GetScopedAllocator()); // Populate the instructions in the liveness object, to please the register allocator. for (size_t i = 0; i < 32; ++i) { diff --git a/compiler/optimizing/scheduler.cc b/compiler/optimizing/scheduler.cc index bb28d50b5693465d307eca672744b8f50134217e..588ea03d697a8789fe4f443be1e02efa6d9e2b34 100644 --- a/compiler/optimizing/scheduler.cc +++ b/compiler/optimizing/scheduler.cc @@ -70,19 +70,19 @@ static bool MayHaveReorderingDependency(SideEffects node, SideEffects other) { return false; } -size_t SchedulingGraph::ArrayAccessHeapLocation(HInstruction* array, HInstruction* index) const { +size_t SchedulingGraph::ArrayAccessHeapLocation(HInstruction* instruction) const { DCHECK(heap_location_collector_ != nullptr); - size_t heap_loc = heap_location_collector_->GetArrayHeapLocation(array, index); + size_t heap_loc = heap_location_collector_->GetArrayHeapLocation(instruction); // This array access should be analyzed and added to HeapLocationCollector before. DCHECK(heap_loc != HeapLocationCollector::kHeapLocationNotFound); return heap_loc; } -bool SchedulingGraph::ArrayAccessMayAlias(const HInstruction* node, - const HInstruction* other) const { +bool SchedulingGraph::ArrayAccessMayAlias(HInstruction* node, + HInstruction* other) const { DCHECK(heap_location_collector_ != nullptr); - size_t node_heap_loc = ArrayAccessHeapLocation(node->InputAt(0), node->InputAt(1)); - size_t other_heap_loc = ArrayAccessHeapLocation(other->InputAt(0), other->InputAt(1)); + size_t node_heap_loc = ArrayAccessHeapLocation(node); + size_t other_heap_loc = ArrayAccessHeapLocation(other); // For example: arr[0] and arr[0] if (node_heap_loc == other_heap_loc) { @@ -194,8 +194,8 @@ bool SchedulingGraph::FieldAccessMayAlias(const HInstruction* node, return true; } -bool SchedulingGraph::HasMemoryDependency(const HInstruction* node, - const HInstruction* other) const { +bool SchedulingGraph::HasMemoryDependency(HInstruction* node, + HInstruction* other) const { if (!MayHaveReorderingDependency(node->GetSideEffects(), other->GetSideEffects())) { return false; } @@ -264,8 +264,8 @@ bool SchedulingGraph::HasExceptionDependency(const HInstruction* node, // Check whether `node` depends on `other`, taking into account `SideEffect` // information and `CanThrow` information. -bool SchedulingGraph::HasSideEffectDependency(const HInstruction* node, - const HInstruction* other) const { +bool SchedulingGraph::HasSideEffectDependency(HInstruction* node, + HInstruction* other) const { if (HasMemoryDependency(node, other)) { return true; } @@ -667,7 +667,8 @@ bool HScheduler::IsSchedulable(const HInstruction* instruction) const { // HUnaryOperation (or HBinaryOperation), check in debug mode that we have // the exhaustive lists here. if (instruction->IsUnaryOperation()) { - DCHECK(instruction->IsBooleanNot() || + DCHECK(instruction->IsAbs() || + instruction->IsBooleanNot() || instruction->IsNot() || instruction->IsNeg()) << "unexpected instruction " << instruction->DebugName(); return true; @@ -678,6 +679,8 @@ bool HScheduler::IsSchedulable(const HInstruction* instruction) const { instruction->IsCompare() || instruction->IsCondition() || instruction->IsDiv() || + instruction->IsMin() || + instruction->IsMax() || instruction->IsMul() || instruction->IsOr() || instruction->IsRem() || @@ -771,7 +774,7 @@ bool HScheduler::IsSchedulingBarrier(const HInstruction* instr) const { instr->IsSuspendCheck(); } -void HInstructionScheduling::Run(bool only_optimize_loop_blocks, +bool HInstructionScheduling::Run(bool only_optimize_loop_blocks, bool schedule_randomly) { #if defined(ART_ENABLE_CODEGEN_arm64) || defined(ART_ENABLE_CODEGEN_arm) // Phase-local allocator that allocates scheduler internal data structures like @@ -811,6 +814,7 @@ void HInstructionScheduling::Run(bool only_optimize_loop_blocks, default: break; } + return true; } } // namespace art diff --git a/compiler/optimizing/scheduler.h b/compiler/optimizing/scheduler.h index dfa077f7deac31f7499f00c8605f5f227007ef01..c7683e04a73d6b23bc9128ea2907703428f5ba2b 100644 --- a/compiler/optimizing/scheduler.h +++ b/compiler/optimizing/scheduler.h @@ -262,14 +262,14 @@ class SchedulingGraph : public ValueObject { std::unique_ptr node( new (allocator_) SchedulingNode(instr, allocator_, is_scheduling_barrier)); SchedulingNode* result = node.get(); - nodes_map_.Insert(std::make_pair(instr, std::move(node))); + nodes_map_.insert(std::make_pair(instr, std::move(node))); contains_scheduling_barrier_ |= is_scheduling_barrier; AddDependencies(instr, is_scheduling_barrier); return result; } void Clear() { - nodes_map_.Clear(); + nodes_map_.clear(); contains_scheduling_barrier_ = false; } @@ -278,7 +278,7 @@ class SchedulingGraph : public ValueObject { } SchedulingNode* GetNode(const HInstruction* instr) const { - auto it = nodes_map_.Find(instr); + auto it = nodes_map_.find(instr); if (it == nodes_map_.end()) { return nullptr; } else { @@ -294,7 +294,7 @@ class SchedulingGraph : public ValueObject { bool HasImmediateOtherDependency(const HInstruction* node, const HInstruction* other) const; size_t Size() const { - return nodes_map_.Size(); + return nodes_map_.size(); } // Dump the scheduling graph, in dot file format, appending it to the file @@ -310,12 +310,12 @@ class SchedulingGraph : public ValueObject { void AddOtherDependency(SchedulingNode* node, SchedulingNode* dependency) { AddDependency(node, dependency, /*is_data_dependency*/false); } - bool HasMemoryDependency(const HInstruction* node, const HInstruction* other) const; + bool HasMemoryDependency(HInstruction* node, HInstruction* other) const; bool HasExceptionDependency(const HInstruction* node, const HInstruction* other) const; - bool HasSideEffectDependency(const HInstruction* node, const HInstruction* other) const; - bool ArrayAccessMayAlias(const HInstruction* node, const HInstruction* other) const; + bool HasSideEffectDependency(HInstruction* node, HInstruction* other) const; + bool ArrayAccessMayAlias(HInstruction* node, HInstruction* other) const; bool FieldAccessMayAlias(const HInstruction* node, const HInstruction* other) const; - size_t ArrayAccessHeapLocation(HInstruction* array, HInstruction* index) const; + size_t ArrayAccessHeapLocation(HInstruction* instruction) const; size_t FieldAccessHeapLocation(HInstruction* obj, const FieldInfo* field) const; // Add dependencies nodes for the given `HInstruction`: inputs, environments, and side-effects. @@ -508,10 +508,11 @@ class HInstructionScheduling : public HOptimization { codegen_(cg), instruction_set_(instruction_set) {} - void Run() { - Run(/*only_optimize_loop_blocks*/ true, /*schedule_randomly*/ false); + bool Run() OVERRIDE { + return Run(/*only_optimize_loop_blocks*/ true, /*schedule_randomly*/ false); } - void Run(bool only_optimize_loop_blocks, bool schedule_randomly); + + bool Run(bool only_optimize_loop_blocks, bool schedule_randomly); static constexpr const char* kInstructionSchedulingPassName = "scheduler"; diff --git a/compiler/optimizing/scheduler_arm64.h b/compiler/optimizing/scheduler_arm64.h index f71cb5b78408ae5082a076a2d603aec237c4ebaa..4f394d5e16db3268c8b39dc01e481916f6ae9288 100644 --- a/compiler/optimizing/scheduler_arm64.h +++ b/compiler/optimizing/scheduler_arm64.h @@ -68,12 +68,10 @@ class SchedulingLatencyVisitorARM64 : public SchedulingLatencyVisitor { M(ArrayGet , unused) \ M(ArrayLength , unused) \ M(ArraySet , unused) \ - M(BinaryOperation , unused) \ M(BoundsCheck , unused) \ M(Div , unused) \ M(InstanceFieldGet , unused) \ M(InstanceOf , unused) \ - M(Invoke , unused) \ M(LoadString , unused) \ M(Mul , unused) \ M(NewArray , unused) \ @@ -108,6 +106,10 @@ class SchedulingLatencyVisitorARM64 : public SchedulingLatencyVisitor { M(VecLoad , unused) \ M(VecStore , unused) +#define FOR_EACH_SCHEDULED_ABSTRACT_INSTRUCTION(M) \ + M(BinaryOperation , unused) \ + M(Invoke , unused) + #define FOR_EACH_SCHEDULED_SHARED_INSTRUCTION(M) \ M(BitwiseNegatedRight, unused) \ M(MultiplyAccumulate, unused) \ @@ -119,6 +121,7 @@ class SchedulingLatencyVisitorARM64 : public SchedulingLatencyVisitor { void Visit##type(H##type* instruction) OVERRIDE; FOR_EACH_SCHEDULED_COMMON_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_SCHEDULED_ABSTRACT_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) FOR_EACH_SCHEDULED_SHARED_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION) diff --git a/compiler/optimizing/scheduler_test.cc b/compiler/optimizing/scheduler_test.cc index fb15fc8975134ddf73b63baefb4b687540fc0624..7079e07ae12ad1c6068cc0b31cc0972071c90f52 100644 --- a/compiler/optimizing/scheduler_test.cc +++ b/compiler/optimizing/scheduler_test.cc @@ -192,7 +192,9 @@ class SchedulerTest : public OptimizingUnitTest { HInstructionScheduling scheduling(graph, target_config.GetInstructionSet()); scheduling.Run(/*only_optimize_loop_blocks*/ false, /*schedule_randomly*/ true); + OverrideInstructionSetFeatures(target_config.GetInstructionSet(), "default"); RunCode(target_config, + *compiler_options_, graph, [](HGraph* graph_arg) { RemoveSuspendChecks(graph_arg); }, has_result, expected); @@ -296,38 +298,38 @@ class SchedulerTest : public OptimizingUnitTest { size_t loc2 = HeapLocationCollector::kHeapLocationNotFound; // Test side effect dependency: array[0] and array[1] - loc1 = heap_location_collector.GetArrayHeapLocation(arr, c0); - loc2 = heap_location_collector.GetArrayHeapLocation(arr, c1); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_0); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_1); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_1, arr_set_0)); // Test side effect dependency based on LSA analysis: array[i] and array[j] - loc1 = heap_location_collector.GetArrayHeapLocation(arr, i); - loc2 = heap_location_collector.GetArrayHeapLocation(arr, j); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_j); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_j, arr_set_i)); // Test side effect dependency based on LSA analysis: array[i] and array[i+0] - loc1 = heap_location_collector.GetArrayHeapLocation(arr, i); - loc2 = heap_location_collector.GetArrayHeapLocation(arr, add0); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_add0); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_add0, arr_set_i)); // Test side effect dependency based on LSA analysis: array[i] and array[i-0] - loc1 = heap_location_collector.GetArrayHeapLocation(arr, i); - loc2 = heap_location_collector.GetArrayHeapLocation(arr, sub0); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_sub0); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_sub0, arr_set_i)); // Test side effect dependency based on LSA analysis: array[i] and array[i+1] - loc1 = heap_location_collector.GetArrayHeapLocation(arr, i); - loc2 = heap_location_collector.GetArrayHeapLocation(arr, add1); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_add1); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_add1, arr_set_i)); // Test side effect dependency based on LSA analysis: array[i+1] and array[i-1] - loc1 = heap_location_collector.GetArrayHeapLocation(arr, add1); - loc2 = heap_location_collector.GetArrayHeapLocation(arr, sub1); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_add1); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_sub1); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_sub1, arr_set_add1)); diff --git a/compiler/optimizing/select_generator.cc b/compiler/optimizing/select_generator.cc index 66e51421ca3a1af9eb7c9b494d32b74c678c07a9..dcc7f77fc2417cf2dcc2b5482c7006d7b33a1dd9 100644 --- a/compiler/optimizing/select_generator.cc +++ b/compiler/optimizing/select_generator.cc @@ -16,6 +16,7 @@ #include "select_generator.h" +#include "base/scoped_arena_containers.h" #include "reference_type_propagation.h" namespace art { @@ -43,12 +44,18 @@ static bool IsSimpleBlock(HBasicBlock* block) { for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* instruction = it.Current(); if (instruction->IsControlFlow()) { - if (num_instructions > kMaxInstructionsInBranch) { - return false; - } return instruction->IsGoto() || instruction->IsReturn(); - } else if (instruction->CanBeMoved() && !instruction->HasSideEffects()) { - num_instructions++; + } else if (instruction->CanBeMoved() && + !instruction->HasSideEffects() && + !instruction->CanThrow()) { + if (instruction->IsSelect() && + instruction->AsSelect()->GetCondition()->GetBlock() == block) { + // Count one HCondition and HSelect in the same block as a single instruction. + // This enables finding nested selects. + continue; + } else if (++num_instructions > kMaxInstructionsInBranch) { + return false; // bail as soon as we exceed number of allowed instructions + } } else { return false; } @@ -85,10 +92,15 @@ static HPhi* GetSingleChangedPhi(HBasicBlock* block, size_t index1, size_t index return select_phi; } -void HSelectGenerator::Run() { +bool HSelectGenerator::Run() { + bool didSelect = false; + // Select cache with local allocator. + ScopedArenaAllocator allocator(graph_->GetArenaStack()); + ScopedArenaSafeMap cache( + std::less(), allocator.Adapter(kArenaAllocSelectGenerator)); + // Iterate in post order in the unlikely case that removing one occurrence of // the selection pattern empties a branch block of another occurrence. - // Otherwise the order does not matter. for (HBasicBlock* block : graph_->GetPostOrder()) { if (!block->EndsWithIf()) continue; @@ -97,6 +109,7 @@ void HSelectGenerator::Run() { HBasicBlock* true_block = if_instruction->IfTrueSuccessor(); HBasicBlock* false_block = if_instruction->IfFalseSuccessor(); DCHECK_NE(true_block, false_block); + if (!IsSimpleBlock(true_block) || !IsSimpleBlock(false_block) || !BlocksMergeTogether(true_block, false_block)) { @@ -107,11 +120,15 @@ void HSelectGenerator::Run() { // If the branches are not empty, move instructions in front of the If. // TODO(dbrazdil): This puts an instruction between If and its condition. // Implement moving of conditions to first users if possible. - if (!true_block->IsSingleGoto() && !true_block->IsSingleReturn()) { - true_block->GetFirstInstruction()->MoveBefore(if_instruction); + while (!true_block->IsSingleGoto() && !true_block->IsSingleReturn()) { + HInstruction* instr = true_block->GetFirstInstruction(); + DCHECK(!instr->CanThrow()); + instr->MoveBefore(if_instruction); } - if (!false_block->IsSingleGoto() && !false_block->IsSingleReturn()) { - false_block->GetFirstInstruction()->MoveBefore(if_instruction); + while (!false_block->IsSingleGoto() && !false_block->IsSingleReturn()) { + HInstruction* instr = false_block->GetFirstInstruction(); + DCHECK(!instr->CanThrow()); + instr->MoveBefore(if_instruction); } DCHECK(true_block->IsSingleGoto() || true_block->IsSingleReturn()); DCHECK(false_block->IsSingleGoto() || false_block->IsSingleReturn()); @@ -138,7 +155,8 @@ void HSelectGenerator::Run() { DCHECK(both_successors_return || phi != nullptr); // Create the Select instruction and insert it in front of the If. - HSelect* select = new (graph_->GetAllocator()) HSelect(if_instruction->InputAt(0), + HInstruction* condition = if_instruction->InputAt(0); + HSelect* select = new (graph_->GetAllocator()) HSelect(condition, true_value, false_value, if_instruction->GetDexPc()); @@ -175,12 +193,34 @@ void HSelectGenerator::Run() { MaybeRecordStat(stats_, MethodCompilationStat::kSelectGenerated); + // Very simple way of finding common subexpressions in the generated HSelect statements + // (since this runs after GVN). Lookup by condition, and reuse latest one if possible + // (due to post order, latest select is most likely replacement). If needed, we could + // improve this by e.g. using the operands in the map as well. + auto it = cache.find(condition); + if (it == cache.end()) { + cache.Put(condition, select); + } else { + // Found cached value. See if latest can replace cached in the HIR. + HSelect* cached = it->second; + DCHECK_EQ(cached->GetCondition(), select->GetCondition()); + if (cached->GetTrueValue() == select->GetTrueValue() && + cached->GetFalseValue() == select->GetFalseValue() && + select->StrictlyDominates(cached)) { + cached->ReplaceWith(select); + cached->GetBlock()->RemoveInstruction(cached); + } + it->second = select; // always cache latest + } + // No need to update dominance information, as we are simplifying // a simple diamond shape, where the join block is merged with the // entry block. Any following blocks would have had the join block // as a dominator, and `MergeWith` handles changing that to the // entry block. + didSelect = true; } + return didSelect; } } // namespace art diff --git a/compiler/optimizing/select_generator.h b/compiler/optimizing/select_generator.h index bda57fd5c87a0f3bcdea9ab5391c0d854e8690b0..d24d2264b258232c7b6bb7a28819bdd77e11fbb2 100644 --- a/compiler/optimizing/select_generator.h +++ b/compiler/optimizing/select_generator.h @@ -68,7 +68,7 @@ class HSelectGenerator : public HOptimization { OptimizingCompilerStats* stats, const char* name = kSelectGeneratorPassName); - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kSelectGeneratorPassName = "select_generator"; diff --git a/compiler/optimizing/select_generator_test.cc b/compiler/optimizing/select_generator_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..6e6549737c6049ada519c669e3ccc855b2ab851b --- /dev/null +++ b/compiler/optimizing/select_generator_test.cc @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "select_generator.h" + +#include "base/arena_allocator.h" +#include "builder.h" +#include "nodes.h" +#include "optimizing_unit_test.h" +#include "side_effects_analysis.h" + +namespace art { + +class SelectGeneratorTest : public ImprovedOptimizingUnitTest { + public: + void ConstructBasicGraphForSelect(HInstruction* instr) { + HBasicBlock* if_block = new (GetAllocator()) HBasicBlock(graph_); + HBasicBlock* then_block = new (GetAllocator()) HBasicBlock(graph_); + HBasicBlock* else_block = new (GetAllocator()) HBasicBlock(graph_); + + graph_->AddBlock(if_block); + graph_->AddBlock(then_block); + graph_->AddBlock(else_block); + + entry_block_->ReplaceSuccessor(return_block_, if_block); + + if_block->AddSuccessor(then_block); + if_block->AddSuccessor(else_block); + then_block->AddSuccessor(return_block_); + else_block->AddSuccessor(return_block_); + + HParameterValue* bool_param = new (GetAllocator()) HParameterValue(graph_->GetDexFile(), + dex::TypeIndex(0), + 1, + DataType::Type::kBool); + entry_block_->AddInstruction(bool_param); + HIntConstant* const1 = graph_->GetIntConstant(1); + + if_block->AddInstruction(new (GetAllocator()) HIf(bool_param)); + + then_block->AddInstruction(instr); + then_block->AddInstruction(new (GetAllocator()) HGoto()); + + else_block->AddInstruction(new (GetAllocator()) HGoto()); + + HPhi* phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32); + return_block_->AddPhi(phi); + phi->AddInput(instr); + phi->AddInput(const1); + } + + bool CheckGraphAndTrySelectGenerator() { + graph_->BuildDominatorTree(); + EXPECT_TRUE(CheckGraph()); + + SideEffectsAnalysis side_effects(graph_); + side_effects.Run(); + return HSelectGenerator(graph_, /*handles*/ nullptr, /*stats*/ nullptr).Run(); + } +}; + +// HDivZeroCheck might throw and should not be hoisted from the conditional to an unconditional. +TEST_F(SelectGeneratorTest, testZeroCheck) { + InitGraph(); + HDivZeroCheck* instr = new (GetAllocator()) HDivZeroCheck(parameter_, 0); + ConstructBasicGraphForSelect(instr); + + ArenaVector current_locals({parameter_, graph_->GetIntConstant(1)}, + GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(instr, ¤t_locals); + + EXPECT_FALSE(CheckGraphAndTrySelectGenerator()); +} + +// Test that SelectGenerator succeeds with HAdd. +TEST_F(SelectGeneratorTest, testAdd) { + InitGraph(); + HAdd* instr = new (GetAllocator()) HAdd(DataType::Type::kInt32, parameter_, parameter_, 0); + ConstructBasicGraphForSelect(instr); + EXPECT_TRUE(CheckGraphAndTrySelectGenerator()); +} + +} // namespace art diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index 1e49411c72deac45156306fb6757ac09096c54bc..27482ac5bfadcbb26606ead7535ea4cfcf9b3069 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -21,7 +21,6 @@ #include "base/enums.h" #include "class_linker.h" #include "code_generator.h" -#include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "driver/dex_compilation_unit.h" #include "gc/heap.h" @@ -36,21 +35,20 @@ namespace art { -void HSharpening::Run() { +bool HSharpening::Run() { // We don't care about the order of the blocks here. for (HBasicBlock* block : graph_->GetReversePostOrder()) { for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* instruction = it.Current(); if (instruction->IsInvokeStaticOrDirect()) { - SharpenInvokeStaticOrDirect(instruction->AsInvokeStaticOrDirect(), - codegen_, - compiler_driver_); + SharpenInvokeStaticOrDirect(instruction->AsInvokeStaticOrDirect(), codegen_); } // TODO: Move the sharpening of invoke-virtual/-interface/-super from HGraphBuilder // here. Rewrite it to avoid the CompilerDriver's reliance on verifier data // because we know the type better when inlining. } } + return true; } static bool IsInBootImage(ArtMethod* method) { @@ -69,21 +67,17 @@ static bool AOTCanEmbedMethod(ArtMethod* method, const CompilerOptions& options) return IsInBootImage(method) && !options.GetCompilePic(); } -static bool BootImageAOTCanEmbedMethod(ArtMethod* method, CompilerDriver* compiler_driver) { - DCHECK(compiler_driver->GetCompilerOptions().IsBootImage()); - if (!compiler_driver->GetSupportBootImageFixup()) { - return false; - } +static bool BootImageAOTCanEmbedMethod(ArtMethod* method, const CompilerOptions& compiler_options) { + DCHECK(compiler_options.IsBootImage()); ScopedObjectAccess soa(Thread::Current()); ObjPtr klass = method->GetDeclaringClass(); DCHECK(klass != nullptr); const DexFile& dex_file = klass->GetDexFile(); - return compiler_driver->IsImageClass(dex_file.StringByTypeIdx(klass->GetDexTypeIndex())); + return compiler_options.IsImageClass(dex_file.StringByTypeIdx(klass->GetDexTypeIndex())); } void HSharpening::SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, - CodeGenerator* codegen, - CompilerDriver* compiler_driver) { + CodeGenerator* codegen) { if (invoke->IsStringInit()) { // Not using the dex cache arrays. But we could still try to use a better dispatch... // TODO: Use direct_method and direct_code for the appropriate StringFactory method. @@ -110,23 +104,35 @@ void HSharpening::SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, // We don't optimize for debuggable as it would prevent us from obsoleting the method in some // situations. + const CompilerOptions& compiler_options = codegen->GetCompilerOptions(); if (callee == codegen->GetGraph()->GetArtMethod() && !codegen->GetGraph()->IsDebuggable()) { // Recursive call. method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kRecursive; code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallSelf; + } else if (compiler_options.IsBootImage()) { + if (!compiler_options.GetCompilePic()) { + // Test configuration, do not sharpen. + method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall; + } else if (BootImageAOTCanEmbedMethod(callee, compiler_options)) { + method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kBootImageLinkTimePcRelative; + } else { + // Use PC-relative access to the .bss methods array. + method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kBssEntry; + } + code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; } else if (Runtime::Current()->UseJitCompilation() || - AOTCanEmbedMethod(callee, codegen->GetCompilerOptions())) { + AOTCanEmbedMethod(callee, compiler_options)) { // JIT or on-device AOT compilation referencing a boot image method. // Use the method address directly. method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress; method_load_data = reinterpret_cast(callee); code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; - } else if (codegen->GetCompilerOptions().IsBootImage() && - BootImageAOTCanEmbedMethod(callee, compiler_driver)) { - method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kBootImageLinkTimePcRelative; + } else if (IsInBootImage(callee)) { + // Use PC-relative access to the .data.bimg.rel.ro methods array. + method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo; code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; } else { - // Use PC-relative access to the .bss methods arrays. + // Use PC-relative access to the .bss methods array. method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kBssEntry; code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; } @@ -148,7 +154,6 @@ void HSharpening::SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, HLoadClass::LoadKind HSharpening::ComputeLoadClassKind( HLoadClass* load_class, CodeGenerator* codegen, - CompilerDriver* compiler_driver, const DexCompilationUnit& dex_compilation_unit) { Handle klass = load_class->GetClass(); DCHECK(load_class->GetLoadKind() == HLoadClass::LoadKind::kRuntimeCall || @@ -172,26 +177,27 @@ HLoadClass::LoadKind HSharpening::ComputeLoadClassKind( bool is_in_boot_image = false; HLoadClass::LoadKind desired_load_kind = HLoadClass::LoadKind::kInvalid; Runtime* runtime = Runtime::Current(); - if (codegen->GetCompilerOptions().IsBootImage()) { + const CompilerOptions& compiler_options = codegen->GetCompilerOptions(); + if (compiler_options.IsBootImage()) { // Compiling boot image. Check if the class is a boot image class. DCHECK(!runtime->UseJitCompilation()); - if (!compiler_driver->GetSupportBootImageFixup()) { - // compiler_driver_test. Do not sharpen. + if (!compiler_options.GetCompilePic()) { + // Test configuration, do not sharpen. desired_load_kind = HLoadClass::LoadKind::kRuntimeCall; } else if ((klass != nullptr) && - compiler_driver->IsImageClass(dex_file.StringByTypeIdx(type_index))) { + compiler_options.IsImageClass(dex_file.StringByTypeIdx(type_index))) { is_in_boot_image = true; desired_load_kind = HLoadClass::LoadKind::kBootImageLinkTimePcRelative; } else { // Not a boot image class. - DCHECK(ContainsElement(compiler_driver->GetDexFilesForOatFile(), &dex_file)); + DCHECK(ContainsElement(compiler_options.GetDexFilesForOatFile(), &dex_file)); desired_load_kind = HLoadClass::LoadKind::kBssEntry; } } else { is_in_boot_image = (klass != nullptr) && runtime->GetHeap()->ObjectIsInBootImageSpace(klass.Get()); if (runtime->UseJitCompilation()) { - DCHECK(!codegen->GetCompilerOptions().GetCompilePic()); + DCHECK(!compiler_options.GetCompilePic()); if (is_in_boot_image) { // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787 desired_load_kind = HLoadClass::LoadKind::kBootImageAddress; @@ -207,7 +213,7 @@ HLoadClass::LoadKind HSharpening::ComputeLoadClassKind( } else if (is_in_boot_image) { // AOT app compilation, boot image class. if (codegen->GetCompilerOptions().GetCompilePic()) { - desired_load_kind = HLoadClass::LoadKind::kBootImageClassTable; + desired_load_kind = HLoadClass::LoadKind::kBootImageRelRo; } else { desired_load_kind = HLoadClass::LoadKind::kBootImageAddress; } @@ -236,10 +242,75 @@ HLoadClass::LoadKind HSharpening::ComputeLoadClassKind( return load_kind; } +static inline bool CanUseTypeCheckBitstring(ObjPtr klass, CodeGenerator* codegen) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(!klass->IsProxyClass()); + DCHECK(!klass->IsArrayClass()); + + if (Runtime::Current()->UseJitCompilation()) { + // If we're JITting, try to assign a type check bitstring (fall through). + } else if (codegen->GetCompilerOptions().IsBootImage()) { + const char* descriptor = klass->GetDexFile().StringByTypeIdx(klass->GetDexTypeIndex()); + if (!codegen->GetCompilerOptions().IsImageClass(descriptor)) { + return false; + } + // If the target is a boot image class, try to assign a type check bitstring (fall through). + // (If --force-determinism, this was already done; repeating is OK and yields the same result.) + } else { + // TODO: Use the bitstring also for AOT app compilation if the target class has a bitstring + // already assigned in the boot image. + return false; + } + + // Try to assign a type check bitstring. + MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); + if ((false) && // FIXME: Inliner does not respect CompilerDriver::IsClassToCompile() + // and we're hitting an unassigned bitstring in dex2oat_image_test. b/26687569 + kIsDebugBuild && + codegen->GetCompilerOptions().IsBootImage() && + codegen->GetCompilerOptions().IsForceDeterminism()) { + SubtypeCheckInfo::State old_state = SubtypeCheck>::GetState(klass); + CHECK(old_state == SubtypeCheckInfo::kAssigned || old_state == SubtypeCheckInfo::kOverflowed) + << klass->PrettyDescriptor() << "/" << old_state + << " in " << codegen->GetGraph()->PrettyMethod(); + } + SubtypeCheckInfo::State state = SubtypeCheck>::EnsureAssigned(klass); + return state == SubtypeCheckInfo::kAssigned; +} + +TypeCheckKind HSharpening::ComputeTypeCheckKind(ObjPtr klass, + CodeGenerator* codegen, + bool needs_access_check) { + if (klass == nullptr) { + return TypeCheckKind::kUnresolvedCheck; + } else if (klass->IsInterface()) { + return TypeCheckKind::kInterfaceCheck; + } else if (klass->IsArrayClass()) { + if (klass->GetComponentType()->IsObjectClass()) { + return TypeCheckKind::kArrayObjectCheck; + } else if (klass->CannotBeAssignedFromOtherTypes()) { + return TypeCheckKind::kExactCheck; + } else { + return TypeCheckKind::kArrayCheck; + } + } else if (klass->IsFinal()) { // TODO: Consider using bitstring for final classes. + return TypeCheckKind::kExactCheck; + } else if (kBitstringSubtypeCheckEnabled && + !needs_access_check && + CanUseTypeCheckBitstring(klass, codegen)) { + // TODO: We should not need the `!needs_access_check` check but getting rid of that + // requires rewriting some optimizations in instruction simplifier. + return TypeCheckKind::kBitstringCheck; + } else if (klass->IsAbstract()) { + return TypeCheckKind::kAbstractClassCheck; + } else { + return TypeCheckKind::kClassHierarchyCheck; + } +} + void HSharpening::ProcessLoadString( HLoadString* load_string, CodeGenerator* codegen, - CompilerDriver* compiler_driver, const DexCompilationUnit& dex_compilation_unit, VariableSizedHandleScope* handles) { DCHECK_EQ(load_string->GetLoadKind(), HLoadString::LoadKind::kRuntimeCall); @@ -258,17 +329,18 @@ void HSharpening::ProcessLoadString( : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)); ObjPtr string = nullptr; - if (codegen->GetCompilerOptions().IsBootImage()) { + const CompilerOptions& compiler_options = codegen->GetCompilerOptions(); + if (compiler_options.IsBootImage()) { // Compiling boot image. Resolve the string and allocate it if needed, to ensure // the string will be added to the boot image. DCHECK(!runtime->UseJitCompilation()); string = class_linker->ResolveString(string_index, dex_cache); CHECK(string != nullptr); - if (compiler_driver->GetSupportBootImageFixup()) { - DCHECK(ContainsElement(compiler_driver->GetDexFilesForOatFile(), &dex_file)); + if (compiler_options.GetCompilePic()) { + DCHECK(ContainsElement(compiler_options.GetDexFilesForOatFile(), &dex_file)); desired_load_kind = HLoadString::LoadKind::kBootImageLinkTimePcRelative; } else { - // compiler_driver_test. Do not sharpen. + // Test configuration, do not sharpen. desired_load_kind = HLoadString::LoadKind::kRuntimeCall; } } else if (runtime->UseJitCompilation()) { @@ -288,7 +360,7 @@ void HSharpening::ProcessLoadString( string = class_linker->LookupString(string_index, dex_cache.Get()); if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { if (codegen->GetCompilerOptions().GetCompilePic()) { - desired_load_kind = HLoadString::LoadKind::kBootImageInternTable; + desired_load_kind = HLoadString::LoadKind::kBootImageRelRo; } else { desired_load_kind = HLoadString::LoadKind::kBootImageAddress; } diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h index 6df7d6d91ed705db13635981f7b2455fdc3e3bf8..cbac361891698b5b9ee00d033d4b2ed7c4be8518 100644 --- a/compiler/optimizing/sharpening.h +++ b/compiler/optimizing/sharpening.h @@ -23,7 +23,6 @@ namespace art { class CodeGenerator; -class CompilerDriver; class DexCompilationUnit; // Optimization that tries to improve the way we dispatch methods and access types, @@ -34,38 +33,37 @@ class HSharpening : public HOptimization { public: HSharpening(HGraph* graph, CodeGenerator* codegen, - CompilerDriver* compiler_driver, const char* name = kSharpeningPassName) : HOptimization(graph, name), - codegen_(codegen), - compiler_driver_(compiler_driver) { } + codegen_(codegen) { } - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kSharpeningPassName = "sharpening"; - // Used by the builder. - static void ProcessLoadString(HLoadString* load_string, - CodeGenerator* codegen, - CompilerDriver* compiler_driver, - const DexCompilationUnit& dex_compilation_unit, - VariableSizedHandleScope* handles); + // Used by Sharpening and InstructionSimplifier. + static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, CodeGenerator* codegen); // Used by the builder and the inliner. static HLoadClass::LoadKind ComputeLoadClassKind(HLoadClass* load_class, CodeGenerator* codegen, - CompilerDriver* compiler_driver, const DexCompilationUnit& dex_compilation_unit) REQUIRES_SHARED(Locks::mutator_lock_); - // Used by Sharpening and InstructionSimplifier. - static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, - CodeGenerator* codegen, - CompilerDriver* compiler_driver); + // Used by the builder. + static TypeCheckKind ComputeTypeCheckKind(ObjPtr klass, + CodeGenerator* codegen, + bool needs_access_check) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Used by the builder. + static void ProcessLoadString(HLoadString* load_string, + CodeGenerator* codegen, + const DexCompilationUnit& dex_compilation_unit, + VariableSizedHandleScope* handles); private: CodeGenerator* codegen_; - CompilerDriver* compiler_driver_; }; } // namespace art diff --git a/compiler/optimizing/side_effects_analysis.cc b/compiler/optimizing/side_effects_analysis.cc index 6d82e8e06d0ea61084d0be2b70175084d5fda724..ba97b43de983bc40d6d5a62f3e616e898b91e6af 100644 --- a/compiler/optimizing/side_effects_analysis.cc +++ b/compiler/optimizing/side_effects_analysis.cc @@ -18,7 +18,7 @@ namespace art { -void SideEffectsAnalysis::Run() { +bool SideEffectsAnalysis::Run() { // Inlining might have created more blocks, so we need to increase the size // if needed. block_effects_.resize(graph_->GetBlocks().size()); @@ -69,6 +69,7 @@ void SideEffectsAnalysis::Run() { } } has_run_ = true; + return true; } SideEffects SideEffectsAnalysis::GetLoopEffects(HBasicBlock* block) const { diff --git a/compiler/optimizing/side_effects_analysis.h b/compiler/optimizing/side_effects_analysis.h index c0f81a9c54b0b5795bfebdd9ff82b99c95fe7676..56a01e63f177f8ab1094606716c196213f876452 100644 --- a/compiler/optimizing/side_effects_analysis.h +++ b/compiler/optimizing/side_effects_analysis.h @@ -37,7 +37,7 @@ class SideEffectsAnalysis : public HOptimization { SideEffects GetBlockEffects(HBasicBlock* block) const; // Compute side effects of individual blocks and loops. - void Run(); + bool Run(); bool HasRun() const { return has_run_; } diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index dd54468217b9e852e60f00fbfdcde64772153997..dda29a1b4bf72015bbcddd24d3f27f7955943ce2 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -440,6 +440,62 @@ static bool HasAliasInEnvironments(HInstruction* instruction) { return false; } +void SsaBuilder::ReplaceUninitializedStringPhis() { + ScopedArenaHashSet seen_instructions( + local_allocator_->Adapter(kArenaAllocGraphBuilder)); + ScopedArenaVector worklist(local_allocator_->Adapter(kArenaAllocGraphBuilder)); + + // Iterate over all inputs and uses of the phi, recursively, until all related instructions + // have been visited. + for (const auto& pair : uninitialized_string_phis_) { + HPhi* string_phi = pair.first; + HInvoke* invoke = pair.second; + worklist.push_back(string_phi); + HNewInstance* found_instance = nullptr; + do { + HInstruction* current = worklist.back(); + worklist.pop_back(); + if (seen_instructions.find(current) != seen_instructions.end()) { + continue; + } + seen_instructions.insert(current); + if (current->IsNewInstance()) { + // If it is the first time we see the allocation, replace its uses. We don't register + // it through `RemoveRedundantUninitializedStrings`, as that method makes assumption about + // aliasing and environment uses that don't hold when the string escapes to phis. + // Note that this also means we will keep the (useless) allocation. + if (found_instance == nullptr) { + found_instance = current->AsNewInstance(); + } else { + DCHECK(found_instance == current); + } + } else if (current->IsPhi()) { + // Push all inputs to the worklist. Those should be Phis or NewInstance. + for (HInstruction* input : current->GetInputs()) { + DCHECK(input->IsPhi() || input->IsNewInstance()) << input->DebugName(); + worklist.push_back(input); + } + } else { + // The verifier prevents any other DEX uses of the uninitialized string. + DCHECK(current->IsEqual() || current->IsNotEqual()); + continue; + } + current->ReplaceUsesDominatedBy(invoke, invoke); + current->ReplaceEnvUsesDominatedBy(invoke, invoke); + // Push all users to the worklist. Now that we have replaced + // the uses dominated by the invokes, the remaining users should only + // be Phi, or Equal/NotEqual. + for (const HUseListNode& use : current->GetUses()) { + HInstruction* user = use.GetUser(); + DCHECK(user->IsPhi() || user->IsEqual() || user->IsNotEqual()) << user->DebugName(); + worklist.push_back(user); + } + } while (!worklist.empty()); + seen_instructions.clear(); + DCHECK(found_instance != nullptr); + } +} + void SsaBuilder::RemoveRedundantUninitializedStrings() { if (graph_->IsDebuggable()) { // Do not perform the optimization for consistency with the interpreter @@ -488,27 +544,32 @@ void SsaBuilder::RemoveRedundantUninitializedStrings() { GraphAnalysisResult SsaBuilder::BuildSsa() { DCHECK(!graph_->IsInSsaForm()); - // 1) Propagate types of phis. At this point, phis are typed void in the general + // Replace Phis that feed in a String., as well as their aliases, with + // the actual String allocation invocation. We do this first, as the phis stored in + // the data structure might get removed from the graph in later stages during `BuildSsa`. + ReplaceUninitializedStringPhis(); + + // Propagate types of phis. At this point, phis are typed void in the general // case, or float/double/reference if we created an equivalent phi. So we need // to propagate the types across phis to give them a correct type. If a type // conflict is detected in this stage, the phi is marked dead. RunPrimitiveTypePropagation(); - // 2) Now that the correct primitive types have been assigned, we can get rid + // Now that the correct primitive types have been assigned, we can get rid // of redundant phis. Note that we cannot do this phase before type propagation, // otherwise we could get rid of phi equivalents, whose presence is a requirement // for the type propagation phase. Note that this is to satisfy statement (a) // of the SsaBuilder (see ssa_builder.h). SsaRedundantPhiElimination(graph_).Run(); - // 3) Fix the type for null constants which are part of an equality comparison. + // Fix the type for null constants which are part of an equality comparison. // We need to do this after redundant phi elimination, to ensure the only cases // that we can see are reference comparison against 0. The redundant phi // elimination ensures we do not see a phi taking two 0 constants in a HEqual // or HNotEqual. FixNullConstantType(); - // 4) Compute type of reference type instructions. The pass assumes that + // Compute type of reference type instructions. The pass assumes that // NullConstant has been fixed up. ReferenceTypePropagation(graph_, class_loader_, @@ -516,7 +577,7 @@ GraphAnalysisResult SsaBuilder::BuildSsa() { handles_, /* is_first_run */ true).Run(); - // 5) HInstructionBuilder duplicated ArrayGet instructions with ambiguous type + // HInstructionBuilder duplicated ArrayGet instructions with ambiguous type // (int/float or long/double) and marked ArraySets with ambiguous input type. // Now that RTP computed the type of the array input, the ambiguity can be // resolved and the correct equivalents kept. @@ -524,13 +585,13 @@ GraphAnalysisResult SsaBuilder::BuildSsa() { return kAnalysisFailAmbiguousArrayOp; } - // 6) Mark dead phis. This will mark phis which are not used by instructions + // Mark dead phis. This will mark phis which are not used by instructions // or other live phis. If compiling as debuggable code, phis will also be kept // live if they have an environment use. SsaDeadPhiElimination dead_phi_elimimation(graph_); dead_phi_elimimation.MarkDeadPhis(); - // 7) Make sure environments use the right phi equivalent: a phi marked dead + // Make sure environments use the right phi equivalent: a phi marked dead // can have a phi equivalent that is not dead. In that case we have to replace // it with the live equivalent because deoptimization and try/catch rely on // environments containing values of all live vregs at that point. Note that @@ -539,14 +600,14 @@ GraphAnalysisResult SsaBuilder::BuildSsa() { // environments to just reference one. FixEnvironmentPhis(); - // 8) Now that the right phis are used for the environments, we can eliminate + // Now that the right phis are used for the environments, we can eliminate // phis we do not need. Regardless of the debuggable status, this phase is /// necessary for statement (b) of the SsaBuilder (see ssa_builder.h), as well // as for the code generation, which does not deal with phis of conflicting // input types. dead_phi_elimimation.EliminateDeadPhis(); - // 9) HInstructionBuidler replaced uses of NewInstances of String with the + // HInstructionBuidler replaced uses of NewInstances of String with the // results of their corresponding StringFactory calls. Unless the String // objects are used before they are initialized, they can be replaced with // NullConstant. Note that this optimization is valid only if unsimplified diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h index 60831a9e6af9df2c99683b5c88bc387d14271db9..765544508ea2592838e8c305cc0c717846cb3ef4 100644 --- a/compiler/optimizing/ssa_builder.h +++ b/compiler/optimizing/ssa_builder.h @@ -61,7 +61,8 @@ class SsaBuilder : public ValueObject { local_allocator_(local_allocator), ambiguous_agets_(local_allocator->Adapter(kArenaAllocGraphBuilder)), ambiguous_asets_(local_allocator->Adapter(kArenaAllocGraphBuilder)), - uninitialized_strings_(local_allocator->Adapter(kArenaAllocGraphBuilder)) { + uninitialized_strings_(local_allocator->Adapter(kArenaAllocGraphBuilder)), + uninitialized_string_phis_(local_allocator->Adapter(kArenaAllocGraphBuilder)) { graph_->InitializeInexactObjectRTI(handles); } @@ -96,6 +97,10 @@ class SsaBuilder : public ValueObject { } } + void AddUninitializedStringPhi(HPhi* phi, HInvoke* invoke) { + uninitialized_string_phis_.push_back(std::make_pair(phi, invoke)); + } + private: void SetLoopHeaderPhiInputs(); void FixEnvironmentPhis(); @@ -118,6 +123,7 @@ class SsaBuilder : public ValueObject { HArrayGet* GetFloatOrDoubleEquivalentOfArrayGet(HArrayGet* aget); void RemoveRedundantUninitializedStrings(); + void ReplaceUninitializedStringPhis(); HGraph* const graph_; Handle class_loader_; @@ -131,6 +137,7 @@ class SsaBuilder : public ValueObject { ScopedArenaVector ambiguous_agets_; ScopedArenaVector ambiguous_asets_; ScopedArenaVector uninitialized_strings_; + ScopedArenaVector> uninitialized_string_phis_; DISALLOW_COPY_AND_ASSIGN(SsaBuilder); }; diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc index f6bd05269e1e27ebe56d633e2e9e6f6f93c44b1f..2f782f39fcbf77e5571837daa926ff7f51770058 100644 --- a/compiler/optimizing/ssa_liveness_analysis.cc +++ b/compiler/optimizing/ssa_liveness_analysis.cc @@ -195,14 +195,19 @@ void SsaLivenessAnalysis::ComputeLiveRanges() { // SsaLivenessAnalysis. for (size_t i = 0, e = environment->Size(); i < e; ++i) { HInstruction* instruction = environment->GetInstructionAt(i); + if (instruction == nullptr) { + continue; + } bool should_be_live = ShouldBeLiveForEnvironment(current, instruction); + // If this environment use does not keep the instruction live, it does not + // affect the live range of that instruction. if (should_be_live) { CHECK(instruction->HasSsaIndex()) << instruction->DebugName(); live_in->SetBit(instruction->GetSsaIndex()); - } - if (instruction != nullptr) { - instruction->GetLiveInterval()->AddUse( - current, environment, i, /* actual_user */ nullptr, should_be_live); + instruction->GetLiveInterval()->AddUse(current, + environment, + i, + /* actual_user */ nullptr); } } } diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index f83bb52b69cace5a82419747785711c03a66185d..83ca5bd5fac307e65765d4d819ca77d87811828f 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -300,8 +300,7 @@ class LiveInterval : public ArenaObject { void AddUse(HInstruction* instruction, HEnvironment* environment, size_t input_index, - HInstruction* actual_user = nullptr, - bool keep_alive = false) { + HInstruction* actual_user = nullptr) { bool is_environment = (environment != nullptr); LocationSummary* locations = instruction->GetLocations(); if (actual_user == nullptr) { @@ -359,12 +358,6 @@ class LiveInterval : public ArenaObject { uses_.push_front(*new_use); } - if (is_environment && !keep_alive) { - // If this environment use does not keep the instruction live, it does not - // affect the live range of that instruction. - return; - } - size_t start_block_position = instruction->GetBlock()->GetLifetimeStart(); if (first_range_ == nullptr) { // First time we see a use of that interval. @@ -1157,8 +1150,11 @@ class LiveInterval : public ArenaObject { * of an instruction that has a primitive type make the instruction live. * If the graph does not have the debuggable property, the environment * use has no effect, and may get a 'none' value after register allocation. + * (d) When compiling in OSR mode, all loops in the compiled method may be entered + * from the interpreter via SuspendCheck; such use in SuspendCheck makes the instruction + * live. * - * (b) and (c) are implemented through SsaLivenessAnalysis::ShouldBeLiveForEnvironment. + * (b), (c) and (d) are implemented through SsaLivenessAnalysis::ShouldBeLiveForEnvironment. */ class SsaLivenessAnalysis : public ValueObject { public: @@ -1259,14 +1255,18 @@ class SsaLivenessAnalysis : public ValueObject { // Returns whether `instruction` in an HEnvironment held by `env_holder` // should be kept live by the HEnvironment. static bool ShouldBeLiveForEnvironment(HInstruction* env_holder, HInstruction* instruction) { - if (instruction == nullptr) return false; + DCHECK(instruction != nullptr); // A value that's not live in compiled code may still be needed in interpreter, // due to code motion, etc. if (env_holder->IsDeoptimize()) return true; // A value live at a throwing instruction in a try block may be copied by // the exception handler to its location at the top of the catch block. if (env_holder->CanThrowIntoCatchBlock()) return true; - if (instruction->GetBlock()->GetGraph()->IsDebuggable()) return true; + HGraph* graph = instruction->GetBlock()->GetGraph(); + if (graph->IsDebuggable()) return true; + // When compiling in OSR mode, all loops in the compiled method may be entered + // from the interpreter via SuspendCheck; thus we need to preserve the environment. + if (env_holder->IsSuspendCheck() && graph->IsCompilingOsr()) return true; return instruction->GetType() == DataType::Type::kReference; } diff --git a/compiler/optimizing/ssa_liveness_analysis_test.cc b/compiler/optimizing/ssa_liveness_analysis_test.cc index b9bfbaa17394a38d1d76453337ba52fe0e72f55e..a683c698d9eddea8e1c6be86eb00012bb42d1e5c 100644 --- a/compiler/optimizing/ssa_liveness_analysis_test.cc +++ b/compiler/optimizing/ssa_liveness_analysis_test.cc @@ -28,18 +28,11 @@ namespace art { class SsaLivenessAnalysisTest : public OptimizingUnitTest { - public: - SsaLivenessAnalysisTest() - : graph_(CreateGraph()), - compiler_options_(), - instruction_set_(kRuntimeISA) { - std::string error_msg; - instruction_set_features_ = - InstructionSetFeatures::FromVariant(instruction_set_, "default", &error_msg); - codegen_ = CodeGenerator::Create(graph_, - instruction_set_, - *instruction_set_features_, - compiler_options_); + protected: + void SetUp() OVERRIDE { + OptimizingUnitTest::SetUp(); + graph_ = CreateGraph(); + codegen_ = CodeGenerator::Create(graph_, *compiler_options_); CHECK(codegen_ != nullptr) << instruction_set_ << " is not a supported target architecture."; // Create entry block. entry_ = new (GetAllocator()) HBasicBlock(graph_); @@ -57,9 +50,6 @@ class SsaLivenessAnalysisTest : public OptimizingUnitTest { } HGraph* graph_; - CompilerOptions compiler_options_; - InstructionSet instruction_set_; - std::unique_ptr instruction_set_features_; std::unique_ptr codegen_; HBasicBlock* entry_; }; @@ -134,12 +124,12 @@ TEST_F(SsaLivenessAnalysisTest, TestAput) { static const char* const expected[] = { "ranges: { [2,21) }, uses: { 15 17 21 }, { 15 19 } is_fixed: 0, is_split: 0 is_low: 0 " "is_high: 0", - "ranges: { [4,21) }, uses: { 19 21 }, { 15 19 } is_fixed: 0, is_split: 0 is_low: 0 " + "ranges: { [4,21) }, uses: { 19 21 }, { } is_fixed: 0, is_split: 0 is_low: 0 " "is_high: 0", - "ranges: { [6,21) }, uses: { 21 }, { 15 19 } is_fixed: 0, is_split: 0 is_low: 0 " + "ranges: { [6,21) }, uses: { 21 }, { } is_fixed: 0, is_split: 0 is_low: 0 " "is_high: 0", // Environment uses do not keep the non-reference argument alive. - "ranges: { [8,10) }, uses: { }, { 15 19 } is_fixed: 0, is_split: 0 is_low: 0 is_high: 0", + "ranges: { [8,10) }, uses: { }, { } is_fixed: 0, is_split: 0 is_low: 0 is_high: 0", // Environment uses keep the reference argument alive. "ranges: { [10,19) }, uses: { }, { 15 19 } is_fixed: 0, is_split: 0 is_low: 0 is_high: 0", }; @@ -207,11 +197,11 @@ TEST_F(SsaLivenessAnalysisTest, TestDeoptimize) { static const char* const expected[] = { "ranges: { [2,23) }, uses: { 15 17 23 }, { 15 21 } is_fixed: 0, is_split: 0 is_low: 0 " "is_high: 0", - "ranges: { [4,23) }, uses: { 19 23 }, { 15 21 } is_fixed: 0, is_split: 0 is_low: 0 " + "ranges: { [4,23) }, uses: { 19 23 }, { 21 } is_fixed: 0, is_split: 0 is_low: 0 " "is_high: 0", - "ranges: { [6,23) }, uses: { 23 }, { 15 21 } is_fixed: 0, is_split: 0 is_low: 0 is_high: 0", + "ranges: { [6,23) }, uses: { 23 }, { 21 } is_fixed: 0, is_split: 0 is_low: 0 is_high: 0", // Environment use in HDeoptimize keeps even the non-reference argument alive. - "ranges: { [8,21) }, uses: { }, { 15 21 } is_fixed: 0, is_split: 0 is_low: 0 is_high: 0", + "ranges: { [8,21) }, uses: { }, { 21 } is_fixed: 0, is_split: 0 is_low: 0 is_high: 0", // Environment uses keep the reference argument alive. "ranges: { [10,21) }, uses: { }, { 15 21 } is_fixed: 0, is_split: 0 is_low: 0 is_high: 0", }; diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc index cb27ded17a9a67bff1a956491b2b0aa58fab3532..5370f43b4f4e0b5b57b79634263fd700942f58ff 100644 --- a/compiler/optimizing/ssa_phi_elimination.cc +++ b/compiler/optimizing/ssa_phi_elimination.cc @@ -23,9 +23,10 @@ namespace art { -void SsaDeadPhiElimination::Run() { +bool SsaDeadPhiElimination::Run() { MarkDeadPhis(); EliminateDeadPhis(); + return true; } void SsaDeadPhiElimination::MarkDeadPhis() { @@ -122,7 +123,7 @@ void SsaDeadPhiElimination::EliminateDeadPhis() { } } -void SsaRedundantPhiElimination::Run() { +bool SsaRedundantPhiElimination::Run() { // Use local allocator for allocating memory used by this optimization. ScopedArenaAllocator allocator(graph_->GetArenaStack()); @@ -255,6 +256,7 @@ void SsaRedundantPhiElimination::Run() { current->GetBlock()->RemovePhi(current); } } + return true; } } // namespace art diff --git a/compiler/optimizing/ssa_phi_elimination.h b/compiler/optimizing/ssa_phi_elimination.h index 11d5837eb544c73c91a855db6815aed68b03deac..ee859e834c96bfd1086d5fd1b8f1d2a1fa06b832 100644 --- a/compiler/optimizing/ssa_phi_elimination.h +++ b/compiler/optimizing/ssa_phi_elimination.h @@ -31,7 +31,7 @@ class SsaDeadPhiElimination : public HOptimization { explicit SsaDeadPhiElimination(HGraph* graph) : HOptimization(graph, kSsaDeadPhiEliminationPassName) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; void MarkDeadPhis(); void EliminateDeadPhis(); @@ -53,7 +53,7 @@ class SsaRedundantPhiElimination : public HOptimization { explicit SsaRedundantPhiElimination(HGraph* graph) : HOptimization(graph, kSsaRedundantPhiEliminationPassName) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kSsaRedundantPhiEliminationPassName = "redundant_phi_elimination"; diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 7010e3f380aacfa0eb04674ebe13813f441f5e7e..a65fbcc514e598b8ec5c4e809bdba5da87dfd99d 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -16,682 +16,335 @@ #include "stack_map_stream.h" +#include + #include "art_method-inl.h" #include "base/stl_util.h" #include "dex/dex_file_types.h" #include "optimizing/optimizing_compiler.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" +#include "stack_map.h" namespace art { +constexpr static bool kVerifyStackMaps = kIsDebugBuild; + +uint32_t StackMapStream::GetStackMapNativePcOffset(size_t i) { + return StackMap::UnpackNativePc(stack_maps_[i][StackMap::kPackedNativePc], instruction_set_); +} + +void StackMapStream::SetStackMapNativePcOffset(size_t i, uint32_t native_pc_offset) { + stack_maps_[i][StackMap::kPackedNativePc] = + StackMap::PackNativePc(native_pc_offset, instruction_set_); +} + +void StackMapStream::BeginMethod(size_t frame_size_in_bytes, + size_t core_spill_mask, + size_t fp_spill_mask, + uint32_t num_dex_registers) { + DCHECK(!in_method_) << "Mismatched Begin/End calls"; + in_method_ = true; + DCHECK_EQ(frame_size_in_bytes_, 0u) << "BeginMethod was already called"; + + frame_size_in_bytes_ = frame_size_in_bytes; + core_spill_mask_ = core_spill_mask; + fp_spill_mask_ = fp_spill_mask; + num_dex_registers_ = num_dex_registers; +} + +void StackMapStream::EndMethod() { + DCHECK(in_method_) << "Mismatched Begin/End calls"; + in_method_ = false; +} + void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, uint32_t native_pc_offset, uint32_t register_mask, - BitVector* sp_mask, - uint32_t num_dex_registers, - uint8_t inlining_depth) { - DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry"; - current_entry_.dex_pc = dex_pc; - current_entry_.native_pc_code_offset = CodeOffset::FromOffset(native_pc_offset, instruction_set_); - current_entry_.register_mask = register_mask; - current_entry_.sp_mask = sp_mask; - current_entry_.inlining_depth = inlining_depth; - current_entry_.inline_infos_start_index = inline_infos_.size(); - current_entry_.stack_mask_index = 0; - current_entry_.dex_method_index = dex::kDexNoIndex; - current_entry_.dex_register_entry.num_dex_registers = num_dex_registers; - current_entry_.dex_register_entry.locations_start_index = dex_register_locations_.size(); - current_entry_.dex_register_entry.live_dex_registers_mask = nullptr; - if (num_dex_registers != 0u) { - current_entry_.dex_register_entry.live_dex_registers_mask = - ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream); - current_entry_.dex_register_entry.live_dex_registers_mask->ClearAllBits(); - } - if (sp_mask != nullptr) { - stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet()); - } - if (inlining_depth > 0) { - number_of_stack_maps_with_inline_info_++; + BitVector* stack_mask, + StackMap::Kind kind) { + DCHECK(in_method_) << "Call BeginMethod first"; + DCHECK(!in_stack_map_) << "Mismatched Begin/End calls"; + in_stack_map_ = true; + + current_stack_map_ = BitTableBuilder::Entry(); + current_stack_map_[StackMap::kKind] = static_cast(kind); + current_stack_map_[StackMap::kPackedNativePc] = + StackMap::PackNativePc(native_pc_offset, instruction_set_); + current_stack_map_[StackMap::kDexPc] = dex_pc; + if (stack_maps_.size() > 0) { + // Check that non-catch stack maps are sorted by pc. + // Catch stack maps are at the end and may be unordered. + if (stack_maps_.back()[StackMap::kKind] == StackMap::Kind::Catch) { + DCHECK(current_stack_map_[StackMap::kKind] == StackMap::Kind::Catch); + } else if (current_stack_map_[StackMap::kKind] != StackMap::Kind::Catch) { + DCHECK_LE(stack_maps_.back()[StackMap::kPackedNativePc], + current_stack_map_[StackMap::kPackedNativePc]); + } } - - // Note: dex_pc can be kNoDexPc for native method intrinsics. - if (dex_pc != dex::kDexNoIndex && (dex_pc_max_ == dex::kDexNoIndex || dex_pc_max_ < dex_pc)) { - dex_pc_max_ = dex_pc; + if (register_mask != 0) { + uint32_t shift = LeastSignificantBit(register_mask); + BitTableBuilder::Entry entry; + entry[RegisterMask::kValue] = register_mask >> shift; + entry[RegisterMask::kShift] = shift; + current_stack_map_[StackMap::kRegisterMaskIndex] = register_masks_.Dedup(&entry); + } + // The compiler assumes the bit vector will be read during PrepareForFillIn(), + // and it might modify the data before that. Therefore, just store the pointer. + // See ClearSpillSlotsFromLoopPhisInStackMap in code_generator.h. + lazy_stack_masks_.push_back(stack_mask); + current_inline_infos_.clear(); + current_dex_registers_.clear(); + expected_num_dex_registers_ = num_dex_registers_; + + if (kVerifyStackMaps) { + size_t stack_map_index = stack_maps_.size(); + // Create lambda method, which will be executed at the very end to verify data. + // Parameters and local variables will be captured(stored) by the lambda "[=]". + dchecks_.emplace_back([=](const CodeInfo& code_info) { + if (kind == StackMap::Kind::Default || kind == StackMap::Kind::OSR) { + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, + instruction_set_); + CHECK_EQ(stack_map.Row(), stack_map_index); + } else if (kind == StackMap::Kind::Catch) { + StackMap stack_map = code_info.GetCatchStackMapForDexPc(dex_pc); + CHECK_EQ(stack_map.Row(), stack_map_index); + } + StackMap stack_map = code_info.GetStackMapAt(stack_map_index); + CHECK_EQ(stack_map.GetNativePcOffset(instruction_set_), native_pc_offset); + CHECK_EQ(stack_map.GetKind(), static_cast(kind)); + CHECK_EQ(stack_map.GetDexPc(), dex_pc); + CHECK_EQ(code_info.GetRegisterMaskOf(stack_map), register_mask); + BitMemoryRegion seen_stack_mask = code_info.GetStackMaskOf(stack_map); + CHECK_GE(seen_stack_mask.size_in_bits(), stack_mask ? stack_mask->GetNumberOfBits() : 0); + for (size_t b = 0; b < seen_stack_mask.size_in_bits(); b++) { + CHECK_EQ(seen_stack_mask.LoadBit(b), stack_mask != nullptr && stack_mask->IsBitSet(b)); + } + }); } - register_mask_max_ = std::max(register_mask_max_, register_mask); - current_dex_register_ = 0; } void StackMapStream::EndStackMapEntry() { - current_entry_.dex_register_map_index = AddDexRegisterMapEntry(current_entry_.dex_register_entry); - stack_maps_.push_back(current_entry_); - current_entry_ = StackMapEntry(); -} + DCHECK(in_stack_map_) << "Mismatched Begin/End calls"; + in_stack_map_ = false; -void StackMapStream::AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value) { - if (kind != DexRegisterLocation::Kind::kNone) { - // Ensure we only use non-compressed location kind at this stage. - DCHECK(DexRegisterLocation::IsShortLocationKind(kind)) << kind; - DexRegisterLocation location(kind, value); - - // Look for Dex register `location` in the location catalog (using the - // companion hash map of locations to indices). Use its index if it - // is already in the location catalog. If not, insert it (in the - // location catalog and the hash map) and use the newly created index. - auto it = location_catalog_entries_indices_.Find(location); - if (it != location_catalog_entries_indices_.end()) { - // Retrieve the index from the hash map. - dex_register_locations_.push_back(it->second); - } else { - // Create a new entry in the location catalog and the hash map. - size_t index = location_catalog_entries_.size(); - location_catalog_entries_.push_back(location); - dex_register_locations_.push_back(index); - location_catalog_entries_indices_.Insert(std::make_pair(location, index)); - } - DexRegisterMapEntry* const entry = in_inline_frame_ - ? ¤t_inline_info_.dex_register_entry - : ¤t_entry_.dex_register_entry; - DCHECK_LT(current_dex_register_, entry->num_dex_registers); - entry->live_dex_registers_mask->SetBit(current_dex_register_); - entry->hash += (1 << - (current_dex_register_ % (sizeof(DexRegisterMapEntry::hash) * kBitsPerByte))); - entry->hash += static_cast(value); - entry->hash += static_cast(kind); + // Generate index into the InlineInfo table. + size_t inlining_depth = current_inline_infos_.size(); + if (!current_inline_infos_.empty()) { + current_inline_infos_.back()[InlineInfo::kIsLast] = InlineInfo::kLast; + current_stack_map_[StackMap::kInlineInfoIndex] = + inline_infos_.Dedup(current_inline_infos_.data(), current_inline_infos_.size()); + } + + // Generate delta-compressed dex register map. + size_t num_dex_registers = current_dex_registers_.size(); + if (!current_dex_registers_.empty()) { + DCHECK_EQ(expected_num_dex_registers_, current_dex_registers_.size()); + CreateDexRegisterMap(); } - current_dex_register_++; -} -void StackMapStream::AddInvoke(InvokeType invoke_type, uint32_t dex_method_index) { - current_entry_.invoke_type = invoke_type; - current_entry_.dex_method_index = dex_method_index; + stack_maps_.Add(current_stack_map_); + + if (kVerifyStackMaps) { + size_t stack_map_index = stack_maps_.size() - 1; + dchecks_.emplace_back([=](const CodeInfo& code_info) { + StackMap stack_map = code_info.GetStackMapAt(stack_map_index); + CHECK_EQ(stack_map.HasDexRegisterMap(), (num_dex_registers != 0)); + CHECK_EQ(stack_map.HasInlineInfo(), (inlining_depth != 0)); + CHECK_EQ(code_info.GetInlineInfosOf(stack_map).size(), inlining_depth); + }); + } } void StackMapStream::BeginInlineInfoEntry(ArtMethod* method, uint32_t dex_pc, uint32_t num_dex_registers, const DexFile* outer_dex_file) { - DCHECK(!in_inline_frame_); - in_inline_frame_ = true; + DCHECK(in_stack_map_) << "Call BeginStackMapEntry first"; + DCHECK(!in_inline_info_) << "Mismatched Begin/End calls"; + in_inline_info_ = true; + DCHECK_EQ(expected_num_dex_registers_, current_dex_registers_.size()); + + expected_num_dex_registers_ += num_dex_registers; + + BitTableBuilder::Entry entry; + entry[InlineInfo::kIsLast] = InlineInfo::kMore; + entry[InlineInfo::kDexPc] = dex_pc; + entry[InlineInfo::kNumberOfDexRegisters] = static_cast(expected_num_dex_registers_); if (EncodeArtMethodInInlineInfo(method)) { - current_inline_info_.method = method; + entry[InlineInfo::kArtMethodHi] = High32Bits(reinterpret_cast(method)); + entry[InlineInfo::kArtMethodLo] = Low32Bits(reinterpret_cast(method)); } else { if (dex_pc != static_cast(-1) && kIsDebugBuild) { ScopedObjectAccess soa(Thread::Current()); DCHECK(IsSameDexFile(*outer_dex_file, *method->GetDexFile())); } - current_inline_info_.method_index = method->GetDexMethodIndexUnchecked(); - } - current_inline_info_.dex_pc = dex_pc; - current_inline_info_.dex_register_entry.num_dex_registers = num_dex_registers; - current_inline_info_.dex_register_entry.locations_start_index = dex_register_locations_.size(); - current_inline_info_.dex_register_entry.live_dex_registers_mask = nullptr; - if (num_dex_registers != 0) { - current_inline_info_.dex_register_entry.live_dex_registers_mask = - ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream); - current_inline_info_.dex_register_entry.live_dex_registers_mask->ClearAllBits(); + uint32_t dex_method_index = method->GetDexMethodIndexUnchecked(); + entry[InlineInfo::kMethodInfoIndex] = method_infos_.Dedup({dex_method_index}); + } + current_inline_infos_.push_back(entry); + + if (kVerifyStackMaps) { + size_t stack_map_index = stack_maps_.size(); + size_t depth = current_inline_infos_.size() - 1; + dchecks_.emplace_back([=](const CodeInfo& code_info) { + StackMap stack_map = code_info.GetStackMapAt(stack_map_index); + InlineInfo inline_info = code_info.GetInlineInfosOf(stack_map)[depth]; + CHECK_EQ(inline_info.GetDexPc(), dex_pc); + bool encode_art_method = EncodeArtMethodInInlineInfo(method); + CHECK_EQ(inline_info.EncodesArtMethod(), encode_art_method); + if (encode_art_method) { + CHECK_EQ(inline_info.GetArtMethod(), method); + } else { + CHECK_EQ(method_infos_[inline_info.GetMethodInfoIndex()][0], + method->GetDexMethodIndexUnchecked()); + } + }); } - current_dex_register_ = 0; } void StackMapStream::EndInlineInfoEntry() { - current_inline_info_.dex_register_map_index = - AddDexRegisterMapEntry(current_inline_info_.dex_register_entry); - DCHECK(in_inline_frame_); - DCHECK_EQ(current_dex_register_, current_inline_info_.dex_register_entry.num_dex_registers) - << "Inline information contains less registers than expected"; - in_inline_frame_ = false; - inline_infos_.push_back(current_inline_info_); - current_inline_info_ = InlineInfoEntry(); + DCHECK(in_inline_info_) << "Mismatched Begin/End calls"; + in_inline_info_ = false; + DCHECK_EQ(expected_num_dex_registers_, current_dex_registers_.size()); } -CodeOffset StackMapStream::ComputeMaxNativePcCodeOffset() const { - CodeOffset max_native_pc_offset; - for (const StackMapEntry& entry : stack_maps_) { - max_native_pc_offset = std::max(max_native_pc_offset, entry.native_pc_code_offset); - } - return max_native_pc_offset; -} - -size_t StackMapStream::PrepareForFillIn() { - CodeInfoEncoding encoding; - encoding.dex_register_map.num_entries = 0; // TODO: Remove this field. - encoding.dex_register_map.num_bytes = ComputeDexRegisterMapsSize(); - encoding.location_catalog.num_entries = location_catalog_entries_.size(); - encoding.location_catalog.num_bytes = ComputeDexRegisterLocationCatalogSize(); - encoding.inline_info.num_entries = inline_infos_.size(); - // Must be done before calling ComputeInlineInfoEncoding since ComputeInlineInfoEncoding requires - // dex_method_index_idx to be filled in. - PrepareMethodIndices(); - ComputeInlineInfoEncoding(&encoding.inline_info.encoding, - encoding.dex_register_map.num_bytes); - CodeOffset max_native_pc_offset = ComputeMaxNativePcCodeOffset(); - // Prepare the CodeInfo variable-sized encoding. - encoding.stack_mask.encoding.num_bits = stack_mask_max_ + 1; // Need room for max element too. - encoding.stack_mask.num_entries = PrepareStackMasks(encoding.stack_mask.encoding.num_bits); - encoding.register_mask.encoding.num_bits = MinimumBitsToStore(register_mask_max_); - encoding.register_mask.num_entries = PrepareRegisterMasks(); - encoding.stack_map.num_entries = stack_maps_.size(); - encoding.stack_map.encoding.SetFromSizes( - // The stack map contains compressed native PC offsets. - max_native_pc_offset.CompressedValue(), - dex_pc_max_, - encoding.dex_register_map.num_bytes, - encoding.inline_info.num_entries, - encoding.register_mask.num_entries, - encoding.stack_mask.num_entries); - ComputeInvokeInfoEncoding(&encoding); - DCHECK_EQ(code_info_encoding_.size(), 0u); - encoding.Compress(&code_info_encoding_); - encoding.ComputeTableOffsets(); - // Compute table offsets so we can get the non header size. - DCHECK_EQ(encoding.HeaderSize(), code_info_encoding_.size()); - needed_size_ = code_info_encoding_.size() + encoding.NonHeaderSize(); - return needed_size_; -} - -size_t StackMapStream::ComputeDexRegisterLocationCatalogSize() const { - size_t size = DexRegisterLocationCatalog::kFixedSize; - for (const DexRegisterLocation& dex_register_location : location_catalog_entries_) { - size += DexRegisterLocationCatalog::EntrySize(dex_register_location); - } - return size; -} - -size_t StackMapStream::DexRegisterMapEntry::ComputeSize(size_t catalog_size) const { - // For num_dex_registers == 0u live_dex_registers_mask may be null. - if (num_dex_registers == 0u) { - return 0u; // No register map will be emitted. - } - DCHECK(live_dex_registers_mask != nullptr); - - // Size of the map in bytes. - size_t size = DexRegisterMap::kFixedSize; - // Add the live bit mask for the Dex register liveness. - size += DexRegisterMap::GetLiveBitMaskSize(num_dex_registers); - // Compute the size of the set of live Dex register entries. - size_t number_of_live_dex_registers = live_dex_registers_mask->NumSetBits(); - size_t map_entries_size_in_bits = - DexRegisterMap::SingleEntrySizeInBits(catalog_size) * number_of_live_dex_registers; - size_t map_entries_size_in_bytes = - RoundUp(map_entries_size_in_bits, kBitsPerByte) / kBitsPerByte; - size += map_entries_size_in_bytes; - return size; -} - -size_t StackMapStream::ComputeDexRegisterMapsSize() const { - size_t size = 0; - for (const DexRegisterMapEntry& entry : dex_register_entries_) { - size += entry.ComputeSize(location_catalog_entries_.size()); - } - return size; -} - -void StackMapStream::ComputeInvokeInfoEncoding(CodeInfoEncoding* encoding) { - DCHECK(encoding != nullptr); - uint32_t native_pc_max = 0; - uint16_t method_index_max = 0; - size_t invoke_infos_count = 0; - size_t invoke_type_max = 0; - for (const StackMapEntry& entry : stack_maps_) { - if (entry.dex_method_index != dex::kDexNoIndex) { - native_pc_max = std::max(native_pc_max, entry.native_pc_code_offset.CompressedValue()); - method_index_max = std::max(method_index_max, static_cast(entry.dex_method_index)); - invoke_type_max = std::max(invoke_type_max, static_cast(entry.invoke_type)); - ++invoke_infos_count; +// Create delta-compressed dex register map based on the current list of DexRegisterLocations. +// All dex registers for a stack map are concatenated - inlined registers are just appended. +void StackMapStream::CreateDexRegisterMap() { + // These are fields rather than local variables so that we can reuse the reserved memory. + temp_dex_register_mask_.ClearAllBits(); + temp_dex_register_map_.clear(); + + // Ensure that the arrays that hold previous state are big enough to be safely indexed below. + if (previous_dex_registers_.size() < current_dex_registers_.size()) { + previous_dex_registers_.resize(current_dex_registers_.size(), DexRegisterLocation::None()); + dex_register_timestamp_.resize(current_dex_registers_.size(), 0u); + } + + // Set bit in the mask for each register that has been changed since the previous stack map. + // Modified registers are stored in the catalogue and the catalogue index added to the list. + for (size_t i = 0; i < current_dex_registers_.size(); i++) { + DexRegisterLocation reg = current_dex_registers_[i]; + // Distance is difference between this index and the index of last modification. + uint32_t distance = stack_maps_.size() - dex_register_timestamp_[i]; + if (previous_dex_registers_[i] != reg || distance > kMaxDexRegisterMapSearchDistance) { + BitTableBuilder::Entry entry; + entry[DexRegisterInfo::kKind] = static_cast(reg.GetKind()); + entry[DexRegisterInfo::kPackedValue] = + DexRegisterInfo::PackValue(reg.GetKind(), reg.GetValue()); + uint32_t index = reg.IsLive() ? dex_register_catalog_.Dedup(&entry) : kNoValue; + temp_dex_register_mask_.SetBit(i); + temp_dex_register_map_.push_back({index}); + previous_dex_registers_[i] = reg; + dex_register_timestamp_[i] = stack_maps_.size(); } } - encoding->invoke_info.num_entries = invoke_infos_count; - encoding->invoke_info.encoding.SetFromSizes(native_pc_max, invoke_type_max, method_index_max); -} -void StackMapStream::ComputeInlineInfoEncoding(InlineInfoEncoding* encoding, - size_t dex_register_maps_bytes) { - uint32_t method_index_max = 0; - uint32_t dex_pc_max = dex::kDexNoIndex; - uint32_t extra_data_max = 0; - - uint32_t inline_info_index = 0; - for (const StackMapEntry& entry : stack_maps_) { - for (size_t j = 0; j < entry.inlining_depth; ++j) { - InlineInfoEntry inline_entry = inline_infos_[inline_info_index++]; - if (inline_entry.method == nullptr) { - method_index_max = std::max(method_index_max, inline_entry.dex_method_index_idx); - extra_data_max = std::max(extra_data_max, 1u); - } else { - method_index_max = std::max( - method_index_max, High32Bits(reinterpret_cast(inline_entry.method))); - extra_data_max = std::max( - extra_data_max, Low32Bits(reinterpret_cast(inline_entry.method))); + // Set the mask and map for the current StackMap (which includes inlined registers). + if (temp_dex_register_mask_.GetNumberOfBits() != 0) { + current_stack_map_[StackMap::kDexRegisterMaskIndex] = + dex_register_masks_.Dedup(temp_dex_register_mask_.GetRawStorage(), + temp_dex_register_mask_.GetNumberOfBits()); + } + if (!current_dex_registers_.empty()) { + current_stack_map_[StackMap::kDexRegisterMapIndex] = + dex_register_maps_.Dedup(temp_dex_register_map_.data(), + temp_dex_register_map_.size()); + } + + if (kVerifyStackMaps) { + size_t stack_map_index = stack_maps_.size(); + // We need to make copy of the current registers for later (when the check is run). + auto expected_dex_registers = std::make_shared>( + current_dex_registers_.begin(), current_dex_registers_.end()); + dchecks_.emplace_back([=](const CodeInfo& code_info) { + StackMap stack_map = code_info.GetStackMapAt(stack_map_index); + uint32_t expected_reg = 0; + for (DexRegisterLocation reg : code_info.GetDexRegisterMapOf(stack_map)) { + CHECK_EQ((*expected_dex_registers)[expected_reg++], reg); } - if (inline_entry.dex_pc != dex::kDexNoIndex && - (dex_pc_max == dex::kDexNoIndex || dex_pc_max < inline_entry.dex_pc)) { - dex_pc_max = inline_entry.dex_pc; + for (InlineInfo inline_info : code_info.GetInlineInfosOf(stack_map)) { + DexRegisterMap map = code_info.GetInlineDexRegisterMapOf(stack_map, inline_info); + for (DexRegisterLocation reg : map) { + CHECK_EQ((*expected_dex_registers)[expected_reg++], reg); + } } - } - } - DCHECK_EQ(inline_info_index, inline_infos_.size()); - - encoding->SetFromSizes(method_index_max, dex_pc_max, extra_data_max, dex_register_maps_bytes); -} - -size_t StackMapStream::MaybeCopyDexRegisterMap(DexRegisterMapEntry& entry, - size_t* current_offset, - MemoryRegion dex_register_locations_region) { - DCHECK(current_offset != nullptr); - if ((entry.num_dex_registers == 0) || (entry.live_dex_registers_mask->NumSetBits() == 0)) { - // No dex register map needed. - return StackMap::kNoDexRegisterMap; + CHECK_EQ(expected_reg, expected_dex_registers->size()); + }); } - if (entry.offset == DexRegisterMapEntry::kOffsetUnassigned) { - // Not already copied, need to copy and and assign an offset. - entry.offset = *current_offset; - const size_t entry_size = entry.ComputeSize(location_catalog_entries_.size()); - DexRegisterMap dex_register_map( - dex_register_locations_region.Subregion(entry.offset, entry_size)); - *current_offset += entry_size; - // Fill in the map since it was just added. - FillInDexRegisterMap(dex_register_map, - entry.num_dex_registers, - *entry.live_dex_registers_mask, - entry.locations_start_index); - } - return entry.offset; } void StackMapStream::FillInMethodInfo(MemoryRegion region) { { - MethodInfo info(region.begin(), method_indices_.size()); - for (size_t i = 0; i < method_indices_.size(); ++i) { - info.SetMethodIndex(i, method_indices_[i]); + MethodInfo info(region.begin(), method_infos_.size()); + for (size_t i = 0; i < method_infos_.size(); ++i) { + info.SetMethodIndex(i, method_infos_[i][0]); } } - if (kIsDebugBuild) { + if (kVerifyStackMaps) { // Check the data matches. MethodInfo info(region.begin()); const size_t count = info.NumMethodIndices(); - DCHECK_EQ(count, method_indices_.size()); + DCHECK_EQ(count, method_infos_.size()); for (size_t i = 0; i < count; ++i) { - DCHECK_EQ(info.GetMethodIndex(i), method_indices_[i]); - } - } -} - -void StackMapStream::FillInCodeInfo(MemoryRegion region) { - DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry"; - DCHECK_NE(0u, needed_size_) << "PrepareForFillIn not called before FillIn"; - - DCHECK_EQ(region.size(), needed_size_); - - // Note that the memory region does not have to be zeroed when we JIT code - // because we do not use the arena allocator there. - - // Write the CodeInfo header. - region.CopyFrom(0, MemoryRegion(code_info_encoding_.data(), code_info_encoding_.size())); - - CodeInfo code_info(region); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - DCHECK_EQ(encoding.stack_map.num_entries, stack_maps_.size()); - - MemoryRegion dex_register_locations_region = region.Subregion( - encoding.dex_register_map.byte_offset, - encoding.dex_register_map.num_bytes); - - // Set the Dex register location catalog. - MemoryRegion dex_register_location_catalog_region = region.Subregion( - encoding.location_catalog.byte_offset, - encoding.location_catalog.num_bytes); - DexRegisterLocationCatalog dex_register_location_catalog(dex_register_location_catalog_region); - // Offset in `dex_register_location_catalog` where to store the next - // register location. - size_t location_catalog_offset = DexRegisterLocationCatalog::kFixedSize; - for (DexRegisterLocation dex_register_location : location_catalog_entries_) { - dex_register_location_catalog.SetRegisterInfo(location_catalog_offset, dex_register_location); - location_catalog_offset += DexRegisterLocationCatalog::EntrySize(dex_register_location); - } - // Ensure we reached the end of the Dex registers location_catalog. - DCHECK_EQ(location_catalog_offset, dex_register_location_catalog_region.size()); - - ArenaBitVector empty_bitmask(allocator_, 0, /* expandable */ false, kArenaAllocStackMapStream); - uintptr_t next_dex_register_map_offset = 0; - uintptr_t next_inline_info_index = 0; - size_t invoke_info_idx = 0; - for (size_t i = 0, e = stack_maps_.size(); i < e; ++i) { - StackMap stack_map = code_info.GetStackMapAt(i, encoding); - StackMapEntry entry = stack_maps_[i]; - - stack_map.SetDexPc(encoding.stack_map.encoding, entry.dex_pc); - stack_map.SetNativePcCodeOffset(encoding.stack_map.encoding, entry.native_pc_code_offset); - stack_map.SetRegisterMaskIndex(encoding.stack_map.encoding, entry.register_mask_index); - stack_map.SetStackMaskIndex(encoding.stack_map.encoding, entry.stack_mask_index); - - size_t offset = MaybeCopyDexRegisterMap(dex_register_entries_[entry.dex_register_map_index], - &next_dex_register_map_offset, - dex_register_locations_region); - stack_map.SetDexRegisterMapOffset(encoding.stack_map.encoding, offset); - - if (entry.dex_method_index != dex::kDexNoIndex) { - InvokeInfo invoke_info(code_info.GetInvokeInfo(encoding, invoke_info_idx)); - invoke_info.SetNativePcCodeOffset(encoding.invoke_info.encoding, entry.native_pc_code_offset); - invoke_info.SetInvokeType(encoding.invoke_info.encoding, entry.invoke_type); - invoke_info.SetMethodIndexIdx(encoding.invoke_info.encoding, entry.dex_method_index_idx); - ++invoke_info_idx; - } - - // Set the inlining info. - if (entry.inlining_depth != 0) { - InlineInfo inline_info = code_info.GetInlineInfo(next_inline_info_index, encoding); - - // Fill in the index. - stack_map.SetInlineInfoIndex(encoding.stack_map.encoding, next_inline_info_index); - DCHECK_EQ(next_inline_info_index, entry.inline_infos_start_index); - next_inline_info_index += entry.inlining_depth; - - inline_info.SetDepth(encoding.inline_info.encoding, entry.inlining_depth); - DCHECK_LE(entry.inline_infos_start_index + entry.inlining_depth, inline_infos_.size()); - - for (size_t depth = 0; depth < entry.inlining_depth; ++depth) { - InlineInfoEntry inline_entry = inline_infos_[depth + entry.inline_infos_start_index]; - if (inline_entry.method != nullptr) { - inline_info.SetMethodIndexIdxAtDepth( - encoding.inline_info.encoding, - depth, - High32Bits(reinterpret_cast(inline_entry.method))); - inline_info.SetExtraDataAtDepth( - encoding.inline_info.encoding, - depth, - Low32Bits(reinterpret_cast(inline_entry.method))); - } else { - inline_info.SetMethodIndexIdxAtDepth(encoding.inline_info.encoding, - depth, - inline_entry.dex_method_index_idx); - inline_info.SetExtraDataAtDepth(encoding.inline_info.encoding, depth, 1); - } - inline_info.SetDexPcAtDepth(encoding.inline_info.encoding, depth, inline_entry.dex_pc); - size_t dex_register_map_offset = MaybeCopyDexRegisterMap( - dex_register_entries_[inline_entry.dex_register_map_index], - &next_dex_register_map_offset, - dex_register_locations_region); - inline_info.SetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding, - depth, - dex_register_map_offset); - } - } else if (encoding.stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0) { - stack_map.SetInlineInfoIndex(encoding.stack_map.encoding, StackMap::kNoInlineInfo); - } - } - - // Write stack masks table. - const size_t stack_mask_bits = encoding.stack_mask.encoding.BitSize(); - if (stack_mask_bits > 0) { - size_t stack_mask_bytes = RoundUp(stack_mask_bits, kBitsPerByte) / kBitsPerByte; - for (size_t i = 0; i < encoding.stack_mask.num_entries; ++i) { - MemoryRegion source(&stack_masks_[i * stack_mask_bytes], stack_mask_bytes); - BitMemoryRegion stack_mask = code_info.GetStackMask(i, encoding); - for (size_t bit_index = 0; bit_index < stack_mask_bits; ++bit_index) { - stack_mask.StoreBit(bit_index, source.LoadBit(bit_index)); - } - } - } - - // Write register masks table. - for (size_t i = 0; i < encoding.register_mask.num_entries; ++i) { - BitMemoryRegion register_mask = code_info.GetRegisterMask(i, encoding); - register_mask.StoreBits(0, register_masks_[i], encoding.register_mask.encoding.BitSize()); - } - - // Verify all written data in debug build. - if (kIsDebugBuild) { - CheckCodeInfo(region); - } -} - -void StackMapStream::FillInDexRegisterMap(DexRegisterMap dex_register_map, - uint32_t num_dex_registers, - const BitVector& live_dex_registers_mask, - uint32_t start_index_in_dex_register_locations) const { - dex_register_map.SetLiveBitMask(num_dex_registers, live_dex_registers_mask); - // Set the dex register location mapping data. - size_t number_of_live_dex_registers = live_dex_registers_mask.NumSetBits(); - DCHECK_LE(number_of_live_dex_registers, dex_register_locations_.size()); - DCHECK_LE(start_index_in_dex_register_locations, - dex_register_locations_.size() - number_of_live_dex_registers); - for (size_t index_in_dex_register_locations = 0; - index_in_dex_register_locations != number_of_live_dex_registers; - ++index_in_dex_register_locations) { - size_t location_catalog_entry_index = dex_register_locations_[ - start_index_in_dex_register_locations + index_in_dex_register_locations]; - dex_register_map.SetLocationCatalogEntryIndex( - index_in_dex_register_locations, - location_catalog_entry_index, - num_dex_registers, - location_catalog_entries_.size()); - } -} - -size_t StackMapStream::AddDexRegisterMapEntry(const DexRegisterMapEntry& entry) { - const size_t current_entry_index = dex_register_entries_.size(); - auto entries_it = dex_map_hash_to_stack_map_indices_.find(entry.hash); - if (entries_it == dex_map_hash_to_stack_map_indices_.end()) { - // We don't have a perfect hash functions so we need a list to collect all stack maps - // which might have the same dex register map. - ScopedArenaVector stack_map_indices(allocator_->Adapter(kArenaAllocStackMapStream)); - stack_map_indices.push_back(current_entry_index); - dex_map_hash_to_stack_map_indices_.Put(entry.hash, std::move(stack_map_indices)); - } else { - // We might have collisions, so we need to check whether or not we really have a match. - for (uint32_t test_entry_index : entries_it->second) { - if (DexRegisterMapEntryEquals(dex_register_entries_[test_entry_index], entry)) { - return test_entry_index; - } - } - entries_it->second.push_back(current_entry_index); - } - dex_register_entries_.push_back(entry); - return current_entry_index; -} - -bool StackMapStream::DexRegisterMapEntryEquals(const DexRegisterMapEntry& a, - const DexRegisterMapEntry& b) const { - if ((a.live_dex_registers_mask == nullptr) != (b.live_dex_registers_mask == nullptr)) { - return false; - } - if (a.num_dex_registers != b.num_dex_registers) { - return false; - } - if (a.num_dex_registers != 0u) { - DCHECK(a.live_dex_registers_mask != nullptr); - DCHECK(b.live_dex_registers_mask != nullptr); - if (!a.live_dex_registers_mask->Equal(b.live_dex_registers_mask)) { - return false; - } - size_t number_of_live_dex_registers = a.live_dex_registers_mask->NumSetBits(); - DCHECK_LE(number_of_live_dex_registers, dex_register_locations_.size()); - DCHECK_LE(a.locations_start_index, - dex_register_locations_.size() - number_of_live_dex_registers); - DCHECK_LE(b.locations_start_index, - dex_register_locations_.size() - number_of_live_dex_registers); - auto a_begin = dex_register_locations_.begin() + a.locations_start_index; - auto b_begin = dex_register_locations_.begin() + b.locations_start_index; - if (!std::equal(a_begin, a_begin + number_of_live_dex_registers, b_begin)) { - return false; + DCHECK_EQ(info.GetMethodIndex(i), method_infos_[i][0]); } } - return true; } -// Helper for CheckCodeInfo - check that register map has the expected content. -void StackMapStream::CheckDexRegisterMap(const CodeInfo& code_info, - const DexRegisterMap& dex_register_map, - size_t num_dex_registers, - BitVector* live_dex_registers_mask, - size_t dex_register_locations_index) const { - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - for (size_t reg = 0; reg < num_dex_registers; reg++) { - // Find the location we tried to encode. - DexRegisterLocation expected = DexRegisterLocation::None(); - if (live_dex_registers_mask->IsBitSet(reg)) { - size_t catalog_index = dex_register_locations_[dex_register_locations_index++]; - expected = location_catalog_entries_[catalog_index]; - } - // Compare to the seen location. - if (expected.GetKind() == DexRegisterLocation::Kind::kNone) { - DCHECK(!dex_register_map.IsValid() || !dex_register_map.IsDexRegisterLive(reg)) - << dex_register_map.IsValid() << " " << dex_register_map.IsDexRegisterLive(reg); - } else { - DCHECK(dex_register_map.IsDexRegisterLive(reg)); - DexRegisterLocation seen = dex_register_map.GetDexRegisterLocation( - reg, num_dex_registers, code_info, encoding); - DCHECK_EQ(expected.GetKind(), seen.GetKind()); - DCHECK_EQ(expected.GetValue(), seen.GetValue()); +size_t StackMapStream::PrepareForFillIn() { + DCHECK_EQ(out_.size(), 0u); + + // Read the stack masks now. The compiler might have updated them. + for (size_t i = 0; i < lazy_stack_masks_.size(); i++) { + BitVector* stack_mask = lazy_stack_masks_[i]; + if (stack_mask != nullptr && stack_mask->GetNumberOfBits() != 0) { + stack_maps_[i][StackMap::kStackMaskIndex] = + stack_masks_.Dedup(stack_mask->GetRawStorage(), stack_mask->GetNumberOfBits()); } } - if (num_dex_registers == 0) { - DCHECK(!dex_register_map.IsValid()); - } -} - -size_t StackMapStream::PrepareRegisterMasks() { - register_masks_.resize(stack_maps_.size(), 0u); - ScopedArenaUnorderedMap dedupe(allocator_->Adapter(kArenaAllocStackMapStream)); - for (StackMapEntry& stack_map : stack_maps_) { - const size_t index = dedupe.size(); - stack_map.register_mask_index = dedupe.emplace(stack_map.register_mask, index).first->second; - register_masks_[index] = stack_map.register_mask; - } - return dedupe.size(); -} -void StackMapStream::PrepareMethodIndices() { - CHECK(method_indices_.empty()); - method_indices_.resize(stack_maps_.size() + inline_infos_.size()); - ScopedArenaUnorderedMap dedupe(allocator_->Adapter(kArenaAllocStackMapStream)); - for (StackMapEntry& stack_map : stack_maps_) { - const size_t index = dedupe.size(); - const uint32_t method_index = stack_map.dex_method_index; - if (method_index != dex::kDexNoIndex) { - stack_map.dex_method_index_idx = dedupe.emplace(method_index, index).first->second; - method_indices_[index] = method_index; - } - } - for (InlineInfoEntry& inline_info : inline_infos_) { - const size_t index = dedupe.size(); - const uint32_t method_index = inline_info.method_index; - CHECK_NE(method_index, dex::kDexNoIndex); - inline_info.dex_method_index_idx = dedupe.emplace(method_index, index).first->second; - method_indices_[index] = method_index; - } - method_indices_.resize(dedupe.size()); + EncodeUnsignedLeb128(&out_, frame_size_in_bytes_); + EncodeUnsignedLeb128(&out_, core_spill_mask_); + EncodeUnsignedLeb128(&out_, fp_spill_mask_); + EncodeUnsignedLeb128(&out_, num_dex_registers_); + BitMemoryWriter> out(&out_, out_.size() * kBitsPerByte); + stack_maps_.Encode(out); + register_masks_.Encode(out); + stack_masks_.Encode(out); + inline_infos_.Encode(out); + dex_register_masks_.Encode(out); + dex_register_maps_.Encode(out); + dex_register_catalog_.Encode(out); + + return out_.size(); } +void StackMapStream::FillInCodeInfo(MemoryRegion region) { + DCHECK(in_stack_map_ == false) << "Mismatched Begin/End calls"; + DCHECK(in_inline_info_ == false) << "Mismatched Begin/End calls"; + DCHECK_NE(0u, out_.size()) << "PrepareForFillIn not called before FillIn"; + DCHECK_EQ(region.size(), out_.size()); -size_t StackMapStream::PrepareStackMasks(size_t entry_size_in_bits) { - // Preallocate memory since we do not want it to move (the dedup map will point into it). - const size_t byte_entry_size = RoundUp(entry_size_in_bits, kBitsPerByte) / kBitsPerByte; - stack_masks_.resize(byte_entry_size * stack_maps_.size(), 0u); - // For deduplicating we store the stack masks as byte packed for simplicity. We can bit pack later - // when copying out from stack_masks_. - ScopedArenaUnorderedMap, - MemoryRegion::ContentEquals> dedup( - stack_maps_.size(), allocator_->Adapter(kArenaAllocStackMapStream)); - for (StackMapEntry& stack_map : stack_maps_) { - size_t index = dedup.size(); - MemoryRegion stack_mask(stack_masks_.data() + index * byte_entry_size, byte_entry_size); - for (size_t i = 0; i < entry_size_in_bits; i++) { - stack_mask.StoreBit(i, stack_map.sp_mask != nullptr && stack_map.sp_mask->IsBitSet(i)); - } - stack_map.stack_mask_index = dedup.emplace(stack_mask, index).first->second; - } - return dedup.size(); -} + region.CopyFromVector(0, out_); -// Check that all StackMapStream inputs are correctly encoded by trying to read them back. -void StackMapStream::CheckCodeInfo(MemoryRegion region) const { + // Verify that we can load the CodeInfo and check some essentials. CodeInfo code_info(region); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - DCHECK_EQ(code_info.GetNumberOfStackMaps(encoding), stack_maps_.size()); - size_t invoke_info_index = 0; - for (size_t s = 0; s < stack_maps_.size(); ++s) { - const StackMap stack_map = code_info.GetStackMapAt(s, encoding); - const StackMapEncoding& stack_map_encoding = encoding.stack_map.encoding; - StackMapEntry entry = stack_maps_[s]; - - // Check main stack map fields. - DCHECK_EQ(stack_map.GetNativePcOffset(stack_map_encoding, instruction_set_), - entry.native_pc_code_offset.Uint32Value(instruction_set_)); - DCHECK_EQ(stack_map.GetDexPc(stack_map_encoding), entry.dex_pc); - DCHECK_EQ(stack_map.GetRegisterMaskIndex(stack_map_encoding), entry.register_mask_index); - DCHECK_EQ(code_info.GetRegisterMaskOf(encoding, stack_map), entry.register_mask); - const size_t num_stack_mask_bits = code_info.GetNumberOfStackMaskBits(encoding); - DCHECK_EQ(stack_map.GetStackMaskIndex(stack_map_encoding), entry.stack_mask_index); - BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, stack_map); - if (entry.sp_mask != nullptr) { - DCHECK_GE(stack_mask.size_in_bits(), entry.sp_mask->GetNumberOfBits()); - for (size_t b = 0; b < num_stack_mask_bits; b++) { - DCHECK_EQ(stack_mask.LoadBit(b), entry.sp_mask->IsBitSet(b)); - } - } else { - for (size_t b = 0; b < num_stack_mask_bits; b++) { - DCHECK_EQ(stack_mask.LoadBit(b), 0u); - } - } - if (entry.dex_method_index != dex::kDexNoIndex) { - InvokeInfo invoke_info = code_info.GetInvokeInfo(encoding, invoke_info_index); - DCHECK_EQ(invoke_info.GetNativePcOffset(encoding.invoke_info.encoding, instruction_set_), - entry.native_pc_code_offset.Uint32Value(instruction_set_)); - DCHECK_EQ(invoke_info.GetInvokeType(encoding.invoke_info.encoding), entry.invoke_type); - DCHECK_EQ(invoke_info.GetMethodIndexIdx(encoding.invoke_info.encoding), - entry.dex_method_index_idx); - invoke_info_index++; - } - CheckDexRegisterMap(code_info, - code_info.GetDexRegisterMapOf( - stack_map, encoding, entry.dex_register_entry.num_dex_registers), - entry.dex_register_entry.num_dex_registers, - entry.dex_register_entry.live_dex_registers_mask, - entry.dex_register_entry.locations_start_index); - - // Check inline info. - DCHECK_EQ(stack_map.HasInlineInfo(stack_map_encoding), (entry.inlining_depth != 0)); - if (entry.inlining_depth != 0) { - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); - DCHECK_EQ(inline_info.GetDepth(encoding.inline_info.encoding), entry.inlining_depth); - for (size_t d = 0; d < entry.inlining_depth; ++d) { - size_t inline_info_index = entry.inline_infos_start_index + d; - DCHECK_LT(inline_info_index, inline_infos_.size()); - InlineInfoEntry inline_entry = inline_infos_[inline_info_index]; - DCHECK_EQ(inline_info.GetDexPcAtDepth(encoding.inline_info.encoding, d), - inline_entry.dex_pc); - if (inline_info.EncodesArtMethodAtDepth(encoding.inline_info.encoding, d)) { - DCHECK_EQ(inline_info.GetArtMethodAtDepth(encoding.inline_info.encoding, d), - inline_entry.method); - } else { - const size_t method_index_idx = - inline_info.GetMethodIndexIdxAtDepth(encoding.inline_info.encoding, d); - DCHECK_EQ(method_index_idx, inline_entry.dex_method_index_idx); - DCHECK_EQ(method_indices_[method_index_idx], inline_entry.method_index); - } + CHECK_EQ(code_info.Size(), out_.size()); + CHECK_EQ(code_info.GetNumberOfStackMaps(), stack_maps_.size()); - CheckDexRegisterMap(code_info, - code_info.GetDexRegisterMapAtDepth( - d, - inline_info, - encoding, - inline_entry.dex_register_entry.num_dex_registers), - inline_entry.dex_register_entry.num_dex_registers, - inline_entry.dex_register_entry.live_dex_registers_mask, - inline_entry.dex_register_entry.locations_start_index); - } + // Verify all written data (usually only in debug builds). + if (kVerifyStackMaps) { + for (const auto& dcheck : dchecks_) { + dcheck(code_info); } } } size_t StackMapStream::ComputeMethodInfoSize() const { - DCHECK_NE(0u, needed_size_) << "PrepareForFillIn not called before " << __FUNCTION__; - return MethodInfo::ComputeSize(method_indices_.size()); + DCHECK_NE(0u, out_.size()) << "PrepareForFillIn not called before " << __FUNCTION__; + return MethodInfo::ComputeSize(method_infos_.size()); } } // namespace art diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index 579aabdb5f50c409f5dc6b9ebfad7e5664d97210..203c2cdf84462aa05f44bee8bcac56afb416a8b2 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -17,43 +17,20 @@ #ifndef ART_COMPILER_OPTIMIZING_STACK_MAP_STREAM_H_ #define ART_COMPILER_OPTIMIZING_STACK_MAP_STREAM_H_ +#include "base/allocator.h" +#include "base/arena_bit_vector.h" +#include "base/bit_table.h" #include "base/bit_vector-inl.h" -#include "base/hash_map.h" +#include "base/memory_region.h" #include "base/scoped_arena_containers.h" #include "base/value_object.h" -#include "memory_region.h" +#include "dex_register_location.h" #include "method_info.h" #include "nodes.h" #include "stack_map.h" namespace art { -// Helper to build art::StackMapStream::LocationCatalogEntriesIndices. -class LocationCatalogEntriesIndicesEmptyFn { - public: - void MakeEmpty(std::pair& item) const { - item.first = DexRegisterLocation::None(); - } - bool IsEmpty(const std::pair& item) const { - return item.first == DexRegisterLocation::None(); - } -}; - -// Hash function for art::StackMapStream::LocationCatalogEntriesIndices. -// This hash function does not create collisions. -class DexRegisterLocationHashFn { - public: - size_t operator()(DexRegisterLocation key) const { - // Concatenate `key`s fields to create a 64-bit value to be hashed. - int64_t kind_and_value = - (static_cast(key.kind_) << 32) | static_cast(key.value_); - return inner_hash_fn_(kind_and_value); - } - private: - std::hash inner_hash_fn_; -}; - - /** * Collects and builds stack maps for a method. All the stack maps * for a method are placed in a CodeInfo object. @@ -61,89 +38,42 @@ class DexRegisterLocationHashFn { class StackMapStream : public ValueObject { public: explicit StackMapStream(ScopedArenaAllocator* allocator, InstructionSet instruction_set) - : allocator_(allocator), - instruction_set_(instruction_set), - stack_maps_(allocator->Adapter(kArenaAllocStackMapStream)), - location_catalog_entries_(allocator->Adapter(kArenaAllocStackMapStream)), - location_catalog_entries_indices_(allocator->Adapter(kArenaAllocStackMapStream)), - dex_register_locations_(allocator->Adapter(kArenaAllocStackMapStream)), - inline_infos_(allocator->Adapter(kArenaAllocStackMapStream)), - stack_masks_(allocator->Adapter(kArenaAllocStackMapStream)), - register_masks_(allocator->Adapter(kArenaAllocStackMapStream)), - method_indices_(allocator->Adapter(kArenaAllocStackMapStream)), - dex_register_entries_(allocator->Adapter(kArenaAllocStackMapStream)), - stack_mask_max_(-1), - dex_pc_max_(kNoDexPc), - register_mask_max_(0), - number_of_stack_maps_with_inline_info_(0), - dex_map_hash_to_stack_map_indices_(std::less(), - allocator->Adapter(kArenaAllocStackMapStream)), - current_entry_(), - current_inline_info_(), - code_info_encoding_(allocator->Adapter(kArenaAllocStackMapStream)), - needed_size_(0), - current_dex_register_(0), - in_inline_frame_(false) { - stack_maps_.reserve(10); - location_catalog_entries_.reserve(4); - dex_register_locations_.reserve(10 * 4); - inline_infos_.reserve(2); - code_info_encoding_.reserve(16); + : instruction_set_(instruction_set), + stack_maps_(allocator), + register_masks_(allocator), + stack_masks_(allocator), + inline_infos_(allocator), + dex_register_masks_(allocator), + dex_register_maps_(allocator), + dex_register_catalog_(allocator), + out_(allocator->Adapter(kArenaAllocStackMapStream)), + method_infos_(allocator), + lazy_stack_masks_(allocator->Adapter(kArenaAllocStackMapStream)), + current_stack_map_(), + current_inline_infos_(allocator->Adapter(kArenaAllocStackMapStream)), + current_dex_registers_(allocator->Adapter(kArenaAllocStackMapStream)), + previous_dex_registers_(allocator->Adapter(kArenaAllocStackMapStream)), + dex_register_timestamp_(allocator->Adapter(kArenaAllocStackMapStream)), + temp_dex_register_mask_(allocator, 32, true, kArenaAllocStackMapStream), + temp_dex_register_map_(allocator->Adapter(kArenaAllocStackMapStream)) { } - // A dex register map entry for a single stack map entry, contains what registers are live as - // well as indices into the location catalog. - class DexRegisterMapEntry { - public: - static const size_t kOffsetUnassigned = -1; - - BitVector* live_dex_registers_mask; - uint32_t num_dex_registers; - size_t locations_start_index; - // Computed fields - size_t hash = 0; - size_t offset = kOffsetUnassigned; - - size_t ComputeSize(size_t catalog_size) const; - }; - - // See runtime/stack_map.h to know what these fields contain. - struct StackMapEntry { - uint32_t dex_pc; - CodeOffset native_pc_code_offset; - uint32_t register_mask; - BitVector* sp_mask; - uint8_t inlining_depth; - size_t inline_infos_start_index; - uint32_t stack_mask_index; - uint32_t register_mask_index; - DexRegisterMapEntry dex_register_entry; - size_t dex_register_map_index; - InvokeType invoke_type; - uint32_t dex_method_index; - uint32_t dex_method_index_idx; // Index into dex method index table. - }; - - struct InlineInfoEntry { - uint32_t dex_pc; // dex::kDexNoIndex for intrinsified native methods. - ArtMethod* method; - uint32_t method_index; - DexRegisterMapEntry dex_register_entry; - size_t dex_register_map_index; - uint32_t dex_method_index_idx; // Index into the dex method index table. - }; + void BeginMethod(size_t frame_size_in_bytes, + size_t core_spill_mask, + size_t fp_spill_mask, + uint32_t num_dex_registers); + void EndMethod(); void BeginStackMapEntry(uint32_t dex_pc, uint32_t native_pc_offset, - uint32_t register_mask, - BitVector* sp_mask, - uint32_t num_dex_registers, - uint8_t inlining_depth); + uint32_t register_mask = 0, + BitVector* sp_mask = nullptr, + StackMap::Kind kind = StackMap::Kind::Default); void EndStackMapEntry(); - void AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value); - - void AddInvoke(InvokeType type, uint32_t dex_method_index); + void AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value) { + current_dex_registers_.push_back(DexRegisterLocation(kind, value)); + } void BeginInlineInfoEntry(ArtMethod* method, uint32_t dex_pc, @@ -155,14 +85,8 @@ class StackMapStream : public ValueObject { return stack_maps_.size(); } - const StackMapEntry& GetStackMap(size_t i) const { - return stack_maps_[i]; - } - - void SetStackMapNativePcOffset(size_t i, uint32_t native_pc_offset) { - stack_maps_[i].native_pc_code_offset = - CodeOffset::FromOffset(native_pc_offset, instruction_set_); - } + uint32_t GetStackMapNativePcOffset(size_t i); + void SetStackMapNativePcOffset(size_t i, uint32_t native_pc_offset); // Prepares the stream to fill in a memory region. Must be called before FillIn. // Returns the size (in bytes) needed to store this stream. @@ -173,91 +97,47 @@ class StackMapStream : public ValueObject { size_t ComputeMethodInfoSize() const; private: - size_t ComputeDexRegisterLocationCatalogSize() const; - size_t ComputeDexRegisterMapsSize() const; - void ComputeInlineInfoEncoding(InlineInfoEncoding* encoding, - size_t dex_register_maps_bytes); - - CodeOffset ComputeMaxNativePcCodeOffset() const; - - // Returns the number of unique stack masks. - size_t PrepareStackMasks(size_t entry_size_in_bits); - - // Returns the number of unique register masks. - size_t PrepareRegisterMasks(); - - // Prepare and deduplicate method indices. - void PrepareMethodIndices(); - - // Deduplicate entry if possible and return the corresponding index into dex_register_entries_ - // array. If entry is not a duplicate, a new entry is added to dex_register_entries_. - size_t AddDexRegisterMapEntry(const DexRegisterMapEntry& entry); - - // Return true if the two dex register map entries are equal. - bool DexRegisterMapEntryEquals(const DexRegisterMapEntry& a, const DexRegisterMapEntry& b) const; - - // Fill in the corresponding entries of a register map. - void ComputeInvokeInfoEncoding(CodeInfoEncoding* encoding); + static constexpr uint32_t kNoValue = -1; - // Returns the index of an entry with the same dex register map as the current_entry, - // or kNoSameDexMapFound if no such entry exists. - size_t FindEntryWithTheSameDexMap(); - bool HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const; + void CreateDexRegisterMap(); - // Fill in the corresponding entries of a register map. - void FillInDexRegisterMap(DexRegisterMap dex_register_map, - uint32_t num_dex_registers, - const BitVector& live_dex_registers_mask, - uint32_t start_index_in_dex_register_locations) const; - - // Returns the offset for the dex register inside of the dex register location region. See FillIn. - // Only copies the dex register map if the offset for the entry is not already assigned. - size_t MaybeCopyDexRegisterMap(DexRegisterMapEntry& entry, - size_t* current_offset, - MemoryRegion dex_register_locations_region); - void CheckDexRegisterMap(const CodeInfo& code_info, - const DexRegisterMap& dex_register_map, - size_t num_dex_registers, - BitVector* live_dex_registers_mask, - size_t dex_register_locations_index) const; - void CheckCodeInfo(MemoryRegion region) const; - - ScopedArenaAllocator* const allocator_; const InstructionSet instruction_set_; - ScopedArenaVector stack_maps_; - - // A catalog of unique [location_kind, register_value] pairs (per method). - ScopedArenaVector location_catalog_entries_; - // Map from Dex register location catalog entries to their indices in the - // location catalog. - using LocationCatalogEntriesIndices = ScopedArenaHashMap; - LocationCatalogEntriesIndices location_catalog_entries_indices_; - - // A set of concatenated maps of Dex register locations indices to `location_catalog_entries_`. - ScopedArenaVector dex_register_locations_; - ScopedArenaVector inline_infos_; - ScopedArenaVector stack_masks_; - ScopedArenaVector register_masks_; - ScopedArenaVector method_indices_; - ScopedArenaVector dex_register_entries_; - int stack_mask_max_; - uint32_t dex_pc_max_; - uint32_t register_mask_max_; - size_t number_of_stack_maps_with_inline_info_; - - ScopedArenaSafeMap> dex_map_hash_to_stack_map_indices_; - - StackMapEntry current_entry_; - InlineInfoEntry current_inline_info_; - ScopedArenaVector code_info_encoding_; - size_t needed_size_; - uint32_t current_dex_register_; - bool in_inline_frame_; - - static constexpr uint32_t kNoSameDexMapFound = -1; + uint32_t frame_size_in_bytes_ = 0; + uint32_t core_spill_mask_ = 0; + uint32_t fp_spill_mask_ = 0; + uint32_t num_dex_registers_ = 0; + BitTableBuilder stack_maps_; + BitTableBuilder register_masks_; + BitmapTableBuilder stack_masks_; + BitTableBuilder inline_infos_; + BitmapTableBuilder dex_register_masks_; + BitTableBuilder dex_register_maps_; + BitTableBuilder dex_register_catalog_; + ScopedArenaVector out_; + + BitTableBuilderBase<1> method_infos_; + + ScopedArenaVector lazy_stack_masks_; + + // Variables which track the current state between Begin/End calls; + bool in_method_ = false; + bool in_stack_map_ = false; + bool in_inline_info_ = false; + BitTableBuilder::Entry current_stack_map_; + ScopedArenaVector::Entry> current_inline_infos_; + ScopedArenaVector current_dex_registers_; + ScopedArenaVector previous_dex_registers_; + ScopedArenaVector dex_register_timestamp_; // Stack map index of last change. + size_t expected_num_dex_registers_; + + // Temporary variables used in CreateDexRegisterMap. + // They are here so that we can reuse the reserved memory. + ArenaBitVector temp_dex_register_mask_; + ScopedArenaVector::Entry> temp_dex_register_map_; + + // A set of lambda functions to be executed at the end to verify + // the encoded data. It is generally only used in debug builds. + std::vector> dchecks_; DISALLOW_COPY_AND_ASSIGN(StackMapStream); }; diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index 7e517f34850a5c661e62c7bc7499dbeef4b9d67a..42f978988f63fc8b540fed258d32f974310b3a32 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -18,6 +18,7 @@ #include "art_method.h" #include "base/arena_bit_vector.h" +#include "base/malloc_arena_pool.h" #include "stack_map_stream.h" #include "gtest/gtest.h" @@ -28,14 +29,13 @@ namespace art { // to the given bit vector. Returns true if they are same. static bool CheckStackMask( const CodeInfo& code_info, - const CodeInfoEncoding& encoding, const StackMap& stack_map, const BitVector& bit_vector) { - BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, stack_map); - if (bit_vector.GetNumberOfBits() > encoding.stack_mask.encoding.BitSize()) { + BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map); + if (bit_vector.GetNumberOfBits() > stack_mask.size_in_bits()) { return false; } - for (size_t i = 0; i < encoding.stack_mask.encoding.BitSize(); ++i) { + for (size_t i = 0; i < stack_mask.size_in_bits(); ++i) { if (stack_mask.LoadBit(i) != bit_vector.IsBitSet(i)) { return false; } @@ -45,93 +45,71 @@ static bool CheckStackMask( using Kind = DexRegisterLocation::Kind; +constexpr static uint32_t kPcAlign = GetInstructionSetInstructionAlignment(kRuntimeISA); + TEST(StackMapTest, Test1) { - ArenaPool pool; + MallocArenaPool pool; ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stream(&allocator, kRuntimeISA); + stream.BeginMethod(32, 0, 0, 2); ArenaBitVector sp_mask(&allocator, 0, false); size_t number_of_dex_registers = 2; - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask); stream.AddDexRegisterEntry(Kind::kInStack, 0); // Short location. stream.AddDexRegisterEntry(Kind::kConstant, -2); // Short location. stream.EndStackMapEntry(); + stream.EndMethod(); size_t size = stream.PrepareForFillIn(); void* memory = allocator.Alloc(size, kArenaAllocMisc); MemoryRegion region(memory, size); stream.FillInCodeInfo(region); CodeInfo code_info(region); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - ASSERT_EQ(1u, code_info.GetNumberOfStackMaps(encoding)); + ASSERT_EQ(1u, code_info.GetNumberOfStackMaps()); - uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding); + uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(); ASSERT_EQ(2u, number_of_catalog_entries); - DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding); - // The Dex register location catalog contains: - // - one 1-byte short Dex register location, and - // - one 5-byte large Dex register location. - size_t expected_location_catalog_size = 1u + 5u; - ASSERT_EQ(expected_location_catalog_size, location_catalog.Size()); - - StackMap stack_map = code_info.GetStackMapAt(0, encoding); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding))); - ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map.encoding)); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA)); - ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(encoding, stack_map)); - - ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask)); - - ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding)); - DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); - ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); - ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); - ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); - // The Dex register map contains: - // - one 1-byte live bit mask, and - // - one 1-byte set of location catalog entry indices composed of two 2-bit values. - size_t expected_dex_register_map_size = 1u + 1u; - ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); - - ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationKind( - 0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind( - 1, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationInternalKind( - 0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind( - 1, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes( - 0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info, encoding)); - - size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( - 0, number_of_dex_registers, number_of_catalog_entries); - size_t index1 = dex_register_map.GetLocationCatalogEntryIndex( - 1, number_of_dex_registers, number_of_catalog_entries); - ASSERT_EQ(0u, index0); - ASSERT_EQ(1u, index1); - DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); - DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + + StackMap stack_map = code_info.GetStackMapAt(0); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64 * kPcAlign))); + ASSERT_EQ(0u, stack_map.GetDexPc()); + ASSERT_EQ(64u * kPcAlign, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map)); + + ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask)); + + ASSERT_TRUE(stack_map.HasDexRegisterMap()); + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map); + ASSERT_EQ(number_of_dex_registers, dex_register_map.size()); + ASSERT_TRUE(dex_register_map[0].IsLive()); + ASSERT_TRUE(dex_register_map[1].IsLive()); + ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters()); + + ASSERT_EQ(Kind::kInStack, dex_register_map[0].GetKind()); + ASSERT_EQ(Kind::kConstant, dex_register_map[1].GetKind()); + ASSERT_EQ(0, dex_register_map[0].GetStackOffsetInBytes()); + ASSERT_EQ(-2, dex_register_map[1].GetConstant()); + + DexRegisterLocation location0 = code_info.GetDexRegisterCatalogEntry(0); + DexRegisterLocation location1 = code_info.GetDexRegisterCatalogEntry(1); ASSERT_EQ(Kind::kInStack, location0.GetKind()); ASSERT_EQ(Kind::kConstant, location1.GetKind()); - ASSERT_EQ(Kind::kInStack, location0.GetInternalKind()); - ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind()); ASSERT_EQ(0, location0.GetValue()); ASSERT_EQ(-2, location1.GetValue()); - ASSERT_FALSE(stack_map.HasInlineInfo(encoding.stack_map.encoding)); + ASSERT_FALSE(stack_map.HasInlineInfo()); } TEST(StackMapTest, Test2) { - ArenaPool pool; + MallocArenaPool pool; ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stream(&allocator, kRuntimeISA); + stream.BeginMethod(32, 0, 0, 2); ArtMethod art_method; ArenaBitVector sp_mask1(&allocator, 0, true); @@ -139,7 +117,7 @@ TEST(StackMapTest, Test2) { sp_mask1.SetBit(4); size_t number_of_dex_registers = 2; size_t number_of_dex_registers_in_inline_info = 0; - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask1, number_of_dex_registers, 2); + stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask1); stream.AddDexRegisterEntry(Kind::kInStack, 0); // Short location. stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location. stream.BeginInlineInfoEntry(&art_method, 3, number_of_dex_registers_in_inline_info); @@ -151,7 +129,7 @@ TEST(StackMapTest, Test2) { ArenaBitVector sp_mask2(&allocator, 0, true); sp_mask2.SetBit(3); sp_mask2.SetBit(8); - stream.BeginStackMapEntry(1, 128, 0xFF, &sp_mask2, number_of_dex_registers, 0); + stream.BeginStackMapEntry(1, 128 * kPcAlign, 0xFF, &sp_mask2); stream.AddDexRegisterEntry(Kind::kInRegister, 18); // Short location. stream.AddDexRegisterEntry(Kind::kInFpuRegister, 3); // Short location. stream.EndStackMapEntry(); @@ -159,7 +137,7 @@ TEST(StackMapTest, Test2) { ArenaBitVector sp_mask3(&allocator, 0, true); sp_mask3.SetBit(1); sp_mask3.SetBit(5); - stream.BeginStackMapEntry(2, 192, 0xAB, &sp_mask3, number_of_dex_registers, 0); + stream.BeginStackMapEntry(2, 192 * kPcAlign, 0xAB, &sp_mask3); stream.AddDexRegisterEntry(Kind::kInRegister, 6); // Short location. stream.AddDexRegisterEntry(Kind::kInRegisterHigh, 8); // Short location. stream.EndStackMapEntry(); @@ -167,256 +145,168 @@ TEST(StackMapTest, Test2) { ArenaBitVector sp_mask4(&allocator, 0, true); sp_mask4.SetBit(6); sp_mask4.SetBit(7); - stream.BeginStackMapEntry(3, 256, 0xCD, &sp_mask4, number_of_dex_registers, 0); + stream.BeginStackMapEntry(3, 256 * kPcAlign, 0xCD, &sp_mask4); stream.AddDexRegisterEntry(Kind::kInFpuRegister, 3); // Short location, same in stack map 2. stream.AddDexRegisterEntry(Kind::kInFpuRegisterHigh, 1); // Short location. stream.EndStackMapEntry(); + stream.EndMethod(); size_t size = stream.PrepareForFillIn(); void* memory = allocator.Alloc(size, kArenaAllocMisc); MemoryRegion region(memory, size); stream.FillInCodeInfo(region); CodeInfo code_info(region); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - ASSERT_EQ(4u, code_info.GetNumberOfStackMaps(encoding)); + ASSERT_EQ(4u, code_info.GetNumberOfStackMaps()); - uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding); + uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(); ASSERT_EQ(7u, number_of_catalog_entries); - DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding); - // The Dex register location catalog contains: - // - six 1-byte short Dex register locations, and - // - one 5-byte large Dex register location. - size_t expected_location_catalog_size = 6u * 1u + 5u; - ASSERT_EQ(expected_location_catalog_size, location_catalog.Size()); // First stack map. { - StackMap stack_map = code_info.GetStackMapAt(0, encoding); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding))); - ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map.encoding)); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA)); - ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(encoding, stack_map)); - - ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask1)); - - ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding)); - DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); - ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); - ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); - ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); - // The Dex register map contains: - // - one 1-byte live bit mask, and - // - one 1-byte set of location catalog entry indices composed of two 2-bit values. - size_t expected_dex_register_map_size = 1u + 1u; - ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); - - ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationKind( - 0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind( - 1, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationInternalKind( - 0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind( - 1, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes( - 0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info, encoding)); - - size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( - 0, number_of_dex_registers, number_of_catalog_entries); - size_t index1 = dex_register_map.GetLocationCatalogEntryIndex( - 1, number_of_dex_registers, number_of_catalog_entries); - ASSERT_EQ(0u, index0); - ASSERT_EQ(1u, index1); - DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); - DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + StackMap stack_map = code_info.GetStackMapAt(0); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64 * kPcAlign))); + ASSERT_EQ(0u, stack_map.GetDexPc()); + ASSERT_EQ(64u * kPcAlign, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map)); + + ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask1)); + + ASSERT_TRUE(stack_map.HasDexRegisterMap()); + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map); + ASSERT_EQ(number_of_dex_registers, dex_register_map.size()); + ASSERT_TRUE(dex_register_map[0].IsLive()); + ASSERT_TRUE(dex_register_map[1].IsLive()); + ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters()); + + ASSERT_EQ(Kind::kInStack, dex_register_map[0].GetKind()); + ASSERT_EQ(Kind::kConstant, dex_register_map[1].GetKind()); + ASSERT_EQ(0, dex_register_map[0].GetStackOffsetInBytes()); + ASSERT_EQ(-2, dex_register_map[1].GetConstant()); + + DexRegisterLocation location0 = code_info.GetDexRegisterCatalogEntry(0); + DexRegisterLocation location1 = code_info.GetDexRegisterCatalogEntry(1); ASSERT_EQ(Kind::kInStack, location0.GetKind()); ASSERT_EQ(Kind::kConstant, location1.GetKind()); - ASSERT_EQ(Kind::kInStack, location0.GetInternalKind()); - ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind()); ASSERT_EQ(0, location0.GetValue()); ASSERT_EQ(-2, location1.GetValue()); - ASSERT_TRUE(stack_map.HasInlineInfo(encoding.stack_map.encoding)); - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); - ASSERT_EQ(2u, inline_info.GetDepth(encoding.inline_info.encoding)); - ASSERT_EQ(3u, inline_info.GetDexPcAtDepth(encoding.inline_info.encoding, 0)); - ASSERT_EQ(2u, inline_info.GetDexPcAtDepth(encoding.inline_info.encoding, 1)); - ASSERT_TRUE(inline_info.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 0)); - ASSERT_TRUE(inline_info.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 1)); + ASSERT_TRUE(stack_map.HasInlineInfo()); + auto inline_infos = code_info.GetInlineInfosOf(stack_map); + ASSERT_EQ(2u, inline_infos.size()); + ASSERT_EQ(3u, inline_infos[0].GetDexPc()); + ASSERT_EQ(2u, inline_infos[1].GetDexPc()); + ASSERT_TRUE(inline_infos[0].EncodesArtMethod()); + ASSERT_TRUE(inline_infos[1].EncodesArtMethod()); } // Second stack map. { - StackMap stack_map = code_info.GetStackMapAt(1, encoding); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1u, encoding))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(128u, encoding))); - ASSERT_EQ(1u, stack_map.GetDexPc(encoding.stack_map.encoding)); - ASSERT_EQ(128u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA)); - ASSERT_EQ(0xFFu, code_info.GetRegisterMaskOf(encoding, stack_map)); - - ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask2)); - - ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding)); - DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); - ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); - ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); - ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); - // The Dex register map contains: - // - one 1-byte live bit mask, and - // - one 1-byte set of location catalog entry indices composed of two 2-bit values. - size_t expected_dex_register_map_size = 1u + 1u; - ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); - - ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationKind( - 0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationKind( - 1, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationInternalKind( - 0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationInternalKind( - 1, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(18, dex_register_map.GetMachineRegister( - 0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(3, dex_register_map.GetMachineRegister( - 1, number_of_dex_registers, code_info, encoding)); - - size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( - 0, number_of_dex_registers, number_of_catalog_entries); - size_t index1 = dex_register_map.GetLocationCatalogEntryIndex( - 1, number_of_dex_registers, number_of_catalog_entries); - ASSERT_EQ(2u, index0); - ASSERT_EQ(3u, index1); - DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); - DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + StackMap stack_map = code_info.GetStackMapAt(1); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1u))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(128u * kPcAlign))); + ASSERT_EQ(1u, stack_map.GetDexPc()); + ASSERT_EQ(128u * kPcAlign, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(0xFFu, code_info.GetRegisterMaskOf(stack_map)); + + ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask2)); + + ASSERT_TRUE(stack_map.HasDexRegisterMap()); + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map); + ASSERT_EQ(number_of_dex_registers, dex_register_map.size()); + ASSERT_TRUE(dex_register_map[0].IsLive()); + ASSERT_TRUE(dex_register_map[1].IsLive()); + ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters()); + + ASSERT_EQ(Kind::kInRegister, dex_register_map[0].GetKind()); + ASSERT_EQ(Kind::kInFpuRegister, dex_register_map[1].GetKind()); + ASSERT_EQ(18, dex_register_map[0].GetMachineRegister()); + ASSERT_EQ(3, dex_register_map[1].GetMachineRegister()); + + DexRegisterLocation location0 = code_info.GetDexRegisterCatalogEntry(2); + DexRegisterLocation location1 = code_info.GetDexRegisterCatalogEntry(3); ASSERT_EQ(Kind::kInRegister, location0.GetKind()); ASSERT_EQ(Kind::kInFpuRegister, location1.GetKind()); - ASSERT_EQ(Kind::kInRegister, location0.GetInternalKind()); - ASSERT_EQ(Kind::kInFpuRegister, location1.GetInternalKind()); ASSERT_EQ(18, location0.GetValue()); ASSERT_EQ(3, location1.GetValue()); - ASSERT_FALSE(stack_map.HasInlineInfo(encoding.stack_map.encoding)); + ASSERT_FALSE(stack_map.HasInlineInfo()); } // Third stack map. { - StackMap stack_map = code_info.GetStackMapAt(2, encoding); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(2u, encoding))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(192u, encoding))); - ASSERT_EQ(2u, stack_map.GetDexPc(encoding.stack_map.encoding)); - ASSERT_EQ(192u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA)); - ASSERT_EQ(0xABu, code_info.GetRegisterMaskOf(encoding, stack_map)); - - ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask3)); - - ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding)); - DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); - ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); - ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); - ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); - // The Dex register map contains: - // - one 1-byte live bit mask, and - // - one 1-byte set of location catalog entry indices composed of two 2-bit values. - size_t expected_dex_register_map_size = 1u + 1u; - ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); - - ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationKind( - 0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(Kind::kInRegisterHigh, dex_register_map.GetLocationKind( - 1, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationInternalKind( - 0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(Kind::kInRegisterHigh, dex_register_map.GetLocationInternalKind( - 1, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(6, dex_register_map.GetMachineRegister( - 0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(8, dex_register_map.GetMachineRegister( - 1, number_of_dex_registers, code_info, encoding)); - - size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( - 0, number_of_dex_registers, number_of_catalog_entries); - size_t index1 = dex_register_map.GetLocationCatalogEntryIndex( - 1, number_of_dex_registers, number_of_catalog_entries); - ASSERT_EQ(4u, index0); - ASSERT_EQ(5u, index1); - DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); - DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + StackMap stack_map = code_info.GetStackMapAt(2); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(2u))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(192u * kPcAlign))); + ASSERT_EQ(2u, stack_map.GetDexPc()); + ASSERT_EQ(192u * kPcAlign, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(0xABu, code_info.GetRegisterMaskOf(stack_map)); + + ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask3)); + + ASSERT_TRUE(stack_map.HasDexRegisterMap()); + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map); + ASSERT_EQ(number_of_dex_registers, dex_register_map.size()); + ASSERT_TRUE(dex_register_map[0].IsLive()); + ASSERT_TRUE(dex_register_map[1].IsLive()); + ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters()); + + ASSERT_EQ(Kind::kInRegister, dex_register_map[0].GetKind()); + ASSERT_EQ(Kind::kInRegisterHigh, dex_register_map[1].GetKind()); + ASSERT_EQ(6, dex_register_map[0].GetMachineRegister()); + ASSERT_EQ(8, dex_register_map[1].GetMachineRegister()); + + DexRegisterLocation location0 = code_info.GetDexRegisterCatalogEntry(4); + DexRegisterLocation location1 = code_info.GetDexRegisterCatalogEntry(5); ASSERT_EQ(Kind::kInRegister, location0.GetKind()); ASSERT_EQ(Kind::kInRegisterHigh, location1.GetKind()); - ASSERT_EQ(Kind::kInRegister, location0.GetInternalKind()); - ASSERT_EQ(Kind::kInRegisterHigh, location1.GetInternalKind()); ASSERT_EQ(6, location0.GetValue()); ASSERT_EQ(8, location1.GetValue()); - ASSERT_FALSE(stack_map.HasInlineInfo(encoding.stack_map.encoding)); + ASSERT_FALSE(stack_map.HasInlineInfo()); } // Fourth stack map. { - StackMap stack_map = code_info.GetStackMapAt(3, encoding); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(3u, encoding))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(256u, encoding))); - ASSERT_EQ(3u, stack_map.GetDexPc(encoding.stack_map.encoding)); - ASSERT_EQ(256u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA)); - ASSERT_EQ(0xCDu, code_info.GetRegisterMaskOf(encoding, stack_map)); - - ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask4)); - - ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding)); - DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); - ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); - ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); - ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); - // The Dex register map contains: - // - one 1-byte live bit mask, and - // - one 1-byte set of location catalog entry indices composed of two 2-bit values. - size_t expected_dex_register_map_size = 1u + 1u; - ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); - - ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationKind( - 0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(Kind::kInFpuRegisterHigh, dex_register_map.GetLocationKind( - 1, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationInternalKind( - 0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(Kind::kInFpuRegisterHigh, dex_register_map.GetLocationInternalKind( - 1, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(3, dex_register_map.GetMachineRegister( - 0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(1, dex_register_map.GetMachineRegister( - 1, number_of_dex_registers, code_info, encoding)); - - size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( - 0, number_of_dex_registers, number_of_catalog_entries); - size_t index1 = dex_register_map.GetLocationCatalogEntryIndex( - 1, number_of_dex_registers, number_of_catalog_entries); - ASSERT_EQ(3u, index0); // Shared with second stack map. - ASSERT_EQ(6u, index1); - DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); - DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + StackMap stack_map = code_info.GetStackMapAt(3); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(3u))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(256u * kPcAlign))); + ASSERT_EQ(3u, stack_map.GetDexPc()); + ASSERT_EQ(256u * kPcAlign, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(0xCDu, code_info.GetRegisterMaskOf(stack_map)); + + ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask4)); + + ASSERT_TRUE(stack_map.HasDexRegisterMap()); + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map); + ASSERT_EQ(number_of_dex_registers, dex_register_map.size()); + ASSERT_TRUE(dex_register_map[0].IsLive()); + ASSERT_TRUE(dex_register_map[1].IsLive()); + ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters()); + + ASSERT_EQ(Kind::kInFpuRegister, dex_register_map[0].GetKind()); + ASSERT_EQ(Kind::kInFpuRegisterHigh, dex_register_map[1].GetKind()); + ASSERT_EQ(3, dex_register_map[0].GetMachineRegister()); + ASSERT_EQ(1, dex_register_map[1].GetMachineRegister()); + + DexRegisterLocation location0 = code_info.GetDexRegisterCatalogEntry(3); + DexRegisterLocation location1 = code_info.GetDexRegisterCatalogEntry(6); ASSERT_EQ(Kind::kInFpuRegister, location0.GetKind()); ASSERT_EQ(Kind::kInFpuRegisterHigh, location1.GetKind()); - ASSERT_EQ(Kind::kInFpuRegister, location0.GetInternalKind()); - ASSERT_EQ(Kind::kInFpuRegisterHigh, location1.GetInternalKind()); ASSERT_EQ(3, location0.GetValue()); ASSERT_EQ(1, location1.GetValue()); - ASSERT_FALSE(stack_map.HasInlineInfo(encoding.stack_map.encoding)); + ASSERT_FALSE(stack_map.HasInlineInfo()); } } TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { - ArenaPool pool; + MallocArenaPool pool; ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stream(&allocator, kRuntimeISA); + stream.BeginMethod(32, 0, 0, 2); ArtMethod art_method; ArenaBitVector sp_mask1(&allocator, 0, true); @@ -424,7 +314,7 @@ TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { sp_mask1.SetBit(4); const size_t number_of_dex_registers = 2; const size_t number_of_dex_registers_in_inline_info = 2; - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask1, number_of_dex_registers, 1); + stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask1); stream.AddDexRegisterEntry(Kind::kInStack, 0); // Short location. stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location. stream.BeginInlineInfoEntry(&art_method, 3, number_of_dex_registers_in_inline_info); @@ -433,338 +323,216 @@ TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { stream.EndInlineInfoEntry(); stream.EndStackMapEntry(); + stream.EndMethod(); size_t size = stream.PrepareForFillIn(); void* memory = allocator.Alloc(size, kArenaAllocMisc); MemoryRegion region(memory, size); stream.FillInCodeInfo(region); CodeInfo code_info(region); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - ASSERT_EQ(1u, code_info.GetNumberOfStackMaps(encoding)); + ASSERT_EQ(1u, code_info.GetNumberOfStackMaps()); - uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding); + uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(); ASSERT_EQ(2u, number_of_catalog_entries); - DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding); - // The Dex register location catalog contains: - // - one 1-byte short Dex register locations, and - // - one 5-byte large Dex register location. - const size_t expected_location_catalog_size = 1u + 5u; - ASSERT_EQ(expected_location_catalog_size, location_catalog.Size()); // First stack map. { - StackMap stack_map = code_info.GetStackMapAt(0, encoding); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding))); - ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map.encoding)); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA)); - ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(encoding, stack_map)); - - ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask1)); - - ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding)); - DexRegisterMap map(code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers)); - ASSERT_TRUE(map.IsDexRegisterLive(0)); - ASSERT_TRUE(map.IsDexRegisterLive(1)); - ASSERT_EQ(2u, map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); - // The Dex register map contains: - // - one 1-byte live bit mask, and - // - one 1-byte set of location catalog entry indices composed of two 2-bit values. - size_t expected_map_size = 1u + 1u; - ASSERT_EQ(expected_map_size, map.Size()); - - ASSERT_EQ(Kind::kInStack, map.GetLocationKind(0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(Kind::kConstant, - map.GetLocationKind(1, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(Kind::kInStack, - map.GetLocationInternalKind(0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(Kind::kConstantLargeValue, - map.GetLocationInternalKind(1, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(0, map.GetStackOffsetInBytes(0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(-2, map.GetConstant(1, number_of_dex_registers, code_info, encoding)); - - const size_t index0 = - map.GetLocationCatalogEntryIndex(0, number_of_dex_registers, number_of_catalog_entries); - const size_t index1 = - map.GetLocationCatalogEntryIndex(1, number_of_dex_registers, number_of_catalog_entries); - ASSERT_EQ(0u, index0); - ASSERT_EQ(1u, index1); - DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); - DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + StackMap stack_map = code_info.GetStackMapAt(0); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64 * kPcAlign))); + ASSERT_EQ(0u, stack_map.GetDexPc()); + ASSERT_EQ(64u * kPcAlign, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map)); + + ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask1)); + + ASSERT_TRUE(stack_map.HasDexRegisterMap()); + DexRegisterMap map(code_info.GetDexRegisterMapOf(stack_map)); + ASSERT_EQ(number_of_dex_registers, map.size()); + ASSERT_TRUE(map[0].IsLive()); + ASSERT_TRUE(map[1].IsLive()); + ASSERT_EQ(2u, map.GetNumberOfLiveDexRegisters()); + + ASSERT_EQ(Kind::kInStack, map[0].GetKind()); + ASSERT_EQ(Kind::kConstant, map[1].GetKind()); + ASSERT_EQ(0, map[0].GetStackOffsetInBytes()); + ASSERT_EQ(-2, map[1].GetConstant()); + + DexRegisterLocation location0 = code_info.GetDexRegisterCatalogEntry(0); + DexRegisterLocation location1 = code_info.GetDexRegisterCatalogEntry(1); ASSERT_EQ(Kind::kInStack, location0.GetKind()); ASSERT_EQ(Kind::kConstant, location1.GetKind()); - ASSERT_EQ(Kind::kInStack, location0.GetInternalKind()); - ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind()); ASSERT_EQ(0, location0.GetValue()); ASSERT_EQ(-2, location1.GetValue()); - - // Test that the inline info dex register map deduplicated to the same offset as the stack map - // one. - ASSERT_TRUE(stack_map.HasInlineInfo(encoding.stack_map.encoding)); - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); - EXPECT_EQ(inline_info.GetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding, 0), - stack_map.GetDexRegisterMapOffset(encoding.stack_map.encoding)); } } TEST(StackMapTest, TestNonLiveDexRegisters) { - ArenaPool pool; + MallocArenaPool pool; ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stream(&allocator, kRuntimeISA); + stream.BeginMethod(32, 0, 0, 2); ArenaBitVector sp_mask(&allocator, 0, false); uint32_t number_of_dex_registers = 2; - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask); stream.AddDexRegisterEntry(Kind::kNone, 0); // No location. stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location. stream.EndStackMapEntry(); + stream.EndMethod(); size_t size = stream.PrepareForFillIn(); void* memory = allocator.Alloc(size, kArenaAllocMisc); MemoryRegion region(memory, size); stream.FillInCodeInfo(region); CodeInfo code_info(region); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - ASSERT_EQ(1u, code_info.GetNumberOfStackMaps(encoding)); + ASSERT_EQ(1u, code_info.GetNumberOfStackMaps()); - uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding); + uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(); ASSERT_EQ(1u, number_of_catalog_entries); - DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding); - // The Dex register location catalog contains: - // - one 5-byte large Dex register location. - size_t expected_location_catalog_size = 5u; - ASSERT_EQ(expected_location_catalog_size, location_catalog.Size()); - - StackMap stack_map = code_info.GetStackMapAt(0, encoding); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding))); - ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map.encoding)); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA)); - ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(encoding, stack_map)); - - ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding)); - DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); - ASSERT_FALSE(dex_register_map.IsDexRegisterLive(0)); - ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); - ASSERT_EQ(1u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); - // The Dex register map contains: - // - one 1-byte live bit mask. - // No space is allocated for the sole location catalog entry index, as it is useless. - size_t expected_dex_register_map_size = 1u + 0u; - ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); - - ASSERT_EQ(Kind::kNone, dex_register_map.GetLocationKind( - 0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind( - 1, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(Kind::kNone, dex_register_map.GetLocationInternalKind( - 0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind( - 1, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info, encoding)); - - size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( - 0, number_of_dex_registers, number_of_catalog_entries); - size_t index1 = dex_register_map.GetLocationCatalogEntryIndex( - 1, number_of_dex_registers, number_of_catalog_entries); - ASSERT_EQ(DexRegisterLocationCatalog::kNoLocationEntryIndex, index0); - ASSERT_EQ(0u, index1); - DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); - DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); - ASSERT_EQ(Kind::kNone, location0.GetKind()); + + StackMap stack_map = code_info.GetStackMapAt(0); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64 * kPcAlign))); + ASSERT_EQ(0u, stack_map.GetDexPc()); + ASSERT_EQ(64u * kPcAlign, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map)); + + ASSERT_TRUE(stack_map.HasDexRegisterMap()); + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map); + ASSERT_EQ(number_of_dex_registers, dex_register_map.size()); + ASSERT_FALSE(dex_register_map[0].IsLive()); + ASSERT_TRUE(dex_register_map[1].IsLive()); + ASSERT_EQ(1u, dex_register_map.GetNumberOfLiveDexRegisters()); + + ASSERT_EQ(Kind::kNone, dex_register_map[0].GetKind()); + ASSERT_EQ(Kind::kConstant, dex_register_map[1].GetKind()); + ASSERT_EQ(-2, dex_register_map[1].GetConstant()); + + DexRegisterLocation location1 = code_info.GetDexRegisterCatalogEntry(0); ASSERT_EQ(Kind::kConstant, location1.GetKind()); - ASSERT_EQ(Kind::kNone, location0.GetInternalKind()); - ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind()); - ASSERT_EQ(0, location0.GetValue()); ASSERT_EQ(-2, location1.GetValue()); - ASSERT_FALSE(stack_map.HasInlineInfo(encoding.stack_map.encoding)); -} - -// Generate a stack map whose dex register offset is -// StackMap::kNoDexRegisterMapSmallEncoding, and ensure we do -// not treat it as kNoDexRegisterMap. -TEST(StackMapTest, DexRegisterMapOffsetOverflow) { - ArenaPool pool; - ArenaStack arena_stack(&pool); - ScopedArenaAllocator allocator(&arena_stack); - StackMapStream stream(&allocator, kRuntimeISA); - - ArenaBitVector sp_mask(&allocator, 0, false); - uint32_t number_of_dex_registers = 1024; - // Create the first stack map (and its Dex register map). - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); - uint32_t number_of_dex_live_registers_in_dex_register_map_0 = number_of_dex_registers - 8; - for (uint32_t i = 0; i < number_of_dex_live_registers_in_dex_register_map_0; ++i) { - // Use two different Dex register locations to populate this map, - // as using a single value (in the whole CodeInfo object) would - // make this Dex register mapping data empty (see - // art::DexRegisterMap::SingleEntrySizeInBits). - stream.AddDexRegisterEntry(Kind::kConstant, i % 2); // Short location. - } - stream.EndStackMapEntry(); - // Create the second stack map (and its Dex register map). - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); - for (uint32_t i = 0; i < number_of_dex_registers; ++i) { - stream.AddDexRegisterEntry(Kind::kConstant, 0); // Short location. - } - stream.EndStackMapEntry(); - - size_t size = stream.PrepareForFillIn(); - void* memory = allocator.Alloc(size, kArenaAllocMisc); - MemoryRegion region(memory, size); - stream.FillInCodeInfo(region); - - CodeInfo code_info(region); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - // The location catalog contains two entries (DexRegisterLocation(kConstant, 0) - // and DexRegisterLocation(kConstant, 1)), therefore the location catalog index - // has a size of 1 bit. - uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding); - ASSERT_EQ(2u, number_of_catalog_entries); - ASSERT_EQ(1u, DexRegisterMap::SingleEntrySizeInBits(number_of_catalog_entries)); - - // The first Dex register map contains: - // - a live register bit mask for 1024 registers (that is, 128 bytes of - // data); and - // - Dex register mapping information for 1016 1-bit Dex (live) register - // locations (that is, 127 bytes of data). - // Hence it has a size of 255 bytes, and therefore... - ASSERT_EQ(128u, DexRegisterMap::GetLiveBitMaskSize(number_of_dex_registers)); - StackMap stack_map0 = code_info.GetStackMapAt(0, encoding); - DexRegisterMap dex_register_map0 = - code_info.GetDexRegisterMapOf(stack_map0, encoding, number_of_dex_registers); - ASSERT_EQ(127u, dex_register_map0.GetLocationMappingDataSize(number_of_dex_registers, - number_of_catalog_entries)); - ASSERT_EQ(255u, dex_register_map0.Size()); - - StackMap stack_map1 = code_info.GetStackMapAt(1, encoding); - ASSERT_TRUE(stack_map1.HasDexRegisterMap(encoding.stack_map.encoding)); - // ...the offset of the second Dex register map (relative to the - // beginning of the Dex register maps region) is 255 (i.e., - // kNoDexRegisterMapSmallEncoding). - ASSERT_NE(stack_map1.GetDexRegisterMapOffset(encoding.stack_map.encoding), - StackMap::kNoDexRegisterMap); - ASSERT_EQ(stack_map1.GetDexRegisterMapOffset(encoding.stack_map.encoding), 0xFFu); + ASSERT_FALSE(stack_map.HasInlineInfo()); } TEST(StackMapTest, TestShareDexRegisterMap) { - ArenaPool pool; + MallocArenaPool pool; ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stream(&allocator, kRuntimeISA); + stream.BeginMethod(32, 0, 0, 2); ArenaBitVector sp_mask(&allocator, 0, false); uint32_t number_of_dex_registers = 2; // First stack map. - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask); stream.AddDexRegisterEntry(Kind::kInRegister, 0); // Short location. stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location. stream.EndStackMapEntry(); // Second stack map, which should share the same dex register map. - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 65 * kPcAlign, 0x3, &sp_mask); stream.AddDexRegisterEntry(Kind::kInRegister, 0); // Short location. stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location. stream.EndStackMapEntry(); // Third stack map (doesn't share the dex register map). - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 66 * kPcAlign, 0x3, &sp_mask); stream.AddDexRegisterEntry(Kind::kInRegister, 2); // Short location. stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location. stream.EndStackMapEntry(); + stream.EndMethod(); size_t size = stream.PrepareForFillIn(); void* memory = allocator.Alloc(size, kArenaAllocMisc); MemoryRegion region(memory, size); stream.FillInCodeInfo(region); CodeInfo ci(region); - CodeInfoEncoding encoding = ci.ExtractEncoding(); // Verify first stack map. - StackMap sm0 = ci.GetStackMapAt(0, encoding); - DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, encoding, number_of_dex_registers); - ASSERT_EQ(0, dex_registers0.GetMachineRegister(0, number_of_dex_registers, ci, encoding)); - ASSERT_EQ(-2, dex_registers0.GetConstant(1, number_of_dex_registers, ci, encoding)); + StackMap sm0 = ci.GetStackMapAt(0); + DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0); + ASSERT_EQ(number_of_dex_registers, dex_registers0.size()); + ASSERT_EQ(0, dex_registers0[0].GetMachineRegister()); + ASSERT_EQ(-2, dex_registers0[1].GetConstant()); // Verify second stack map. - StackMap sm1 = ci.GetStackMapAt(1, encoding); - DexRegisterMap dex_registers1 = ci.GetDexRegisterMapOf(sm1, encoding, number_of_dex_registers); - ASSERT_EQ(0, dex_registers1.GetMachineRegister(0, number_of_dex_registers, ci, encoding)); - ASSERT_EQ(-2, dex_registers1.GetConstant(1, number_of_dex_registers, ci, encoding)); + StackMap sm1 = ci.GetStackMapAt(1); + DexRegisterMap dex_registers1 = ci.GetDexRegisterMapOf(sm1); + ASSERT_EQ(number_of_dex_registers, dex_registers1.size()); + ASSERT_EQ(0, dex_registers1[0].GetMachineRegister()); + ASSERT_EQ(-2, dex_registers1[1].GetConstant()); // Verify third stack map. - StackMap sm2 = ci.GetStackMapAt(2, encoding); - DexRegisterMap dex_registers2 = ci.GetDexRegisterMapOf(sm2, encoding, number_of_dex_registers); - ASSERT_EQ(2, dex_registers2.GetMachineRegister(0, number_of_dex_registers, ci, encoding)); - ASSERT_EQ(-2, dex_registers2.GetConstant(1, number_of_dex_registers, ci, encoding)); - - // Verify dex register map offsets. - ASSERT_EQ(sm0.GetDexRegisterMapOffset(encoding.stack_map.encoding), - sm1.GetDexRegisterMapOffset(encoding.stack_map.encoding)); - ASSERT_NE(sm0.GetDexRegisterMapOffset(encoding.stack_map.encoding), - sm2.GetDexRegisterMapOffset(encoding.stack_map.encoding)); - ASSERT_NE(sm1.GetDexRegisterMapOffset(encoding.stack_map.encoding), - sm2.GetDexRegisterMapOffset(encoding.stack_map.encoding)); + StackMap sm2 = ci.GetStackMapAt(2); + DexRegisterMap dex_registers2 = ci.GetDexRegisterMapOf(sm2); + ASSERT_EQ(number_of_dex_registers, dex_registers2.size()); + ASSERT_EQ(2, dex_registers2[0].GetMachineRegister()); + ASSERT_EQ(-2, dex_registers2[1].GetConstant()); + + // Verify dex register mask offsets. + ASSERT_FALSE(sm1.HasDexRegisterMaskIndex()); // No delta. + ASSERT_TRUE(sm2.HasDexRegisterMaskIndex()); // Has delta. } TEST(StackMapTest, TestNoDexRegisterMap) { - ArenaPool pool; + MallocArenaPool pool; ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stream(&allocator, kRuntimeISA); + stream.BeginMethod(32, 0, 0, 1); ArenaBitVector sp_mask(&allocator, 0, false); uint32_t number_of_dex_registers = 0; - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask); stream.EndStackMapEntry(); number_of_dex_registers = 1; - stream.BeginStackMapEntry(1, 68, 0x4, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(1, 68 * kPcAlign, 0x4, &sp_mask); + stream.AddDexRegisterEntry(Kind::kNone, 0); stream.EndStackMapEntry(); + stream.EndMethod(); size_t size = stream.PrepareForFillIn(); void* memory = allocator.Alloc(size, kArenaAllocMisc); MemoryRegion region(memory, size); stream.FillInCodeInfo(region); CodeInfo code_info(region); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - ASSERT_EQ(2u, code_info.GetNumberOfStackMaps(encoding)); + ASSERT_EQ(2u, code_info.GetNumberOfStackMaps()); - uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding); + uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(); ASSERT_EQ(0u, number_of_catalog_entries); - DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding); - ASSERT_EQ(0u, location_catalog.Size()); - - StackMap stack_map = code_info.GetStackMapAt(0, encoding); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding))); - ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map.encoding)); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA)); - ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(encoding, stack_map)); - - ASSERT_FALSE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding)); - ASSERT_FALSE(stack_map.HasInlineInfo(encoding.stack_map.encoding)); - - stack_map = code_info.GetStackMapAt(1, encoding); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1, encoding))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(68, encoding))); - ASSERT_EQ(1u, stack_map.GetDexPc(encoding.stack_map.encoding)); - ASSERT_EQ(68u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA)); - ASSERT_EQ(0x4u, code_info.GetRegisterMaskOf(encoding, stack_map)); - - ASSERT_FALSE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding)); - ASSERT_FALSE(stack_map.HasInlineInfo(encoding.stack_map.encoding)); + + StackMap stack_map = code_info.GetStackMapAt(0); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64 * kPcAlign))); + ASSERT_EQ(0u, stack_map.GetDexPc()); + ASSERT_EQ(64u * kPcAlign, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map)); + + ASSERT_FALSE(stack_map.HasDexRegisterMap()); + ASSERT_FALSE(stack_map.HasInlineInfo()); + + stack_map = code_info.GetStackMapAt(1); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(68 * kPcAlign))); + ASSERT_EQ(1u, stack_map.GetDexPc()); + ASSERT_EQ(68u * kPcAlign, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(0x4u, code_info.GetRegisterMaskOf(stack_map)); + + ASSERT_TRUE(stack_map.HasDexRegisterMap()); + ASSERT_FALSE(stack_map.HasInlineInfo()); } TEST(StackMapTest, InlineTest) { - ArenaPool pool; + MallocArenaPool pool; ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stream(&allocator, kRuntimeISA); + stream.BeginMethod(32, 0, 0, 2); ArtMethod art_method; ArenaBitVector sp_mask1(&allocator, 0, true); @@ -772,7 +540,7 @@ TEST(StackMapTest, InlineTest) { sp_mask1.SetBit(4); // First stack map. - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask1, 2, 2); + stream.BeginStackMapEntry(0, 10 * kPcAlign, 0x3, &sp_mask1); stream.AddDexRegisterEntry(Kind::kInStack, 0); stream.AddDexRegisterEntry(Kind::kConstant, 4); @@ -788,7 +556,7 @@ TEST(StackMapTest, InlineTest) { stream.EndStackMapEntry(); // Second stack map. - stream.BeginStackMapEntry(2, 22, 0x3, &sp_mask1, 2, 3); + stream.BeginStackMapEntry(2, 22 * kPcAlign, 0x3, &sp_mask1); stream.AddDexRegisterEntry(Kind::kInStack, 56); stream.AddDexRegisterEntry(Kind::kConstant, 0); @@ -806,13 +574,13 @@ TEST(StackMapTest, InlineTest) { stream.EndStackMapEntry(); // Third stack map. - stream.BeginStackMapEntry(4, 56, 0x3, &sp_mask1, 2, 0); + stream.BeginStackMapEntry(4, 56 * kPcAlign, 0x3, &sp_mask1); stream.AddDexRegisterEntry(Kind::kNone, 0); stream.AddDexRegisterEntry(Kind::kConstant, 4); stream.EndStackMapEntry(); // Fourth stack map. - stream.BeginStackMapEntry(6, 78, 0x3, &sp_mask1, 2, 3); + stream.BeginStackMapEntry(6, 78 * kPcAlign, 0x3, &sp_mask1); stream.AddDexRegisterEntry(Kind::kInStack, 56); stream.AddDexRegisterEntry(Kind::kConstant, 0); @@ -828,204 +596,166 @@ TEST(StackMapTest, InlineTest) { stream.EndStackMapEntry(); + stream.EndMethod(); size_t size = stream.PrepareForFillIn(); void* memory = allocator.Alloc(size, kArenaAllocMisc); MemoryRegion region(memory, size); stream.FillInCodeInfo(region); CodeInfo ci(region); - CodeInfoEncoding encoding = ci.ExtractEncoding(); { // Verify first stack map. - StackMap sm0 = ci.GetStackMapAt(0, encoding); - - DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, encoding, 2); - ASSERT_EQ(0, dex_registers0.GetStackOffsetInBytes(0, 2, ci, encoding)); - ASSERT_EQ(4, dex_registers0.GetConstant(1, 2, ci, encoding)); - - InlineInfo if0 = ci.GetInlineInfoOf(sm0, encoding); - ASSERT_EQ(2u, if0.GetDepth(encoding.inline_info.encoding)); - ASSERT_EQ(2u, if0.GetDexPcAtDepth(encoding.inline_info.encoding, 0)); - ASSERT_TRUE(if0.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 0)); - ASSERT_EQ(3u, if0.GetDexPcAtDepth(encoding.inline_info.encoding, 1)); - ASSERT_TRUE(if0.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 1)); - - DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if0, encoding, 1); - ASSERT_EQ(8, dex_registers1.GetStackOffsetInBytes(0, 1, ci, encoding)); - - DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if0, encoding, 3); - ASSERT_EQ(16, dex_registers2.GetStackOffsetInBytes(0, 3, ci, encoding)); - ASSERT_EQ(20, dex_registers2.GetConstant(1, 3, ci, encoding)); - ASSERT_EQ(15, dex_registers2.GetMachineRegister(2, 3, ci, encoding)); + StackMap sm0 = ci.GetStackMapAt(0); + + DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0); + ASSERT_EQ(2u, dex_registers0.size()); + ASSERT_EQ(0, dex_registers0[0].GetStackOffsetInBytes()); + ASSERT_EQ(4, dex_registers0[1].GetConstant()); + + auto inline_infos = ci.GetInlineInfosOf(sm0); + ASSERT_EQ(2u, inline_infos.size()); + ASSERT_EQ(2u, inline_infos[0].GetDexPc()); + ASSERT_TRUE(inline_infos[0].EncodesArtMethod()); + ASSERT_EQ(3u, inline_infos[1].GetDexPc()); + ASSERT_TRUE(inline_infos[1].EncodesArtMethod()); + + DexRegisterMap dex_registers1 = ci.GetInlineDexRegisterMapOf(sm0, inline_infos[0]); + ASSERT_EQ(1u, dex_registers1.size()); + ASSERT_EQ(8, dex_registers1[0].GetStackOffsetInBytes()); + + DexRegisterMap dex_registers2 = ci.GetInlineDexRegisterMapOf(sm0, inline_infos[1]); + ASSERT_EQ(3u, dex_registers2.size()); + ASSERT_EQ(16, dex_registers2[0].GetStackOffsetInBytes()); + ASSERT_EQ(20, dex_registers2[1].GetConstant()); + ASSERT_EQ(15, dex_registers2[2].GetMachineRegister()); } { // Verify second stack map. - StackMap sm1 = ci.GetStackMapAt(1, encoding); - - DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm1, encoding, 2); - ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0, 2, ci, encoding)); - ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci, encoding)); - - InlineInfo if1 = ci.GetInlineInfoOf(sm1, encoding); - ASSERT_EQ(3u, if1.GetDepth(encoding.inline_info.encoding)); - ASSERT_EQ(2u, if1.GetDexPcAtDepth(encoding.inline_info.encoding, 0)); - ASSERT_TRUE(if1.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 0)); - ASSERT_EQ(3u, if1.GetDexPcAtDepth(encoding.inline_info.encoding, 1)); - ASSERT_TRUE(if1.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 1)); - ASSERT_EQ(5u, if1.GetDexPcAtDepth(encoding.inline_info.encoding, 2)); - ASSERT_TRUE(if1.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 2)); - - DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if1, encoding, 1); - ASSERT_EQ(12, dex_registers1.GetStackOffsetInBytes(0, 1, ci, encoding)); - - DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if1, encoding, 3); - ASSERT_EQ(80, dex_registers2.GetStackOffsetInBytes(0, 3, ci, encoding)); - ASSERT_EQ(10, dex_registers2.GetConstant(1, 3, ci, encoding)); - ASSERT_EQ(5, dex_registers2.GetMachineRegister(2, 3, ci, encoding)); - - ASSERT_FALSE(if1.HasDexRegisterMapAtDepth(encoding.inline_info.encoding, 2)); + StackMap sm1 = ci.GetStackMapAt(1); + + DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm1); + ASSERT_EQ(2u, dex_registers0.size()); + ASSERT_EQ(56, dex_registers0[0].GetStackOffsetInBytes()); + ASSERT_EQ(0, dex_registers0[1].GetConstant()); + + auto inline_infos = ci.GetInlineInfosOf(sm1); + ASSERT_EQ(3u, inline_infos.size()); + ASSERT_EQ(2u, inline_infos[0].GetDexPc()); + ASSERT_TRUE(inline_infos[0].EncodesArtMethod()); + ASSERT_EQ(3u, inline_infos[1].GetDexPc()); + ASSERT_TRUE(inline_infos[1].EncodesArtMethod()); + ASSERT_EQ(5u, inline_infos[2].GetDexPc()); + ASSERT_TRUE(inline_infos[2].EncodesArtMethod()); + + DexRegisterMap dex_registers1 = ci.GetInlineDexRegisterMapOf(sm1, inline_infos[0]); + ASSERT_EQ(1u, dex_registers1.size()); + ASSERT_EQ(12, dex_registers1[0].GetStackOffsetInBytes()); + + DexRegisterMap dex_registers2 = ci.GetInlineDexRegisterMapOf(sm1, inline_infos[1]); + ASSERT_EQ(3u, dex_registers2.size()); + ASSERT_EQ(80, dex_registers2[0].GetStackOffsetInBytes()); + ASSERT_EQ(10, dex_registers2[1].GetConstant()); + ASSERT_EQ(5, dex_registers2[2].GetMachineRegister()); } { // Verify third stack map. - StackMap sm2 = ci.GetStackMapAt(2, encoding); + StackMap sm2 = ci.GetStackMapAt(2); - DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm2, encoding, 2); - ASSERT_FALSE(dex_registers0.IsDexRegisterLive(0)); - ASSERT_EQ(4, dex_registers0.GetConstant(1, 2, ci, encoding)); - ASSERT_FALSE(sm2.HasInlineInfo(encoding.stack_map.encoding)); + DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm2); + ASSERT_EQ(2u, dex_registers0.size()); + ASSERT_FALSE(dex_registers0[0].IsLive()); + ASSERT_EQ(4, dex_registers0[1].GetConstant()); + ASSERT_FALSE(sm2.HasInlineInfo()); } { // Verify fourth stack map. - StackMap sm3 = ci.GetStackMapAt(3, encoding); - - DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm3, encoding, 2); - ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0, 2, ci, encoding)); - ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci, encoding)); - - InlineInfo if2 = ci.GetInlineInfoOf(sm3, encoding); - ASSERT_EQ(3u, if2.GetDepth(encoding.inline_info.encoding)); - ASSERT_EQ(2u, if2.GetDexPcAtDepth(encoding.inline_info.encoding, 0)); - ASSERT_TRUE(if2.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 0)); - ASSERT_EQ(5u, if2.GetDexPcAtDepth(encoding.inline_info.encoding, 1)); - ASSERT_TRUE(if2.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 1)); - ASSERT_EQ(10u, if2.GetDexPcAtDepth(encoding.inline_info.encoding, 2)); - ASSERT_TRUE(if2.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 2)); - - ASSERT_FALSE(if2.HasDexRegisterMapAtDepth(encoding.inline_info.encoding, 0)); - - DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(1, if2, encoding, 1); - ASSERT_EQ(2, dex_registers1.GetMachineRegister(0, 1, ci, encoding)); - - DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(2, if2, encoding, 2); - ASSERT_FALSE(dex_registers2.IsDexRegisterLive(0)); - ASSERT_EQ(3, dex_registers2.GetMachineRegister(1, 2, ci, encoding)); + StackMap sm3 = ci.GetStackMapAt(3); + + DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm3); + ASSERT_EQ(2u, dex_registers0.size()); + ASSERT_EQ(56, dex_registers0[0].GetStackOffsetInBytes()); + ASSERT_EQ(0, dex_registers0[1].GetConstant()); + + auto inline_infos = ci.GetInlineInfosOf(sm3); + ASSERT_EQ(3u, inline_infos.size()); + ASSERT_EQ(2u, inline_infos[0].GetDexPc()); + ASSERT_TRUE(inline_infos[0].EncodesArtMethod()); + ASSERT_EQ(5u, inline_infos[1].GetDexPc()); + ASSERT_TRUE(inline_infos[1].EncodesArtMethod()); + ASSERT_EQ(10u, inline_infos[2].GetDexPc()); + ASSERT_TRUE(inline_infos[2].EncodesArtMethod()); + + DexRegisterMap dex_registers1 = ci.GetInlineDexRegisterMapOf(sm3, inline_infos[1]); + ASSERT_EQ(1u, dex_registers1.size()); + ASSERT_EQ(2, dex_registers1[0].GetMachineRegister()); + + DexRegisterMap dex_registers2 = ci.GetInlineDexRegisterMapOf(sm3, inline_infos[2]); + ASSERT_EQ(2u, dex_registers2.size()); + ASSERT_FALSE(dex_registers2[0].IsLive()); + ASSERT_EQ(3, dex_registers2[1].GetMachineRegister()); } } -TEST(StackMapTest, CodeOffsetTest) { - // Test minimum alignments, encoding, and decoding. - CodeOffset offset_thumb2 = - CodeOffset::FromOffset(kThumb2InstructionAlignment, InstructionSet::kThumb2); - CodeOffset offset_arm64 = - CodeOffset::FromOffset(kArm64InstructionAlignment, InstructionSet::kArm64); - CodeOffset offset_x86 = - CodeOffset::FromOffset(kX86InstructionAlignment, InstructionSet::kX86); - CodeOffset offset_x86_64 = - CodeOffset::FromOffset(kX86_64InstructionAlignment, InstructionSet::kX86_64); - CodeOffset offset_mips = - CodeOffset::FromOffset(kMipsInstructionAlignment, InstructionSet::kMips); - CodeOffset offset_mips64 = - CodeOffset::FromOffset(kMips64InstructionAlignment, InstructionSet::kMips64); - EXPECT_EQ(offset_thumb2.Uint32Value(InstructionSet::kThumb2), kThumb2InstructionAlignment); - EXPECT_EQ(offset_arm64.Uint32Value(InstructionSet::kArm64), kArm64InstructionAlignment); - EXPECT_EQ(offset_x86.Uint32Value(InstructionSet::kX86), kX86InstructionAlignment); - EXPECT_EQ(offset_x86_64.Uint32Value(InstructionSet::kX86_64), kX86_64InstructionAlignment); - EXPECT_EQ(offset_mips.Uint32Value(InstructionSet::kMips), kMipsInstructionAlignment); - EXPECT_EQ(offset_mips64.Uint32Value(InstructionSet::kMips64), kMips64InstructionAlignment); +TEST(StackMapTest, PackedNativePcTest) { + // Test minimum alignments, and decoding. + uint32_t packed_thumb2 = + StackMap::PackNativePc(kThumb2InstructionAlignment, InstructionSet::kThumb2); + uint32_t packed_arm64 = + StackMap::PackNativePc(kArm64InstructionAlignment, InstructionSet::kArm64); + uint32_t packed_x86 = + StackMap::PackNativePc(kX86InstructionAlignment, InstructionSet::kX86); + uint32_t packed_x86_64 = + StackMap::PackNativePc(kX86_64InstructionAlignment, InstructionSet::kX86_64); + uint32_t packed_mips = + StackMap::PackNativePc(kMipsInstructionAlignment, InstructionSet::kMips); + uint32_t packed_mips64 = + StackMap::PackNativePc(kMips64InstructionAlignment, InstructionSet::kMips64); + EXPECT_EQ(StackMap::UnpackNativePc(packed_thumb2, InstructionSet::kThumb2), + kThumb2InstructionAlignment); + EXPECT_EQ(StackMap::UnpackNativePc(packed_arm64, InstructionSet::kArm64), + kArm64InstructionAlignment); + EXPECT_EQ(StackMap::UnpackNativePc(packed_x86, InstructionSet::kX86), + kX86InstructionAlignment); + EXPECT_EQ(StackMap::UnpackNativePc(packed_x86_64, InstructionSet::kX86_64), + kX86_64InstructionAlignment); + EXPECT_EQ(StackMap::UnpackNativePc(packed_mips, InstructionSet::kMips), + kMipsInstructionAlignment); + EXPECT_EQ(StackMap::UnpackNativePc(packed_mips64, InstructionSet::kMips64), + kMips64InstructionAlignment); } TEST(StackMapTest, TestDeduplicateStackMask) { - ArenaPool pool; + MallocArenaPool pool; ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stream(&allocator, kRuntimeISA); + stream.BeginMethod(32, 0, 0, 0); ArenaBitVector sp_mask(&allocator, 0, true); sp_mask.SetBit(1); sp_mask.SetBit(4); - stream.BeginStackMapEntry(0, 4, 0x3, &sp_mask, 0, 0); + stream.BeginStackMapEntry(0, 4 * kPcAlign, 0x3, &sp_mask); stream.EndStackMapEntry(); - stream.BeginStackMapEntry(0, 8, 0x3, &sp_mask, 0, 0); + stream.BeginStackMapEntry(0, 8 * kPcAlign, 0x3, &sp_mask); stream.EndStackMapEntry(); + stream.EndMethod(); size_t size = stream.PrepareForFillIn(); void* memory = allocator.Alloc(size, kArenaAllocMisc); MemoryRegion region(memory, size); stream.FillInCodeInfo(region); CodeInfo code_info(region); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - ASSERT_EQ(2u, code_info.GetNumberOfStackMaps(encoding)); - - StackMap stack_map1 = code_info.GetStackMapForNativePcOffset(4, encoding); - StackMap stack_map2 = code_info.GetStackMapForNativePcOffset(8, encoding); - EXPECT_EQ(stack_map1.GetStackMaskIndex(encoding.stack_map.encoding), - stack_map2.GetStackMaskIndex(encoding.stack_map.encoding)); -} - -TEST(StackMapTest, TestInvokeInfo) { - ArenaPool pool; - ArenaStack arena_stack(&pool); - ScopedArenaAllocator allocator(&arena_stack); - StackMapStream stream(&allocator, kRuntimeISA); - - ArenaBitVector sp_mask(&allocator, 0, true); - sp_mask.SetBit(1); - stream.BeginStackMapEntry(0, 4, 0x3, &sp_mask, 0, 0); - stream.AddInvoke(kSuper, 1); - stream.EndStackMapEntry(); - stream.BeginStackMapEntry(0, 8, 0x3, &sp_mask, 0, 0); - stream.AddInvoke(kStatic, 3); - stream.EndStackMapEntry(); - stream.BeginStackMapEntry(0, 16, 0x3, &sp_mask, 0, 0); - stream.AddInvoke(kDirect, 65535); - stream.EndStackMapEntry(); + ASSERT_EQ(2u, code_info.GetNumberOfStackMaps()); - const size_t code_info_size = stream.PrepareForFillIn(); - MemoryRegion code_info_region(allocator.Alloc(code_info_size, kArenaAllocMisc), code_info_size); - stream.FillInCodeInfo(code_info_region); - - const size_t method_info_size = stream.ComputeMethodInfoSize(); - MemoryRegion method_info_region(allocator.Alloc(method_info_size, kArenaAllocMisc), - method_info_size); - stream.FillInMethodInfo(method_info_region); - - CodeInfo code_info(code_info_region); - MethodInfo method_info(method_info_region.begin()); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - ASSERT_EQ(3u, code_info.GetNumberOfStackMaps(encoding)); - - InvokeInfo invoke1(code_info.GetInvokeInfoForNativePcOffset(4, encoding)); - InvokeInfo invoke2(code_info.GetInvokeInfoForNativePcOffset(8, encoding)); - InvokeInfo invoke3(code_info.GetInvokeInfoForNativePcOffset(16, encoding)); - InvokeInfo invoke_invalid(code_info.GetInvokeInfoForNativePcOffset(12, encoding)); - EXPECT_FALSE(invoke_invalid.IsValid()); // No entry for that index. - EXPECT_TRUE(invoke1.IsValid()); - EXPECT_TRUE(invoke2.IsValid()); - EXPECT_TRUE(invoke3.IsValid()); - EXPECT_EQ(invoke1.GetInvokeType(encoding.invoke_info.encoding), kSuper); - EXPECT_EQ(invoke1.GetMethodIndex(encoding.invoke_info.encoding, method_info), 1u); - EXPECT_EQ(invoke1.GetNativePcOffset(encoding.invoke_info.encoding, kRuntimeISA), 4u); - EXPECT_EQ(invoke2.GetInvokeType(encoding.invoke_info.encoding), kStatic); - EXPECT_EQ(invoke2.GetMethodIndex(encoding.invoke_info.encoding, method_info), 3u); - EXPECT_EQ(invoke2.GetNativePcOffset(encoding.invoke_info.encoding, kRuntimeISA), 8u); - EXPECT_EQ(invoke3.GetInvokeType(encoding.invoke_info.encoding), kDirect); - EXPECT_EQ(invoke3.GetMethodIndex(encoding.invoke_info.encoding, method_info), 65535u); - EXPECT_EQ(invoke3.GetNativePcOffset(encoding.invoke_info.encoding, kRuntimeISA), 16u); + StackMap stack_map1 = code_info.GetStackMapForNativePcOffset(4 * kPcAlign); + StackMap stack_map2 = code_info.GetStackMapForNativePcOffset(8 * kPcAlign); + EXPECT_EQ(stack_map1.GetStackMaskIndex(), + stack_map2.GetStackMaskIndex()); } } // namespace art diff --git a/compiler/optimizing/superblock_cloner.cc b/compiler/optimizing/superblock_cloner.cc index a7c23bef7e3ab37b013647008dfae10802dee445..878967cc6e361da6bbcc1628252335da4eb91e4b 100644 --- a/compiler/optimizing/superblock_cloner.cc +++ b/compiler/optimizing/superblock_cloner.cc @@ -70,20 +70,18 @@ static bool ArePhiInputsTheSame(const HPhi* phi) { return true; } -// Returns a common predecessor of loop1 and loop2 in the loop tree or nullptr if it is the whole -// graph. -static HLoopInformation* FindCommonLoop(HLoopInformation* loop1, HLoopInformation* loop2) { - if (loop1 != nullptr || loop2 != nullptr) { - return nullptr; +// Returns whether two Edge sets are equal (ArenaHashSet doesn't have "Equal" method). +static bool EdgeHashSetsEqual(const HEdgeSet* set1, const HEdgeSet* set2) { + if (set1->size() != set2->size()) { + return false; } - if (loop1->IsIn(*loop2)) { - return loop2; - } else if (loop2->IsIn(*loop1)) { - return loop1; + for (auto e : *set1) { + if (set2->find(e) == set2->end()) { + return false; + } } - HBasicBlock* block = CommonDominator::ForPair(loop1->GetHeader(), loop2->GetHeader()); - return block->GetLoopInformation(); + return true; } // Calls HGraph::OrderLoopHeaderPredecessors for each loop in the graph. @@ -95,6 +93,21 @@ static void OrderLoopsHeadersPredecessors(HGraph* graph) { } } +// Performs DFS on the subgraph (specified by 'bb_set') starting from the specified block; while +// traversing the function removes basic blocks from the bb_set (instead of traditional DFS +// 'marking'). So what is left in the 'bb_set' after the traversal is not reachable from the start +// block. +static void TraverseSubgraphForConnectivity(HBasicBlock* block, HBasicBlockSet* bb_set) { + DCHECK(bb_set->IsBitSet(block->GetBlockId())); + bb_set->ClearBit(block->GetBlockId()); + + for (HBasicBlock* succ : block->GetSuccessors()) { + if (bb_set->IsBitSet(succ->GetBlockId())) { + TraverseSubgraphForConnectivity(succ, bb_set); + } + } +} + // // Helpers for CloneBasicBlock. // @@ -268,7 +281,6 @@ void SuperblockCloner::FindBackEdgesLocal(HBasicBlock* entry_block, ArenaBitVect } void SuperblockCloner::RecalculateBackEdgesInfo(ArenaBitVector* outer_loop_bb_set) { - // TODO: DCHECK that after the transformation the graph is connected. HBasicBlock* block_entry = nullptr; if (outer_loop_ == nullptr) { @@ -397,7 +409,7 @@ void SuperblockCloner::ResolvePhi(HPhi* phi) { // Main algorithm methods. // -void SuperblockCloner::SearchForSubgraphExits(ArenaVector* exits) { +void SuperblockCloner::SearchForSubgraphExits(ArenaVector* exits) const { DCHECK(exits->empty()); for (uint32_t block_id : orig_bb_set_.Indexes()) { HBasicBlock* block = GetBlockById(block_id); @@ -424,6 +436,11 @@ void SuperblockCloner::FindAndSetLocalAreaForAdjustments() { outer_loop_ = nullptr; break; } + if (outer_loop_ == nullptr) { + // We should not use the initial outer_loop_ value 'nullptr' when finding the most outer + // common loop. + outer_loop_ = loop_exit_loop_info; + } outer_loop_ = FindCommonLoop(outer_loop_, loop_exit_loop_info); } @@ -455,8 +472,8 @@ void SuperblockCloner::RemapEdgesSuccessors() { continue; } - auto orig_redir = remap_orig_internal_->Find(HEdge(orig_block_id, orig_succ_id)); - auto copy_redir = remap_copy_internal_->Find(HEdge(orig_block_id, orig_succ_id)); + auto orig_redir = remap_orig_internal_->find(HEdge(orig_block_id, orig_succ_id)); + auto copy_redir = remap_copy_internal_->find(HEdge(orig_block_id, orig_succ_id)); // Due to construction all successors of copied block were set to original. if (copy_redir != remap_copy_internal_->end()) { @@ -503,10 +520,145 @@ void SuperblockCloner::ResolveDataFlow() { } } +// +// Helpers for live-outs processing and Subgraph-closed SSA. +// + +bool SuperblockCloner::CollectLiveOutsAndCheckClonable(HInstructionMap* live_outs) const { + DCHECK(live_outs->empty()); + for (uint32_t idx : orig_bb_set_.Indexes()) { + HBasicBlock* block = GetBlockById(idx); + + for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { + HInstruction* instr = it.Current(); + DCHECK(instr->IsClonable()); + + if (IsUsedOutsideRegion(instr, orig_bb_set_)) { + live_outs->FindOrAdd(instr, instr); + } + } + + for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { + HInstruction* instr = it.Current(); + if (!instr->IsClonable()) { + return false; + } + + if (IsUsedOutsideRegion(instr, orig_bb_set_)) { + // TODO: Investigate why HNewInstance, HCheckCast has a requirement for the input. + if (instr->IsLoadClass()) { + return false; + } + live_outs->FindOrAdd(instr, instr); + } + } + } + return true; +} + +void SuperblockCloner::ConstructSubgraphClosedSSA() { + if (live_outs_.empty()) { + return; + } + + ArenaVector exits(arena_->Adapter(kArenaAllocSuperblockCloner)); + SearchForSubgraphExits(&exits); + if (exits.empty()) { + DCHECK(live_outs_.empty()); + return; + } + + DCHECK_EQ(exits.size(), 1u); + HBasicBlock* exit_block = exits[0]; + // There should be no critical edges. + DCHECK_EQ(exit_block->GetPredecessors().size(), 1u); + DCHECK(exit_block->GetPhis().IsEmpty()); + + // For each live-out value insert a phi into the loop exit and replace all the value's uses + // external to the loop with this phi. The phi will have the original value as its only input; + // after copying is done FixSubgraphClosedSSAAfterCloning will add a corresponding copy of the + // original value as the second input thus merging data flow from the original and copy parts of + // the subgraph. Also update the record in the live_outs_ map from (value, value) to + // (value, new_phi). + for (auto live_out_it = live_outs_.begin(); live_out_it != live_outs_.end(); ++live_out_it) { + HInstruction* value = live_out_it->first; + HPhi* phi = new (arena_) HPhi(arena_, kNoRegNumber, 0, value->GetType()); + + if (value->GetType() == DataType::Type::kReference) { + phi->SetReferenceTypeInfo(value->GetReferenceTypeInfo()); + } + + exit_block->AddPhi(phi); + live_out_it->second = phi; + + const HUseList& uses = value->GetUses(); + for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) { + HInstruction* user = it->GetUser(); + size_t index = it->GetIndex(); + // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput(). + ++it; + if (!IsInOrigBBSet(user->GetBlock())) { + user->ReplaceInput(phi, index); + } + } + + const HUseList& env_uses = value->GetEnvUses(); + for (auto it = env_uses.begin(), e = env_uses.end(); it != e; /* ++it below */) { + HEnvironment* env = it->GetUser(); + size_t index = it->GetIndex(); + ++it; + if (!IsInOrigBBSet(env->GetHolder()->GetBlock())) { + env->ReplaceInput(phi, index); + } + } + + phi->AddInput(value); + } +} + +void SuperblockCloner::FixSubgraphClosedSSAAfterCloning() { + for (auto it : live_outs_) { + DCHECK(it.first != it.second); + HInstruction* orig_value = it.first; + HPhi* phi = it.second->AsPhi(); + HInstruction* copy_value = GetInstrCopy(orig_value); + // Copy edges are inserted after the original so we can just add new input to the phi. + phi->AddInput(copy_value); + } +} + // // Debug and logging methods. // +// Debug function to dump graph' BasicBlocks info. +void DumpBB(HGraph* graph) { + for (HBasicBlock* bb : graph->GetBlocks()) { + if (bb == nullptr) { + continue; + } + std::cout << bb->GetBlockId(); + std::cout << " <- "; + for (HBasicBlock* pred : bb->GetPredecessors()) { + std::cout << pred->GetBlockId() << " "; + } + std::cout << " -> "; + for (HBasicBlock* succ : bb->GetSuccessors()) { + std::cout << succ->GetBlockId() << " "; + } + + if (bb->GetDominator()) { + std::cout << " dom " << bb->GetDominator()->GetBlockId(); + } + + if (bb->GetLoopInformation()) { + std::cout << "\tloop: " << bb->GetLoopInformation()->GetHeader()->GetBlockId(); + } + + std::cout << std::endl; + } +} + void SuperblockCloner::CheckInstructionInputsRemapping(HInstruction* orig_instr) { DCHECK(!orig_instr->IsPhi()); HInstruction* copy_instr = GetInstrCopy(orig_instr); @@ -542,6 +694,81 @@ void SuperblockCloner::CheckInstructionInputsRemapping(HInstruction* orig_instr) } } +bool SuperblockCloner::CheckRemappingInfoIsValid() { + for (HEdge edge : *remap_orig_internal_) { + if (!IsEdgeValid(edge, graph_) || + !IsInOrigBBSet(edge.GetFrom()) || + !IsInOrigBBSet(edge.GetTo())) { + return false; + } + } + + for (auto edge : *remap_copy_internal_) { + if (!IsEdgeValid(edge, graph_) || + !IsInOrigBBSet(edge.GetFrom()) || + !IsInOrigBBSet(edge.GetTo())) { + return false; + } + } + + for (auto edge : *remap_incoming_) { + if (!IsEdgeValid(edge, graph_) || + IsInOrigBBSet(edge.GetFrom()) || + !IsInOrigBBSet(edge.GetTo())) { + return false; + } + } + + return true; +} + +void SuperblockCloner::VerifyGraph() { + for (auto it : *hir_map_) { + HInstruction* orig_instr = it.first; + HInstruction* copy_instr = it.second; + if (!orig_instr->IsPhi() && !orig_instr->IsSuspendCheck()) { + DCHECK(it.first->GetBlock() != nullptr); + } + if (!copy_instr->IsPhi() && !copy_instr->IsSuspendCheck()) { + DCHECK(it.second->GetBlock() != nullptr); + } + } + + GraphChecker checker(graph_); + checker.Run(); + if (!checker.IsValid()) { + for (const std::string& error : checker.GetErrors()) { + std::cout << error << std::endl; + } + LOG(FATAL) << "GraphChecker failed: superblock cloner\n"; + } +} + +void DumpBBSet(const ArenaBitVector* set) { + for (uint32_t idx : set->Indexes()) { + std::cout << idx << "\n"; + } +} + +void SuperblockCloner::DumpInputSets() { + std::cout << "orig_bb_set:\n"; + for (uint32_t idx : orig_bb_set_.Indexes()) { + std::cout << idx << "\n"; + } + std::cout << "remap_orig_internal:\n"; + for (HEdge e : *remap_orig_internal_) { + std::cout << e << "\n"; + } + std::cout << "remap_copy_internal:\n"; + for (auto e : *remap_copy_internal_) { + std::cout << e << "\n"; + } + std::cout << "remap_incoming:\n"; + for (auto e : *remap_incoming_) { + std::cout << e << "\n"; + } +} + // // Public methods. // @@ -559,7 +786,9 @@ SuperblockCloner::SuperblockCloner(HGraph* graph, bb_map_(bb_map), hir_map_(hir_map), outer_loop_(nullptr), - outer_loop_bb_set_(arena_, orig_bb_set->GetSizeOf(), true, kArenaAllocSuperblockCloner) { + outer_loop_bb_set_(arena_, orig_bb_set->GetSizeOf(), true, kArenaAllocSuperblockCloner), + live_outs_(std::less(), + graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)) { orig_bb_set_.Copy(orig_bb_set); } @@ -569,6 +798,7 @@ void SuperblockCloner::SetSuccessorRemappingInfo(const HEdgeSet* remap_orig_inte remap_orig_internal_ = remap_orig_internal; remap_copy_internal_ = remap_copy_internal; remap_incoming_ = remap_incoming; + DCHECK(CheckRemappingInfoIsValid()); } bool SuperblockCloner::IsSubgraphClonable() const { @@ -577,29 +807,79 @@ bool SuperblockCloner::IsSubgraphClonable() const { return false; } - // Check that there are no instructions defined in the subgraph and used outside. - // TODO: Improve this by accepting graph with such uses but only one exit. + HInstructionMap live_outs( + std::less(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + + if (!CollectLiveOutsAndCheckClonable(&live_outs)) { + return false; + } + + ArenaVector exits(arena_->Adapter(kArenaAllocSuperblockCloner)); + SearchForSubgraphExits(&exits); + + // The only loops with live-outs which are currently supported are loops with a single exit. + if (!live_outs.empty() && exits.size() != 1) { + return false; + } + + return true; +} + +bool SuperblockCloner::IsFastCase() const { + // Check that loop unrolling/loop peeling is being conducted. + // Check that all the basic blocks belong to the same loop. + bool flag = false; + HLoopInformation* common_loop_info = nullptr; for (uint32_t idx : orig_bb_set_.Indexes()) { HBasicBlock* block = GetBlockById(idx); - - for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { - HInstruction* instr = it.Current(); - if (!instr->IsClonable() || - IsUsedOutsideRegion(instr, orig_bb_set_)) { + HLoopInformation* block_loop_info = block->GetLoopInformation(); + if (!flag) { + common_loop_info = block_loop_info; + } else { + if (block_loop_info != common_loop_info) { return false; } } + } - for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { - HInstruction* instr = it.Current(); - if (!instr->IsClonable() || - IsUsedOutsideRegion(instr, orig_bb_set_)) { - return false; - } - } + // Check that orig_bb_set_ corresponds to loop peeling/unrolling. + if (common_loop_info == nullptr || !orig_bb_set_.SameBitsSet(&common_loop_info->GetBlocks())) { + return false; } - return true; + bool peeling_or_unrolling = false; + HEdgeSet remap_orig_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + HEdgeSet remap_copy_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + HEdgeSet remap_incoming(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + + + // Check whether remapping info corresponds to loop unrolling. + CollectRemappingInfoForPeelUnroll(/* to_unroll*/ true, + common_loop_info, + &remap_orig_internal, + &remap_copy_internal, + &remap_incoming); + + peeling_or_unrolling |= EdgeHashSetsEqual(&remap_orig_internal, remap_orig_internal_) && + EdgeHashSetsEqual(&remap_copy_internal, remap_copy_internal_) && + EdgeHashSetsEqual(&remap_incoming, remap_incoming_); + + remap_orig_internal.clear(); + remap_copy_internal.clear(); + remap_incoming.clear(); + + // Check whether remapping info corresponds to loop peeling. + CollectRemappingInfoForPeelUnroll(/* to_unroll*/ false, + common_loop_info, + &remap_orig_internal, + &remap_copy_internal, + &remap_incoming); + + peeling_or_unrolling |= EdgeHashSetsEqual(&remap_orig_internal, remap_orig_internal_) && + EdgeHashSetsEqual(&remap_copy_internal, remap_copy_internal_) && + EdgeHashSetsEqual(&remap_incoming, remap_incoming_); + + return peeling_or_unrolling; } void SuperblockCloner::Run() { @@ -609,19 +889,40 @@ void SuperblockCloner::Run() { remap_copy_internal_ != nullptr && remap_incoming_ != nullptr); DCHECK(IsSubgraphClonable()); + DCHECK(IsFastCase()); + + if (kSuperblockClonerLogging) { + DumpInputSets(); + } + CollectLiveOutsAndCheckClonable(&live_outs_); // Find an area in the graph for which control flow information should be adjusted. FindAndSetLocalAreaForAdjustments(); + ConstructSubgraphClosedSSA(); // Clone the basic blocks from the orig_bb_set_; data flow is invalid after the call and is to be // adjusted. CloneBasicBlocks(); // Connect the blocks together/remap successors and fix phis which are directly affected my the // remapping. RemapEdgesSuccessors(); + + // Check that the subgraph is connected. + if (kIsDebugBuild) { + HBasicBlockSet work_set(arena_, orig_bb_set_.GetSizeOf(), true, kArenaAllocSuperblockCloner); + + // Add original and copy blocks of the subgraph to the work set. + for (auto iter : *bb_map_) { + work_set.SetBit(iter.first->GetBlockId()); // Original block. + work_set.SetBit(iter.second->GetBlockId()); // Copy block. + } + CHECK(IsSubgraphConnected(&work_set, graph_)); + } + // Recalculate dominance and backedge information which is required by the next stage. AdjustControlFlowInfo(); // Fix data flow of the graph. ResolveDataFlow(); + FixSubgraphClosedSSAAfterCloning(); } void SuperblockCloner::CleanUp() { @@ -650,6 +951,10 @@ void SuperblockCloner::CleanUp() { } } } + + if (kIsDebugBuild) { + VerifyGraph(); + } } HBasicBlock* SuperblockCloner::CloneBasicBlock(const HBasicBlock* orig_block) { @@ -701,4 +1006,133 @@ void SuperblockCloner::CloneBasicBlocks() { } } +// +// Stand-alone methods. +// + +void CollectRemappingInfoForPeelUnroll(bool to_unroll, + HLoopInformation* loop_info, + HEdgeSet* remap_orig_internal, + HEdgeSet* remap_copy_internal, + HEdgeSet* remap_incoming) { + DCHECK(loop_info != nullptr); + HBasicBlock* loop_header = loop_info->GetHeader(); + // Set up remap_orig_internal edges set - set is empty. + // Set up remap_copy_internal edges set. + for (HBasicBlock* back_edge_block : loop_info->GetBackEdges()) { + HEdge e = HEdge(back_edge_block, loop_header); + if (to_unroll) { + remap_orig_internal->insert(e); + remap_copy_internal->insert(e); + } else { + remap_copy_internal->insert(e); + } + } + + // Set up remap_incoming edges set. + if (!to_unroll) { + remap_incoming->insert(HEdge(loop_info->GetPreHeader(), loop_header)); + } +} + +bool IsSubgraphConnected(SuperblockCloner::HBasicBlockSet* work_set, HGraph* graph) { + ArenaVector entry_blocks( + graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + + // Find subgraph entry blocks. + for (uint32_t orig_block_id : work_set->Indexes()) { + HBasicBlock* block = graph->GetBlocks()[orig_block_id]; + for (HBasicBlock* pred : block->GetPredecessors()) { + if (!work_set->IsBitSet(pred->GetBlockId())) { + entry_blocks.push_back(block); + break; + } + } + } + + for (HBasicBlock* entry_block : entry_blocks) { + if (work_set->IsBitSet(entry_block->GetBlockId())) { + TraverseSubgraphForConnectivity(entry_block, work_set); + } + } + + // Return whether there are unvisited - unreachable - blocks. + return work_set->NumSetBits() == 0; +} + +HLoopInformation* FindCommonLoop(HLoopInformation* loop1, HLoopInformation* loop2) { + if (loop1 == nullptr || loop2 == nullptr) { + return nullptr; + } + + if (loop1->IsIn(*loop2)) { + return loop2; + } + + HLoopInformation* current = loop1; + while (current != nullptr && !loop2->IsIn(*current)) { + current = current->GetPreHeader()->GetLoopInformation(); + } + + return current; +} + +bool PeelUnrollHelper::IsLoopClonable(HLoopInformation* loop_info) { + PeelUnrollHelper helper(loop_info, nullptr, nullptr); + return helper.IsLoopClonable(); +} + +HBasicBlock* PeelUnrollHelper::DoPeelUnrollImpl(bool to_unroll) { + // For now do peeling only for natural loops. + DCHECK(!loop_info_->IsIrreducible()); + + HBasicBlock* loop_header = loop_info_->GetHeader(); + // Check that loop info is up-to-date. + DCHECK(loop_info_ == loop_header->GetLoopInformation()); + HGraph* graph = loop_header->GetGraph(); + + if (kSuperblockClonerLogging) { + std::cout << "Method: " << graph->PrettyMethod() << std::endl; + std::cout << "Scalar loop " << (to_unroll ? "unrolling" : "peeling") << + " was applied to the loop <" << loop_header->GetBlockId() << ">." << std::endl; + } + + ArenaAllocator allocator(graph->GetAllocator()->GetArenaPool()); + + HEdgeSet remap_orig_internal(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + HEdgeSet remap_copy_internal(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + HEdgeSet remap_incoming(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + + CollectRemappingInfoForPeelUnroll(to_unroll, + loop_info_, + &remap_orig_internal, + &remap_copy_internal, + &remap_incoming); + + cloner_.SetSuccessorRemappingInfo(&remap_orig_internal, &remap_copy_internal, &remap_incoming); + cloner_.Run(); + cloner_.CleanUp(); + + // Check that loop info is preserved. + DCHECK(loop_info_ == loop_header->GetLoopInformation()); + + return loop_header; +} + +PeelUnrollSimpleHelper::PeelUnrollSimpleHelper(HLoopInformation* info) + : bb_map_(std::less(), + info->GetHeader()->GetGraph()->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)), + hir_map_(std::less(), + info->GetHeader()->GetGraph()->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)), + helper_(info, &bb_map_, &hir_map_) {} + } // namespace art + +namespace std { + +ostream& operator<<(ostream& os, const art::HEdge& e) { + e.Dump(os); + return os; +} + +} // namespace std diff --git a/compiler/optimizing/superblock_cloner.h b/compiler/optimizing/superblock_cloner.h index 23de6926735d1b64c4b92a4cc861dcd46f89a2de..f21172131b392281557a0350024616dd5b0985e3 100644 --- a/compiler/optimizing/superblock_cloner.h +++ b/compiler/optimizing/superblock_cloner.h @@ -25,7 +25,6 @@ namespace art { static const bool kSuperblockClonerLogging = false; -static const bool kSuperblockClonerVerify = false; // Represents an edge between two HBasicBlocks. // @@ -152,6 +151,15 @@ class SuperblockCloner : public ValueObject { // TODO: Start from small range of graph patterns then extend it. bool IsSubgraphClonable() const; + // Returns whether selected subgraph satisfies the criteria for fast data flow resolution + // when iterative DF algorithm is not required and dominators/instructions inputs can be + // trivially adjusted. + // + // TODO: formally describe the criteria. + // + // Loop peeling and unrolling satisfy the criteria. + bool IsFastCase() const; + // Runs the copy algorithm according to the description. void Run(); @@ -202,11 +210,17 @@ class SuperblockCloner : public ValueObject { return IsInOrigBBSet(block->GetBlockId()); } + // Returns the area (the most outer loop) in the graph for which control flow (back edges, loops, + // dominators) needs to be adjusted. + HLoopInformation* GetRegionToBeAdjusted() const { + return outer_loop_; + } + private: // Fills the 'exits' vector with the subgraph exits. - void SearchForSubgraphExits(ArenaVector* exits); + void SearchForSubgraphExits(ArenaVector* exits) const; - // Finds and records information about the area in the graph for which control-flow (back edges, + // Finds and records information about the area in the graph for which control flow (back edges, // loops, dominators) needs to be adjusted. void FindAndSetLocalAreaForAdjustments(); @@ -217,7 +231,7 @@ class SuperblockCloner : public ValueObject { // phis' nor instructions' inputs values are resolved. void RemapEdgesSuccessors(); - // Adjusts control-flow (back edges, loops, dominators) for the local area defined by + // Adjusts control flow (back edges, loops, dominators) for the local area defined by // FindAndSetLocalAreaForAdjustments. void AdjustControlFlowInfo(); @@ -225,6 +239,33 @@ class SuperblockCloner : public ValueObject { // the SSA form. void ResolveDataFlow(); + // + // Helpers for live-outs processing and Subgraph-closed SSA. + // + // - live-outs - values which are defined inside the subgraph and have uses outside. + // - Subgraph-closed SSA - SSA form for which all the values defined inside the subgraph + // have no outside uses except for the phi-nodes in the subgraph exits. + // + // Note: now if the subgraph has live-outs it is only clonable if it has a single exit; this + // makes the subgraph-closed SSA form construction much easier. + // + // TODO: Support subgraphs with live-outs and multiple exits. + // + + // For each live-out value 'val' in the region puts a record into the map. + // Returns whether all of the instructions in the subgraph are clonable. + bool CollectLiveOutsAndCheckClonable(HInstructionMap* live_outs_) const; + + // Constructs Subgraph-closed SSA; precondition - a subgraph has a single exit. + // + // For each live-out 'val' in 'live_outs_' map inserts a HPhi 'phi' into the exit node, updates + // the record in the map to and replaces all outside uses with this phi. + void ConstructSubgraphClosedSSA(); + + // Fixes the data flow for the live-out 'val' by adding a 'copy_val' input to the corresponding + // () phi after the cloning is done. + void FixSubgraphClosedSSAAfterCloning(); + // // Helpers for CloneBasicBlock. // @@ -272,6 +313,9 @@ class SuperblockCloner : public ValueObject { // Debug and logging methods. // void CheckInstructionInputsRemapping(HInstruction* orig_instr); + bool CheckRemappingInfoIsValid(); + void VerifyGraph(); + void DumpInputSets(); HBasicBlock* GetBlockById(uint32_t block_id) const { DCHECK(block_id < graph_->GetBlocks().size()); @@ -295,15 +339,99 @@ class SuperblockCloner : public ValueObject { HBasicBlockMap* bb_map_; // Correspondence map for instructions: (original HInstruction, copy HInstruction). HInstructionMap* hir_map_; - // Area in the graph for which control-flow (back edges, loops, dominators) needs to be adjusted. + // Area in the graph for which control flow (back edges, loops, dominators) needs to be adjusted. HLoopInformation* outer_loop_; HBasicBlockSet outer_loop_bb_set_; + HInstructionMap live_outs_; + ART_FRIEND_TEST(SuperblockClonerTest, AdjustControlFlowInfo); + ART_FRIEND_TEST(SuperblockClonerTest, IsGraphConnected); DISALLOW_COPY_AND_ASSIGN(SuperblockCloner); }; +// Helper class to perform loop peeling/unrolling. +// +// This helper should be used when correspondence map between original and copied +// basic blocks/instructions are demanded. +class PeelUnrollHelper : public ValueObject { + public: + explicit PeelUnrollHelper(HLoopInformation* info, + SuperblockCloner::HBasicBlockMap* bb_map, + SuperblockCloner::HInstructionMap* hir_map) : + loop_info_(info), + cloner_(info->GetHeader()->GetGraph(), &info->GetBlocks(), bb_map, hir_map) { + // For now do peeling/unrolling only for natural loops. + DCHECK(!info->IsIrreducible()); + } + + // Returns whether the loop can be peeled/unrolled (static function). + static bool IsLoopClonable(HLoopInformation* loop_info); + + // Returns whether the loop can be peeled/unrolled. + bool IsLoopClonable() const { return cloner_.IsSubgraphClonable(); } + + HBasicBlock* DoPeeling() { return DoPeelUnrollImpl(/* to_unroll */ false); } + HBasicBlock* DoUnrolling() { return DoPeelUnrollImpl(/* to_unroll */ true); } + HLoopInformation* GetRegionToBeAdjusted() const { return cloner_.GetRegionToBeAdjusted(); } + + protected: + // Applies loop peeling/unrolling for the loop specified by 'loop_info'. + // + // Depending on 'do_unroll' either unrolls loop by 2 or peels one iteration from it. + HBasicBlock* DoPeelUnrollImpl(bool to_unroll); + + private: + HLoopInformation* loop_info_; + SuperblockCloner cloner_; + + DISALLOW_COPY_AND_ASSIGN(PeelUnrollHelper); +}; + +// Helper class to perform loop peeling/unrolling. +// +// This helper should be used when there is no need to get correspondence information between +// original and copied basic blocks/instructions. +class PeelUnrollSimpleHelper : public ValueObject { + public: + explicit PeelUnrollSimpleHelper(HLoopInformation* info); + bool IsLoopClonable() const { return helper_.IsLoopClonable(); } + HBasicBlock* DoPeeling() { return helper_.DoPeeling(); } + HBasicBlock* DoUnrolling() { return helper_.DoUnrolling(); } + HLoopInformation* GetRegionToBeAdjusted() const { return helper_.GetRegionToBeAdjusted(); } + + const SuperblockCloner::HBasicBlockMap* GetBasicBlockMap() const { return &bb_map_; } + const SuperblockCloner::HInstructionMap* GetInstructionMap() const { return &hir_map_; } + + private: + SuperblockCloner::HBasicBlockMap bb_map_; + SuperblockCloner::HInstructionMap hir_map_; + PeelUnrollHelper helper_; + + DISALLOW_COPY_AND_ASSIGN(PeelUnrollSimpleHelper); +}; + +// Collects edge remapping info for loop peeling/unrolling for the loop specified by loop info. +void CollectRemappingInfoForPeelUnroll(bool to_unroll, + HLoopInformation* loop_info, + SuperblockCloner::HEdgeSet* remap_orig_internal, + SuperblockCloner::HEdgeSet* remap_copy_internal, + SuperblockCloner::HEdgeSet* remap_incoming); + +// Returns whether blocks from 'work_set' are reachable from the rest of the graph. +// +// Returns whether such a set 'outer_entries' of basic blocks exists that: +// - each block from 'outer_entries' is not from 'work_set'. +// - each block from 'work_set' is reachable from at least one block from 'outer_entries'. +// +// After the function returns work_set contains only blocks from the original 'work_set' +// which are unreachable from the rest of the graph. +bool IsSubgraphConnected(SuperblockCloner::HBasicBlockSet* work_set, HGraph* graph); + +// Returns a common predecessor of loop1 and loop2 in the loop tree or nullptr if it is the whole +// graph. +HLoopInformation* FindCommonLoop(HLoopInformation* loop1, HLoopInformation* loop2); } // namespace art namespace std { @@ -312,11 +440,12 @@ template <> struct hash { size_t operator()(art::HEdge const& x) const noexcept { // Use Cantor pairing function as the hash function. - uint32_t a = x.GetFrom(); - uint32_t b = x.GetTo(); + size_t a = x.GetFrom(); + size_t b = x.GetTo(); return (a + b) * (a + b + 1) / 2 + b; } }; +ostream& operator<<(ostream& os, const art::HEdge& e); } // namespace std diff --git a/compiler/optimizing/superblock_cloner_test.cc b/compiler/optimizing/superblock_cloner_test.cc index f1b7bffdf5f6ea39a7565842287218190528e605..31114b6dccef9f949f7280a4a88eb8619eb202df 100644 --- a/compiler/optimizing/superblock_cloner_test.cc +++ b/compiler/optimizing/superblock_cloner_test.cc @@ -25,52 +25,35 @@ namespace art { using HBasicBlockMap = SuperblockCloner::HBasicBlockMap; using HInstructionMap = SuperblockCloner::HInstructionMap; +using HBasicBlockSet = SuperblockCloner::HBasicBlockSet; +using HEdgeSet = SuperblockCloner::HEdgeSet; // This class provides methods and helpers for testing various cloning and copying routines: // individual instruction cloning and cloning of the more coarse-grain structures. -class SuperblockClonerTest : public OptimizingUnitTest { +class SuperblockClonerTest : public ImprovedOptimizingUnitTest { public: - SuperblockClonerTest() - : graph_(CreateGraph()), entry_block_(nullptr), exit_block_(nullptr), parameter_(nullptr) {} - - void CreateBasicLoopControlFlow(/* out */ HBasicBlock** header_p, + void CreateBasicLoopControlFlow(HBasicBlock* position, + HBasicBlock* successor, + /* out */ HBasicBlock** header_p, /* out */ HBasicBlock** body_p) { - entry_block_ = new (GetAllocator()) HBasicBlock(graph_); - graph_->AddBlock(entry_block_); - graph_->SetEntryBlock(entry_block_); - HBasicBlock* loop_preheader = new (GetAllocator()) HBasicBlock(graph_); HBasicBlock* loop_header = new (GetAllocator()) HBasicBlock(graph_); HBasicBlock* loop_body = new (GetAllocator()) HBasicBlock(graph_); - HBasicBlock* loop_exit = new (GetAllocator()) HBasicBlock(graph_); graph_->AddBlock(loop_preheader); graph_->AddBlock(loop_header); graph_->AddBlock(loop_body); - graph_->AddBlock(loop_exit); - exit_block_ = new (GetAllocator()) HBasicBlock(graph_); - graph_->AddBlock(exit_block_); - graph_->SetExitBlock(exit_block_); + position->ReplaceSuccessor(successor, loop_preheader); - entry_block_->AddSuccessor(loop_preheader); loop_preheader->AddSuccessor(loop_header); // Loop exit first to have a proper exit condition/target for HIf. - loop_header->AddSuccessor(loop_exit); + loop_header->AddSuccessor(successor); loop_header->AddSuccessor(loop_body); loop_body->AddSuccessor(loop_header); - loop_exit->AddSuccessor(exit_block_); *header_p = loop_header; *body_p = loop_body; - - parameter_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(), - dex::TypeIndex(0), - 0, - DataType::Type::kInt32); - entry_block_->AddInstruction(parameter_); - loop_exit->AddInstruction(new (GetAllocator()) HReturnVoid()); - exit_block_->AddInstruction(new (GetAllocator()) HExit()); } void CreateBasicLoopDataFlow(HBasicBlock* loop_header, HBasicBlock* loop_body) { @@ -84,11 +67,12 @@ class SuperblockClonerTest : public OptimizingUnitTest { // Header block. HPhi* phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32); HInstruction* suspend_check = new (GetAllocator()) HSuspendCheck(); + HInstruction* loop_check = new (GetAllocator()) HGreaterThanOrEqual(phi, const_128); loop_header->AddPhi(phi); loop_header->AddInstruction(suspend_check); - loop_header->AddInstruction(new (GetAllocator()) HGreaterThanOrEqual(phi, const_128)); - loop_header->AddInstruction(new (GetAllocator()) HIf(parameter_)); + loop_header->AddInstruction(loop_check); + loop_header->AddInstruction(new (GetAllocator()) HIf(loop_check)); // Loop body block. HInstruction* null_check = new (GetAllocator()) HNullCheck(parameter_, dex_pc); @@ -97,8 +81,8 @@ class SuperblockClonerTest : public OptimizingUnitTest { HInstruction* array_get = new (GetAllocator()) HArrayGet(null_check, bounds_check, DataType::Type::kInt32, dex_pc); HInstruction* add = new (GetAllocator()) HAdd(DataType::Type::kInt32, array_get, const_1); - HInstruction* array_set = - new (GetAllocator()) HArraySet(null_check, bounds_check, add, DataType::Type::kInt32, dex_pc); + HInstruction* array_set = new (GetAllocator()) HArraySet( + null_check, bounds_check, add, DataType::Type::kInt32, dex_pc); HInstruction* induction_inc = new (GetAllocator()) HAdd(DataType::Type::kInt32, phi, const_1); loop_body->AddInstruction(null_check); @@ -123,49 +107,17 @@ class SuperblockClonerTest : public OptimizingUnitTest { null_check->CopyEnvironmentFrom(env); bounds_check->CopyEnvironmentFrom(env); } - - HEnvironment* ManuallyBuildEnvFor(HInstruction* instruction, - ArenaVector* current_locals) { - HEnvironment* environment = new (GetAllocator()) HEnvironment( - (GetAllocator()), - current_locals->size(), - graph_->GetArtMethod(), - instruction->GetDexPc(), - instruction); - - environment->CopyFrom(ArrayRef(*current_locals)); - instruction->SetRawEnvironment(environment); - return environment; - } - - bool CheckGraph() { - GraphChecker checker(graph_); - checker.Run(); - if (!checker.IsValid()) { - for (const std::string& error : checker.GetErrors()) { - std::cout << error << std::endl; - } - return false; - } - return true; - } - - HGraph* graph_; - - HBasicBlock* entry_block_; - HBasicBlock* exit_block_; - - HInstruction* parameter_; }; TEST_F(SuperblockClonerTest, IndividualInstrCloner) { HBasicBlock* header = nullptr; HBasicBlock* loop_body = nullptr; - CreateBasicLoopControlFlow(&header, &loop_body); + InitGraph(); + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); CreateBasicLoopDataFlow(header, loop_body); graph_->BuildDominatorTree(); - ASSERT_TRUE(CheckGraph()); + EXPECT_TRUE(CheckGraph()); HSuspendCheck* old_suspend_check = header->GetLoopInformation()->GetSuspendCheck(); CloneAndReplaceInstructionVisitor visitor(graph_); @@ -193,7 +145,8 @@ TEST_F(SuperblockClonerTest, CloneBasicBlocks) { HBasicBlock* loop_body = nullptr; ArenaAllocator* arena = graph_->GetAllocator(); - CreateBasicLoopControlFlow(&header, &loop_body); + InitGraph(); + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); CreateBasicLoopDataFlow(header, loop_body); graph_->BuildDominatorTree(); ASSERT_TRUE(CheckGraph()); @@ -272,7 +225,8 @@ TEST_F(SuperblockClonerTest, AdjustControlFlowInfo) { HBasicBlock* loop_body = nullptr; ArenaAllocator* arena = graph_->GetAllocator(); - CreateBasicLoopControlFlow(&header, &loop_body); + InitGraph(); + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); CreateBasicLoopDataFlow(header, loop_body); graph_->BuildDominatorTree(); ASSERT_TRUE(CheckGraph()); @@ -303,4 +257,487 @@ TEST_F(SuperblockClonerTest, AdjustControlFlowInfo) { EXPECT_TRUE(loop_info->IsBackEdge(*loop_body)); } +// Tests IsSubgraphConnected function for negative case. +TEST_F(SuperblockClonerTest, IsGraphConnected) { + HBasicBlock* header = nullptr; + HBasicBlock* loop_body = nullptr; + ArenaAllocator* arena = graph_->GetAllocator(); + + InitGraph(); + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* unreachable_block = new (GetAllocator()) HBasicBlock(graph_); + graph_->AddBlock(unreachable_block); + + HBasicBlockSet bb_set( + arena, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner); + bb_set.SetBit(header->GetBlockId()); + bb_set.SetBit(loop_body->GetBlockId()); + bb_set.SetBit(unreachable_block->GetBlockId()); + + EXPECT_FALSE(IsSubgraphConnected(&bb_set, graph_)); + EXPECT_EQ(bb_set.NumSetBits(), 1u); + EXPECT_TRUE(bb_set.IsBitSet(unreachable_block->GetBlockId())); +} + +// Tests SuperblockCloner for loop peeling case. +// +// Control Flow of the example (ignoring critical edges splitting). +// +// Before After +// +// |B| |B| +// | | +// v v +// |1| |1| +// | | +// v v +// |2|<-\ (6) |2A| +// / \ / / \ +// v v/ / v +// |4| |3| / |3A| (7) +// | / / +// v | v +// |E| \ |2|<-\ +// \ / \ / +// v v / +// |4| |3| +// | +// v +// |E| +TEST_F(SuperblockClonerTest, LoopPeeling) { + HBasicBlock* header = nullptr; + HBasicBlock* loop_body = nullptr; + + InitGraph(); + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + graph_->BuildDominatorTree(); + EXPECT_TRUE(CheckGraph()); + + HBasicBlockMap bb_map( + std::less(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + HInstructionMap hir_map( + std::less(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + + HLoopInformation* loop_info = header->GetLoopInformation(); + PeelUnrollHelper helper(loop_info, &bb_map, &hir_map); + EXPECT_TRUE(helper.IsLoopClonable()); + HBasicBlock* new_header = helper.DoPeeling(); + HLoopInformation* new_loop_info = new_header->GetLoopInformation(); + + EXPECT_TRUE(CheckGraph()); + + // Check loop body successors. + EXPECT_EQ(loop_body->GetSingleSuccessor(), header); + EXPECT_EQ(bb_map.Get(loop_body)->GetSingleSuccessor(), header); + + // Check loop structure. + EXPECT_EQ(header, new_header); + EXPECT_EQ(new_loop_info->GetHeader(), header); + EXPECT_EQ(new_loop_info->GetBackEdges().size(), 1u); + EXPECT_EQ(new_loop_info->GetBackEdges()[0], loop_body); +} + +// Tests SuperblockCloner for loop unrolling case. +// +// Control Flow of the example (ignoring critical edges splitting). +// +// Before After +// +// |B| |B| +// | | +// v v +// |1| |1| +// | | +// v v +// |2|<-\ (6) |2A|<-\ +// / \ / / \ \ +// v v/ / v \ +// |4| |3| /(7)|3A| | +// | / / / +// v | v / +// |E| \ |2| / +// \ / \ / +// v v/ +// |4| |3| +// | +// v +// |E| +TEST_F(SuperblockClonerTest, LoopUnrolling) { + HBasicBlock* header = nullptr; + HBasicBlock* loop_body = nullptr; + + InitGraph(); + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + graph_->BuildDominatorTree(); + EXPECT_TRUE(CheckGraph()); + + HBasicBlockMap bb_map( + std::less(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + HInstructionMap hir_map( + std::less(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + + HLoopInformation* loop_info = header->GetLoopInformation(); + PeelUnrollHelper helper(loop_info, &bb_map, &hir_map); + EXPECT_TRUE(helper.IsLoopClonable()); + HBasicBlock* new_header = helper.DoUnrolling(); + + EXPECT_TRUE(CheckGraph()); + + // Check loop body successors. + EXPECT_EQ(loop_body->GetSingleSuccessor(), bb_map.Get(header)); + EXPECT_EQ(bb_map.Get(loop_body)->GetSingleSuccessor(), header); + + // Check loop structure. + EXPECT_EQ(header, new_header); + EXPECT_EQ(loop_info, new_header->GetLoopInformation()); + EXPECT_EQ(loop_info->GetHeader(), new_header); + EXPECT_EQ(loop_info->GetBackEdges().size(), 1u); + EXPECT_EQ(loop_info->GetBackEdges()[0], bb_map.Get(loop_body)); +} + +// Checks that loop unrolling works fine for a loop with multiple back edges. Tests that after +// the transformation the loop has a single preheader. +TEST_F(SuperblockClonerTest, LoopPeelingMultipleBackEdges) { + HBasicBlock* header = nullptr; + HBasicBlock* loop_body = nullptr; + + InitGraph(); + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + + // Transform a basic loop to have multiple back edges. + HBasicBlock* latch = header->GetSuccessors()[1]; + HBasicBlock* if_block = new (GetAllocator()) HBasicBlock(graph_); + HBasicBlock* temp1 = new (GetAllocator()) HBasicBlock(graph_); + graph_->AddBlock(if_block); + graph_->AddBlock(temp1); + header->ReplaceSuccessor(latch, if_block); + if_block->AddSuccessor(latch); + if_block->AddSuccessor(temp1); + temp1->AddSuccessor(header); + + if_block->AddInstruction(new (GetAllocator()) HIf(parameter_)); + + HInstructionIterator it(header->GetPhis()); + DCHECK(!it.Done()); + HPhi* loop_phi = it.Current()->AsPhi(); + HInstruction* temp_add = new (GetAllocator()) HAdd(DataType::Type::kInt32, + loop_phi, + graph_->GetIntConstant(2)); + temp1->AddInstruction(temp_add); + temp1->AddInstruction(new (GetAllocator()) HGoto()); + loop_phi->AddInput(temp_add); + + graph_->BuildDominatorTree(); + EXPECT_TRUE(CheckGraph()); + + HLoopInformation* loop_info = header->GetLoopInformation(); + PeelUnrollSimpleHelper helper(loop_info); + HBasicBlock* new_header = helper.DoPeeling(); + EXPECT_EQ(header, new_header); + + EXPECT_TRUE(CheckGraph()); + EXPECT_EQ(header->GetPredecessors().size(), 3u); +} + +static void CheckLoopStructureForLoopPeelingNested(HBasicBlock* loop1_header, + HBasicBlock* loop2_header, + HBasicBlock* loop3_header) { + EXPECT_EQ(loop1_header->GetLoopInformation()->GetHeader(), loop1_header); + EXPECT_EQ(loop2_header->GetLoopInformation()->GetHeader(), loop2_header); + EXPECT_EQ(loop3_header->GetLoopInformation()->GetHeader(), loop3_header); + EXPECT_EQ(loop1_header->GetLoopInformation()->GetPreHeader()->GetLoopInformation(), nullptr); + EXPECT_EQ(loop2_header->GetLoopInformation()->GetPreHeader()->GetLoopInformation(), nullptr); + EXPECT_EQ(loop3_header->GetLoopInformation()->GetPreHeader()->GetLoopInformation()->GetHeader(), + loop2_header); +} + +TEST_F(SuperblockClonerTest, LoopPeelingNested) { + HBasicBlock* header = nullptr; + HBasicBlock* loop_body = nullptr; + + InitGraph(); + + // Create the following nested structure of loops + // Headers: 1 2 3 + // [ ], [ [ ] ] + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop1_header = header; + + CreateBasicLoopControlFlow(header, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop2_header = header; + + CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop3_header = header; + + graph_->BuildDominatorTree(); + EXPECT_TRUE(CheckGraph()); + + HLoopInformation* loop2_info_before = loop2_header->GetLoopInformation(); + HLoopInformation* loop3_info_before = loop3_header->GetLoopInformation(); + + // Check nested loops structure. + CheckLoopStructureForLoopPeelingNested(loop1_header, loop2_header, loop3_header); + PeelUnrollSimpleHelper helper(loop1_header->GetLoopInformation()); + helper.DoPeeling(); + // Check that nested loops structure has not changed after the transformation. + CheckLoopStructureForLoopPeelingNested(loop1_header, loop2_header, loop3_header); + + // Test that the loop info is preserved. + EXPECT_EQ(loop2_info_before, loop2_header->GetLoopInformation()); + EXPECT_EQ(loop3_info_before, loop3_header->GetLoopInformation()); + + EXPECT_EQ(loop3_info_before->GetPreHeader()->GetLoopInformation(), loop2_info_before); + EXPECT_EQ(loop2_info_before->GetPreHeader()->GetLoopInformation(), nullptr); + + EXPECT_EQ(helper.GetRegionToBeAdjusted(), nullptr); + + EXPECT_TRUE(CheckGraph()); +} + +// Checks that the loop population is correctly propagated after an inner loop is peeled. +TEST_F(SuperblockClonerTest, OuterLoopPopulationAfterInnerPeeled) { + HBasicBlock* header = nullptr; + HBasicBlock* loop_body = nullptr; + + InitGraph(); + + // Create the following nested structure of loops + // Headers: 1 2 3 4 + // [ [ [ ] ] ], [ ] + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop1_header = header; + + CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop2_header = header; + + CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop3_header = header; + + CreateBasicLoopControlFlow(loop1_header, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop4_header = header; + + graph_->BuildDominatorTree(); + EXPECT_TRUE(CheckGraph()); + + PeelUnrollSimpleHelper helper(loop3_header->GetLoopInformation()); + helper.DoPeeling(); + HLoopInformation* loop1 = loop1_header->GetLoopInformation(); + HLoopInformation* loop2 = loop2_header->GetLoopInformation(); + HLoopInformation* loop3 = loop3_header->GetLoopInformation(); + HLoopInformation* loop4 = loop4_header->GetLoopInformation(); + + EXPECT_TRUE(loop1->Contains(*loop2_header)); + EXPECT_TRUE(loop1->Contains(*loop3_header)); + EXPECT_TRUE(loop1->Contains(*loop3_header->GetLoopInformation()->GetPreHeader())); + + // Check that loop4 info has not been touched after local run of AnalyzeLoops. + EXPECT_EQ(loop4, loop4_header->GetLoopInformation()); + + EXPECT_TRUE(loop1->IsIn(*loop1)); + EXPECT_TRUE(loop2->IsIn(*loop1)); + EXPECT_TRUE(loop3->IsIn(*loop1)); + EXPECT_TRUE(loop3->IsIn(*loop2)); + EXPECT_TRUE(!loop4->IsIn(*loop1)); + + EXPECT_EQ(loop4->GetPreHeader()->GetLoopInformation(), nullptr); + + EXPECT_EQ(helper.GetRegionToBeAdjusted(), loop2); + + EXPECT_TRUE(CheckGraph()); +} + +// Checks the case when inner loop have an exit not to its immediate outer_loop but some other loop +// in the hierarchy. Loop population information must be valid after loop peeling. +TEST_F(SuperblockClonerTest, NestedCaseExitToOutermost) { + HBasicBlock* header = nullptr; + HBasicBlock* loop_body = nullptr; + + InitGraph(); + + // Create the following nested structure of loops then peel loop3. + // Headers: 1 2 3 + // [ [ [ ] ] ] + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop1_header = header; + HBasicBlock* loop_body1 = loop_body; + + CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + + CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop3_header = header; + HBasicBlock* loop_body3 = loop_body; + + // Change the loop3 - insert an exit which leads to loop1. + HBasicBlock* loop3_extra_if_block = new (GetAllocator()) HBasicBlock(graph_); + graph_->AddBlock(loop3_extra_if_block); + loop3_extra_if_block->AddInstruction(new (GetAllocator()) HIf(parameter_)); + + loop3_header->ReplaceSuccessor(loop_body3, loop3_extra_if_block); + loop3_extra_if_block->AddSuccessor(loop_body1); // Long exit. + loop3_extra_if_block->AddSuccessor(loop_body3); + + graph_->BuildDominatorTree(); + EXPECT_TRUE(CheckGraph()); + + HBasicBlock* loop3_long_exit = loop3_extra_if_block->GetSuccessors()[0]; + EXPECT_TRUE(loop1_header->GetLoopInformation()->Contains(*loop3_long_exit)); + + PeelUnrollSimpleHelper helper(loop3_header->GetLoopInformation()); + helper.DoPeeling(); + + HLoopInformation* loop1 = loop1_header->GetLoopInformation(); + // Check that after the transformation the local area for CF adjustments has been chosen + // correctly and loop population has been updated. + loop3_long_exit = loop3_extra_if_block->GetSuccessors()[0]; + EXPECT_TRUE(loop1->Contains(*loop3_long_exit)); + + EXPECT_EQ(helper.GetRegionToBeAdjusted(), loop1); + + EXPECT_TRUE(loop1->Contains(*loop3_header)); + EXPECT_TRUE(loop1->Contains(*loop3_header->GetLoopInformation()->GetPreHeader())); + + EXPECT_TRUE(CheckGraph()); +} + +TEST_F(SuperblockClonerTest, FastCaseCheck) { + HBasicBlock* header = nullptr; + HBasicBlock* loop_body = nullptr; + ArenaAllocator* arena = graph_->GetAllocator(); + + InitGraph(); + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + graph_->BuildDominatorTree(); + + HLoopInformation* loop_info = header->GetLoopInformation(); + + ArenaBitVector orig_bb_set( + arena, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner); + orig_bb_set.Union(&loop_info->GetBlocks()); + + HEdgeSet remap_orig_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + HEdgeSet remap_copy_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + HEdgeSet remap_incoming(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + + CollectRemappingInfoForPeelUnroll(true, + loop_info, + &remap_orig_internal, + &remap_copy_internal, + &remap_incoming); + + // Insert some extra nodes and edges. + HBasicBlock* preheader = loop_info->GetPreHeader(); + orig_bb_set.SetBit(preheader->GetBlockId()); + + // Adjust incoming edges. + remap_incoming.clear(); + remap_incoming.insert(HEdge(preheader->GetSinglePredecessor(), preheader)); + + HBasicBlockMap bb_map(std::less(), arena->Adapter(kArenaAllocSuperblockCloner)); + HInstructionMap hir_map(std::less(), arena->Adapter(kArenaAllocSuperblockCloner)); + + SuperblockCloner cloner(graph_, + &orig_bb_set, + &bb_map, + &hir_map); + cloner.SetSuccessorRemappingInfo(&remap_orig_internal, &remap_copy_internal, &remap_incoming); + + EXPECT_FALSE(cloner.IsFastCase()); +} + +// Helper for FindCommonLoop which also check that FindCommonLoop is symmetric. +static HLoopInformation* FindCommonLoopCheck(HLoopInformation* loop1, HLoopInformation* loop2) { + HLoopInformation* common_loop12 = FindCommonLoop(loop1, loop2); + HLoopInformation* common_loop21 = FindCommonLoop(loop2, loop1); + EXPECT_EQ(common_loop21, common_loop12); + return common_loop12; +} + +// Tests FindCommonLoop function on a loop nest. +TEST_F(SuperblockClonerTest, FindCommonLoop) { + HBasicBlock* header = nullptr; + HBasicBlock* loop_body = nullptr; + + InitGraph(); + + // Create the following nested structure of loops + // Headers: 1 2 3 4 5 + // [ [ [ ] ], [ ] ], [ ] + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop1_header = header; + + CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop2_header = header; + + CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop3_header = header; + + CreateBasicLoopControlFlow(loop2_header, loop2_header->GetSuccessors()[0], &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop4_header = header; + + CreateBasicLoopControlFlow(loop1_header, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop5_header = header; + + graph_->BuildDominatorTree(); + EXPECT_TRUE(CheckGraph()); + + HLoopInformation* loop1 = loop1_header->GetLoopInformation(); + HLoopInformation* loop2 = loop2_header->GetLoopInformation(); + HLoopInformation* loop3 = loop3_header->GetLoopInformation(); + HLoopInformation* loop4 = loop4_header->GetLoopInformation(); + HLoopInformation* loop5 = loop5_header->GetLoopInformation(); + + EXPECT_TRUE(loop1->IsIn(*loop1)); + EXPECT_TRUE(loop2->IsIn(*loop1)); + EXPECT_TRUE(loop3->IsIn(*loop1)); + EXPECT_TRUE(loop3->IsIn(*loop2)); + EXPECT_TRUE(loop4->IsIn(*loop1)); + + EXPECT_FALSE(loop5->IsIn(*loop1)); + EXPECT_FALSE(loop4->IsIn(*loop2)); + EXPECT_FALSE(loop4->IsIn(*loop3)); + + EXPECT_EQ(loop1->GetPreHeader()->GetLoopInformation(), nullptr); + EXPECT_EQ(loop4->GetPreHeader()->GetLoopInformation(), loop1); + + EXPECT_EQ(FindCommonLoopCheck(nullptr, nullptr), nullptr); + EXPECT_EQ(FindCommonLoopCheck(loop2, nullptr), nullptr); + + EXPECT_EQ(FindCommonLoopCheck(loop1, loop1), loop1); + EXPECT_EQ(FindCommonLoopCheck(loop1, loop2), loop1); + EXPECT_EQ(FindCommonLoopCheck(loop1, loop3), loop1); + EXPECT_EQ(FindCommonLoopCheck(loop1, loop4), loop1); + EXPECT_EQ(FindCommonLoopCheck(loop1, loop5), nullptr); + + EXPECT_EQ(FindCommonLoopCheck(loop2, loop3), loop2); + EXPECT_EQ(FindCommonLoopCheck(loop2, loop4), loop1); + EXPECT_EQ(FindCommonLoopCheck(loop2, loop5), nullptr); + + EXPECT_EQ(FindCommonLoopCheck(loop3, loop4), loop1); + EXPECT_EQ(FindCommonLoopCheck(loop3, loop5), nullptr); + + EXPECT_EQ(FindCommonLoopCheck(loop4, loop5), nullptr); + + EXPECT_EQ(FindCommonLoopCheck(loop5, loop5), loop5); +} + } // namespace art diff --git a/compiler/optimizing/x86_memory_gen.cc b/compiler/optimizing/x86_memory_gen.cc index 0271850f2985ef87e752857a2e0ab540affd8267..f0069c0e09ff3bc65bb948430f6b0cab2994fee9 100644 --- a/compiler/optimizing/x86_memory_gen.cc +++ b/compiler/optimizing/x86_memory_gen.cc @@ -76,9 +76,10 @@ X86MemoryOperandGeneration::X86MemoryOperandGeneration(HGraph* graph, do_implicit_null_checks_(codegen->GetCompilerOptions().GetImplicitNullChecks()) { } -void X86MemoryOperandGeneration::Run() { +bool X86MemoryOperandGeneration::Run() { MemoryOperandVisitor visitor(graph_, do_implicit_null_checks_); visitor.VisitInsertionOrder(); + return true; } } // namespace x86 diff --git a/compiler/optimizing/x86_memory_gen.h b/compiler/optimizing/x86_memory_gen.h index 5f15d9f1e609ae0b47ea137a8161e5a5822a6c68..b254000f28ff25f5ed5e92f2a1a85ad8ba918c7c 100644 --- a/compiler/optimizing/x86_memory_gen.h +++ b/compiler/optimizing/x86_memory_gen.h @@ -31,7 +31,7 @@ class X86MemoryOperandGeneration : public HOptimization { CodeGenerator* codegen, OptimizingCompilerStats* stats); - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kX86MemoryOperandGenerationPassName = "x86_memory_operand_generation"; diff --git a/compiler/trampolines/trampoline_compiler.cc b/compiler/trampolines/trampoline_compiler.cc index 921d4018492976180f67a5a1e96ea946584ab689..26aa434c0d06a8460793f5fb9a6bf012e1ec9bcb 100644 --- a/compiler/trampolines/trampoline_compiler.cc +++ b/compiler/trampolines/trampoline_compiler.cc @@ -17,7 +17,8 @@ #include "trampoline_compiler.h" #include "base/arena_allocator.h" -#include "jni_env_ext.h" +#include "base/malloc_arena_pool.h" +#include "jni/jni_env_ext.h" #ifdef ART_ENABLE_CODEGEN_arm #include "utils/arm/assembler_arm_vixl.h" @@ -243,7 +244,7 @@ static std::unique_ptr> CreateTrampoline(ArenaAllocat std::unique_ptr> CreateTrampoline64(InstructionSet isa, EntryPointCallingConvention abi, ThreadOffset64 offset) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); switch (isa) { #ifdef ART_ENABLE_CODEGEN_arm64 @@ -269,7 +270,7 @@ std::unique_ptr> CreateTrampoline64(InstructionSet is std::unique_ptr> CreateTrampoline32(InstructionSet isa, EntryPointCallingConvention abi, ThreadOffset32 offset) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); switch (isa) { #ifdef ART_ENABLE_CODEGEN_arm diff --git a/compiler/utils/arm/constants_arm.h b/compiler/utils/arm/constants_arm.h index 66252bed865260a3fe604bf6b5c6912ad06924b5..3e316c8e84312a659b168aa0b5d968190b147531 100644 --- a/compiler/utils/arm/constants_arm.h +++ b/compiler/utils/arm/constants_arm.h @@ -25,7 +25,7 @@ #include "arch/arm/registers_arm.h" #include "base/casts.h" -#include "globals.h" +#include "base/globals.h" namespace art { namespace arm { diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc index 065c3de23cafb20cc885708edfd2847cd0153d91..c6c764e3a9fead591fed0aaa9ac7c28efcffaadf 100644 --- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc +++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc @@ -37,6 +37,29 @@ namespace arm { #define ___ asm_.GetVIXLAssembler()-> #endif +vixl::aarch32::Register AsVIXLRegister(ArmManagedRegister reg) { + CHECK(reg.IsCoreRegister()); + return vixl::aarch32::Register(reg.RegId()); +} + +static inline vixl::aarch32::SRegister AsVIXLSRegister(ArmManagedRegister reg) { + CHECK(reg.IsSRegister()); + return vixl::aarch32::SRegister(reg.RegId() - kNumberOfCoreRegIds); +} + +static inline vixl::aarch32::DRegister AsVIXLDRegister(ArmManagedRegister reg) { + CHECK(reg.IsDRegister()); + return vixl::aarch32::DRegister(reg.RegId() - kNumberOfCoreRegIds - kNumberOfSRegIds); +} + +static inline vixl::aarch32::Register AsVIXLRegisterPairLow(ArmManagedRegister reg) { + return vixl::aarch32::Register(reg.AsRegisterPairLow()); +} + +static inline vixl::aarch32::Register AsVIXLRegisterPairHigh(ArmManagedRegister reg) { + return vixl::aarch32::Register(reg.AsRegisterPairHigh()); +} + void ArmVIXLJNIMacroAssembler::FinalizeCode() { for (const std::unique_ptr< ArmVIXLJNIMacroAssembler::ArmException>& exception : exception_blocks_) { @@ -60,7 +83,7 @@ void ArmVIXLJNIMacroAssembler::BuildFrame(size_t frame_size, ArrayRef callee_save_regs, const ManagedRegisterEntrySpills& entry_spills) { CHECK_ALIGNED(frame_size, kStackAlignment); - CHECK(r0.Is(method_reg.AsArm().AsVIXLRegister())); + CHECK(r0.Is(AsVIXLRegister(method_reg.AsArm()))); // Push callee saves and link register. RegList core_spill_mask = 1 << LR; @@ -97,20 +120,19 @@ void ArmVIXLJNIMacroAssembler::BuildFrame(size_t frame_size, // Write out entry spills. int32_t offset = frame_size + kFramePointerSize; - for (size_t i = 0; i < entry_spills.size(); ++i) { - ArmManagedRegister reg = entry_spills.at(i).AsArm(); + for (const ManagedRegisterSpill& spill : entry_spills) { + ArmManagedRegister reg = spill.AsArm(); if (reg.IsNoRegister()) { // only increment stack offset. - ManagedRegisterSpill spill = entry_spills.at(i); offset += spill.getSize(); } else if (reg.IsCoreRegister()) { - asm_.StoreToOffset(kStoreWord, reg.AsVIXLRegister(), sp, offset); + asm_.StoreToOffset(kStoreWord, AsVIXLRegister(reg), sp, offset); offset += 4; } else if (reg.IsSRegister()) { - asm_.StoreSToOffset(reg.AsVIXLSRegister(), sp, offset); + asm_.StoreSToOffset(AsVIXLSRegister(reg), sp, offset); offset += 4; } else if (reg.IsDRegister()) { - asm_.StoreDToOffset(reg.AsVIXLDRegister(), sp, offset); + asm_.StoreDToOffset(AsVIXLDRegister(reg), sp, offset); offset += 8; } } @@ -208,76 +230,71 @@ void ArmVIXLJNIMacroAssembler::Store(FrameOffset dest, ManagedRegister m_src, si } else if (src.IsCoreRegister()) { CHECK_EQ(4u, size); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(src.AsVIXLRegister()); - asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value()); + temps.Exclude(AsVIXLRegister(src)); + asm_.StoreToOffset(kStoreWord, AsVIXLRegister(src), sp, dest.Int32Value()); } else if (src.IsRegisterPair()) { CHECK_EQ(8u, size); - asm_.StoreToOffset(kStoreWord, src.AsVIXLRegisterPairLow(), sp, dest.Int32Value()); - asm_.StoreToOffset(kStoreWord, src.AsVIXLRegisterPairHigh(), sp, dest.Int32Value() + 4); + asm_.StoreToOffset(kStoreWord, AsVIXLRegisterPairLow(src), sp, dest.Int32Value()); + asm_.StoreToOffset(kStoreWord, AsVIXLRegisterPairHigh(src), sp, dest.Int32Value() + 4); } else if (src.IsSRegister()) { CHECK_EQ(4u, size); - asm_.StoreSToOffset(src.AsVIXLSRegister(), sp, dest.Int32Value()); + asm_.StoreSToOffset(AsVIXLSRegister(src), sp, dest.Int32Value()); } else { CHECK_EQ(8u, size); CHECK(src.IsDRegister()) << src; - asm_.StoreDToOffset(src.AsVIXLDRegister(), sp, dest.Int32Value()); + asm_.StoreDToOffset(AsVIXLDRegister(src), sp, dest.Int32Value()); } } void ArmVIXLJNIMacroAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) { - ArmManagedRegister src = msrc.AsArm(); - CHECK(src.IsCoreRegister()) << src; + vixl::aarch32::Register src = AsVIXLRegister(msrc.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(src.AsVIXLRegister()); - asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value()); + temps.Exclude(src); + asm_.StoreToOffset(kStoreWord, src, sp, dest.Int32Value()); } void ArmVIXLJNIMacroAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) { - ArmManagedRegister src = msrc.AsArm(); - CHECK(src.IsCoreRegister()) << src; + vixl::aarch32::Register src = AsVIXLRegister(msrc.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(src.AsVIXLRegister()); - asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value()); + temps.Exclude(src); + asm_.StoreToOffset(kStoreWord, src, sp, dest.Int32Value()); } void ArmVIXLJNIMacroAssembler::StoreSpanning(FrameOffset dest, ManagedRegister msrc, FrameOffset in_off, ManagedRegister mscratch) { - ArmManagedRegister src = msrc.AsArm(); - ArmManagedRegister scratch = mscratch.AsArm(); - asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value()); + vixl::aarch32::Register src = AsVIXLRegister(msrc.AsArm()); + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); + asm_.StoreToOffset(kStoreWord, src, sp, dest.Int32Value()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); - asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, in_off.Int32Value()); - asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, dest.Int32Value() + 4); + temps.Exclude(scratch); + asm_.LoadFromOffset(kLoadWord, scratch, sp, in_off.Int32Value()); + asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value() + 4); } void ArmVIXLJNIMacroAssembler::CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister mscratch) { - ArmManagedRegister scratch = mscratch.AsArm(); + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); - asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, src.Int32Value()); - asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, dest.Int32Value()); + temps.Exclude(scratch); + asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value()); + asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value()); } -void ArmVIXLJNIMacroAssembler::LoadRef(ManagedRegister dest, - ManagedRegister base, +void ArmVIXLJNIMacroAssembler::LoadRef(ManagedRegister mdest, + ManagedRegister mbase, MemberOffset offs, bool unpoison_reference) { - ArmManagedRegister dst = dest.AsArm(); - CHECK(dst.IsCoreRegister() && dst.IsCoreRegister()) << dst; + vixl::aarch32::Register dest = AsVIXLRegister(mdest.AsArm()); + vixl::aarch32::Register base = AsVIXLRegister(mbase.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(dst.AsVIXLRegister(), base.AsArm().AsVIXLRegister()); - asm_.LoadFromOffset(kLoadWord, - dst.AsVIXLRegister(), - base.AsArm().AsVIXLRegister(), - offs.Int32Value()); + temps.Exclude(dest, base); + asm_.LoadFromOffset(kLoadWord, dest, base, offs.Int32Value()); if (unpoison_reference) { - asm_.MaybeUnpoisonHeapReference(dst.AsVIXLRegister()); + asm_.MaybeUnpoisonHeapReference(dest); } } @@ -294,13 +311,12 @@ void ArmVIXLJNIMacroAssembler::LoadRawPtr(ManagedRegister dest ATTRIBUTE_UNUSED, void ArmVIXLJNIMacroAssembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm, - ManagedRegister scratch) { - ArmManagedRegister mscratch = scratch.AsArm(); - CHECK(mscratch.IsCoreRegister()) << mscratch; + ManagedRegister mscratch) { + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(mscratch.AsVIXLRegister()); - asm_.LoadImmediate(mscratch.AsVIXLRegister(), imm); - asm_.StoreToOffset(kStoreWord, mscratch.AsVIXLRegister(), sp, dest.Int32Value()); + temps.Exclude(scratch); + asm_.LoadImmediate(scratch, imm); + asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value()); } void ArmVIXLJNIMacroAssembler::Load(ManagedRegister m_dst, FrameOffset src, size_t size) { @@ -313,23 +329,21 @@ void ArmVIXLJNIMacroAssembler::LoadFromThread(ManagedRegister m_dst, return Load(m_dst.AsArm(), tr, src.Int32Value(), size); } -void ArmVIXLJNIMacroAssembler::LoadRawPtrFromThread(ManagedRegister m_dst, ThreadOffset32 offs) { - ArmManagedRegister dst = m_dst.AsArm(); - CHECK(dst.IsCoreRegister()) << dst; +void ArmVIXLJNIMacroAssembler::LoadRawPtrFromThread(ManagedRegister mdest, ThreadOffset32 offs) { + vixl::aarch32::Register dest = AsVIXLRegister(mdest.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(dst.AsVIXLRegister()); - asm_.LoadFromOffset(kLoadWord, dst.AsVIXLRegister(), tr, offs.Int32Value()); + temps.Exclude(dest); + asm_.LoadFromOffset(kLoadWord, dest, tr, offs.Int32Value()); } void ArmVIXLJNIMacroAssembler::CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset32 thr_offs, ManagedRegister mscratch) { - ArmManagedRegister scratch = mscratch.AsArm(); - CHECK(scratch.IsCoreRegister()) << scratch; + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); - asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), tr, thr_offs.Int32Value()); - asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, fr_offs.Int32Value()); + temps.Exclude(scratch); + asm_.LoadFromOffset(kLoadWord, scratch, tr, thr_offs.Int32Value()); + asm_.StoreToOffset(kStoreWord, scratch, sp, fr_offs.Int32Value()); } void ArmVIXLJNIMacroAssembler::CopyRawPtrToThread(ThreadOffset32 thr_offs ATTRIBUTE_UNUSED, @@ -341,12 +355,11 @@ void ArmVIXLJNIMacroAssembler::CopyRawPtrToThread(ThreadOffset32 thr_offs ATTRIB void ArmVIXLJNIMacroAssembler::StoreStackOffsetToThread(ThreadOffset32 thr_offs, FrameOffset fr_offs, ManagedRegister mscratch) { - ArmManagedRegister scratch = mscratch.AsArm(); - CHECK(scratch.IsCoreRegister()) << scratch; + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); - asm_.AddConstant(scratch.AsVIXLRegister(), sp, fr_offs.Int32Value()); - asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), tr, thr_offs.Int32Value()); + temps.Exclude(scratch); + asm_.AddConstant(scratch, sp, fr_offs.Int32Value()); + asm_.StoreToOffset(kStoreWord, scratch, tr, thr_offs.Int32Value()); } void ArmVIXLJNIMacroAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs) { @@ -363,43 +376,43 @@ void ArmVIXLJNIMacroAssembler::ZeroExtend(ManagedRegister mreg ATTRIBUTE_UNUSED, UNIMPLEMENTED(FATAL) << "no zero extension necessary for arm"; } -void ArmVIXLJNIMacroAssembler::Move(ManagedRegister m_dst, - ManagedRegister m_src, +void ArmVIXLJNIMacroAssembler::Move(ManagedRegister mdst, + ManagedRegister msrc, size_t size ATTRIBUTE_UNUSED) { - ArmManagedRegister dst = m_dst.AsArm(); - ArmManagedRegister src = m_src.AsArm(); + ArmManagedRegister dst = mdst.AsArm(); + ArmManagedRegister src = msrc.AsArm(); if (!dst.Equals(src)) { if (dst.IsCoreRegister()) { CHECK(src.IsCoreRegister()) << src; UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(dst.AsVIXLRegister()); - ___ Mov(dst.AsVIXLRegister(), src.AsVIXLRegister()); + temps.Exclude(AsVIXLRegister(dst)); + ___ Mov(AsVIXLRegister(dst), AsVIXLRegister(src)); } else if (dst.IsDRegister()) { if (src.IsDRegister()) { - ___ Vmov(F64, dst.AsVIXLDRegister(), src.AsVIXLDRegister()); + ___ Vmov(F64, AsVIXLDRegister(dst), AsVIXLDRegister(src)); } else { // VMOV Dn, Rlo, Rhi (Dn = {Rlo, Rhi}) CHECK(src.IsRegisterPair()) << src; - ___ Vmov(dst.AsVIXLDRegister(), src.AsVIXLRegisterPairLow(), src.AsVIXLRegisterPairHigh()); + ___ Vmov(AsVIXLDRegister(dst), AsVIXLRegisterPairLow(src), AsVIXLRegisterPairHigh(src)); } } else if (dst.IsSRegister()) { if (src.IsSRegister()) { - ___ Vmov(F32, dst.AsVIXLSRegister(), src.AsVIXLSRegister()); + ___ Vmov(F32, AsVIXLSRegister(dst), AsVIXLSRegister(src)); } else { // VMOV Sn, Rn (Sn = Rn) CHECK(src.IsCoreRegister()) << src; - ___ Vmov(dst.AsVIXLSRegister(), src.AsVIXLRegister()); + ___ Vmov(AsVIXLSRegister(dst), AsVIXLRegister(src)); } } else { CHECK(dst.IsRegisterPair()) << dst; CHECK(src.IsRegisterPair()) << src; // Ensure that the first move doesn't clobber the input of the second. if (src.AsRegisterPairHigh() != dst.AsRegisterPairLow()) { - ___ Mov(dst.AsVIXLRegisterPairLow(), src.AsVIXLRegisterPairLow()); - ___ Mov(dst.AsVIXLRegisterPairHigh(), src.AsVIXLRegisterPairHigh()); + ___ Mov(AsVIXLRegisterPairLow(dst), AsVIXLRegisterPairLow(src)); + ___ Mov(AsVIXLRegisterPairHigh(dst), AsVIXLRegisterPairHigh(src)); } else { - ___ Mov(dst.AsVIXLRegisterPairHigh(), src.AsVIXLRegisterPairHigh()); - ___ Mov(dst.AsVIXLRegisterPairLow(), src.AsVIXLRegisterPairLow()); + ___ Mov(AsVIXLRegisterPairHigh(dst), AsVIXLRegisterPairHigh(src)); + ___ Mov(AsVIXLRegisterPairLow(dst), AsVIXLRegisterPairLow(src)); } } } @@ -407,21 +420,20 @@ void ArmVIXLJNIMacroAssembler::Move(ManagedRegister m_dst, void ArmVIXLJNIMacroAssembler::Copy(FrameOffset dest, FrameOffset src, - ManagedRegister scratch, + ManagedRegister mscratch, size_t size) { - ArmManagedRegister temp = scratch.AsArm(); - CHECK(temp.IsCoreRegister()) << temp; + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); CHECK(size == 4 || size == 8) << size; UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(temp.AsVIXLRegister()); + temps.Exclude(scratch); if (size == 4) { - asm_.LoadFromOffset(kLoadWord, temp.AsVIXLRegister(), sp, src.Int32Value()); - asm_.StoreToOffset(kStoreWord, temp.AsVIXLRegister(), sp, dest.Int32Value()); + asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value()); + asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value()); } else if (size == 8) { - asm_.LoadFromOffset(kLoadWord, temp.AsVIXLRegister(), sp, src.Int32Value()); - asm_.StoreToOffset(kStoreWord, temp.AsVIXLRegister(), sp, dest.Int32Value()); - asm_.LoadFromOffset(kLoadWord, temp.AsVIXLRegister(), sp, src.Int32Value() + 4); - asm_.StoreToOffset(kStoreWord, temp.AsVIXLRegister(), sp, dest.Int32Value() + 4); + asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value()); + asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value()); + asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value() + 4); + asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value() + 4); } } @@ -471,48 +483,44 @@ void ArmVIXLJNIMacroAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg, FrameOffset handle_scope_offset, ManagedRegister min_reg, bool null_allowed) { - ArmManagedRegister out_reg = mout_reg.AsArm(); - ArmManagedRegister in_reg = min_reg.AsArm(); - CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg; - CHECK(out_reg.IsCoreRegister()) << out_reg; + vixl::aarch32::Register out_reg = AsVIXLRegister(mout_reg.AsArm()); + vixl::aarch32::Register in_reg = + min_reg.AsArm().IsNoRegister() ? vixl::aarch32::Register() : AsVIXLRegister(min_reg.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(out_reg.AsVIXLRegister()); + temps.Exclude(out_reg); if (null_allowed) { // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is // the address in the handle scope holding the reference. // e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset) - if (in_reg.IsNoRegister()) { - asm_.LoadFromOffset(kLoadWord, - out_reg.AsVIXLRegister(), - sp, - handle_scope_offset.Int32Value()); + if (!in_reg.IsValid()) { + asm_.LoadFromOffset(kLoadWord, out_reg, sp, handle_scope_offset.Int32Value()); in_reg = out_reg; } - temps.Exclude(in_reg.AsVIXLRegister()); - ___ Cmp(in_reg.AsVIXLRegister(), 0); + temps.Exclude(in_reg); + ___ Cmp(in_reg, 0); if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value())) { - if (!out_reg.Equals(in_reg)) { + if (!out_reg.Is(in_reg)) { ExactAssemblyScope guard(asm_.GetVIXLAssembler(), 3 * vixl32::kMaxInstructionSizeInBytes, CodeBufferCheckScope::kMaximumSize); ___ it(eq, 0xc); - ___ mov(eq, out_reg.AsVIXLRegister(), 0); - asm_.AddConstantInIt(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne); + ___ mov(eq, out_reg, 0); + asm_.AddConstantInIt(out_reg, sp, handle_scope_offset.Int32Value(), ne); } else { ExactAssemblyScope guard(asm_.GetVIXLAssembler(), 2 * vixl32::kMaxInstructionSizeInBytes, CodeBufferCheckScope::kMaximumSize); ___ it(ne, 0x8); - asm_.AddConstantInIt(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne); + asm_.AddConstantInIt(out_reg, sp, handle_scope_offset.Int32Value(), ne); } } else { // TODO: Implement this (old arm assembler would have crashed here). UNIMPLEMENTED(FATAL); } } else { - asm_.AddConstant(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value()); + asm_.AddConstant(out_reg, sp, handle_scope_offset.Int32Value()); } } @@ -520,31 +528,30 @@ void ArmVIXLJNIMacroAssembler::CreateHandleScopeEntry(FrameOffset out_off, FrameOffset handle_scope_offset, ManagedRegister mscratch, bool null_allowed) { - ArmManagedRegister scratch = mscratch.AsArm(); - CHECK(scratch.IsCoreRegister()) << scratch; + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); + temps.Exclude(scratch); if (null_allowed) { - asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, handle_scope_offset.Int32Value()); + asm_.LoadFromOffset(kLoadWord, scratch, sp, handle_scope_offset.Int32Value()); // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is // the address in the handle scope holding the reference. // e.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset) - ___ Cmp(scratch.AsVIXLRegister(), 0); + ___ Cmp(scratch, 0); if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value())) { ExactAssemblyScope guard(asm_.GetVIXLAssembler(), 2 * vixl32::kMaxInstructionSizeInBytes, CodeBufferCheckScope::kMaximumSize); ___ it(ne, 0x8); - asm_.AddConstantInIt(scratch.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne); + asm_.AddConstantInIt(scratch, sp, handle_scope_offset.Int32Value(), ne); } else { // TODO: Implement this (old arm assembler would have crashed here). UNIMPLEMENTED(FATAL); } } else { - asm_.AddConstant(scratch.AsVIXLRegister(), sp, handle_scope_offset.Int32Value()); + asm_.AddConstant(scratch, sp, handle_scope_offset.Int32Value()); } - asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, out_off.Int32Value()); + asm_.StoreToOffset(kStoreWord, scratch, sp, out_off.Int32Value()); } void ArmVIXLJNIMacroAssembler::LoadReferenceFromHandleScope( @@ -566,32 +573,23 @@ void ArmVIXLJNIMacroAssembler::VerifyObject(FrameOffset src ATTRIBUTE_UNUSED, void ArmVIXLJNIMacroAssembler::Call(ManagedRegister mbase, Offset offset, ManagedRegister mscratch) { - ArmManagedRegister base = mbase.AsArm(); - ArmManagedRegister scratch = mscratch.AsArm(); - CHECK(base.IsCoreRegister()) << base; - CHECK(scratch.IsCoreRegister()) << scratch; + vixl::aarch32::Register base = AsVIXLRegister(mbase.AsArm()); + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); - asm_.LoadFromOffset(kLoadWord, - scratch.AsVIXLRegister(), - base.AsVIXLRegister(), - offset.Int32Value()); - ___ Blx(scratch.AsVIXLRegister()); + temps.Exclude(scratch); + asm_.LoadFromOffset(kLoadWord, scratch, base, offset.Int32Value()); + ___ Blx(scratch); // TODO: place reference map on call. } void ArmVIXLJNIMacroAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) { - ArmManagedRegister scratch = mscratch.AsArm(); - CHECK(scratch.IsCoreRegister()) << scratch; + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); + temps.Exclude(scratch); // Call *(*(SP + base) + offset) - asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, base.Int32Value()); - asm_.LoadFromOffset(kLoadWord, - scratch.AsVIXLRegister(), - scratch.AsVIXLRegister(), - offset.Int32Value()); - ___ Blx(scratch.AsVIXLRegister()); + asm_.LoadFromOffset(kLoadWord, scratch, sp, base.Int32Value()); + asm_.LoadFromOffset(kLoadWord, scratch, scratch, offset.Int32Value()); + ___ Blx(scratch); // TODO: place reference map on call } @@ -602,8 +600,8 @@ void ArmVIXLJNIMacroAssembler::CallFromThread(ThreadOffset32 offset ATTRIBUTE_UN void ArmVIXLJNIMacroAssembler::GetCurrentThread(ManagedRegister mtr) { UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(mtr.AsArm().AsVIXLRegister()); - ___ Mov(mtr.AsArm().AsVIXLRegister(), tr); + temps.Exclude(AsVIXLRegister(mtr.AsArm())); + ___ Mov(AsVIXLRegister(mtr.AsArm()), tr); } void ArmVIXLJNIMacroAssembler::GetCurrentThread(FrameOffset dest_offset, @@ -611,19 +609,19 @@ void ArmVIXLJNIMacroAssembler::GetCurrentThread(FrameOffset dest_offset, asm_.StoreToOffset(kStoreWord, tr, sp, dest_offset.Int32Value()); } -void ArmVIXLJNIMacroAssembler::ExceptionPoll(ManagedRegister m_scratch, size_t stack_adjust) { +void ArmVIXLJNIMacroAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) { CHECK_ALIGNED(stack_adjust, kStackAlignment); - ArmManagedRegister scratch = m_scratch.AsArm(); + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); + temps.Exclude(scratch); exception_blocks_.emplace_back( - new ArmVIXLJNIMacroAssembler::ArmException(scratch, stack_adjust)); + new ArmVIXLJNIMacroAssembler::ArmException(mscratch.AsArm(), stack_adjust)); asm_.LoadFromOffset(kLoadWord, - scratch.AsVIXLRegister(), + scratch, tr, Thread::ExceptionOffset().Int32Value()); - ___ Cmp(scratch.AsVIXLRegister(), 0); + ___ Cmp(scratch, 0); vixl32::Label* label = exception_blocks_.back()->Entry(); ___ BPreferNear(ne, label); // TODO: think about using CBNZ here. @@ -640,19 +638,18 @@ void ArmVIXLJNIMacroAssembler::Jump(JNIMacroLabel* label) { void ArmVIXLJNIMacroAssembler::Jump(JNIMacroLabel* label, JNIMacroUnaryCondition condition, - ManagedRegister test) { + ManagedRegister mtest) { CHECK(label != nullptr); + vixl::aarch32::Register test = AsVIXLRegister(mtest.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(test.AsArm().AsVIXLRegister()); + temps.Exclude(test); switch (condition) { case JNIMacroUnaryCondition::kZero: - ___ CompareAndBranchIfZero(test.AsArm().AsVIXLRegister(), - ArmVIXLJNIMacroLabel::Cast(label)->AsArm()); + ___ CompareAndBranchIfZero(test, ArmVIXLJNIMacroLabel::Cast(label)->AsArm()); break; case JNIMacroUnaryCondition::kNotZero: - ___ CompareAndBranchIfNonZero(test.AsArm().AsVIXLRegister(), - ArmVIXLJNIMacroLabel::Cast(label)->AsArm()); + ___ CompareAndBranchIfNonZero(test, ArmVIXLJNIMacroLabel::Cast(label)->AsArm()); break; default: LOG(FATAL) << "Not implemented unary condition: " << static_cast(condition); @@ -672,12 +669,13 @@ void ArmVIXLJNIMacroAssembler::EmitExceptionPoll( DecreaseFrameSize(exception->stack_adjust_); } + vixl::aarch32::Register scratch = AsVIXLRegister(exception->scratch_); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(exception->scratch_.AsVIXLRegister()); + temps.Exclude(scratch); // Pass exception object as argument. // Don't care about preserving r0 as this won't return. - ___ Mov(r0, exception->scratch_.AsVIXLRegister()); - temps.Include(exception->scratch_.AsVIXLRegister()); + ___ Mov(r0, scratch); + temps.Include(scratch); // TODO: check that exception->scratch_ is dead by this point. vixl32::Register temp = temps.Acquire(); ___ Ldr(temp, @@ -698,26 +696,27 @@ void ArmVIXLJNIMacroAssembler::Load(ArmManagedRegister if (dest.IsNoRegister()) { CHECK_EQ(0u, size) << dest; } else if (dest.IsCoreRegister()) { - CHECK(!dest.AsVIXLRegister().Is(sp)) << dest; + vixl::aarch32::Register dst = AsVIXLRegister(dest); + CHECK(!dst.Is(sp)) << dest; UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(dest.AsVIXLRegister()); + temps.Exclude(dst); if (size == 1u) { - ___ Ldrb(dest.AsVIXLRegister(), MemOperand(base, offset)); + ___ Ldrb(dst, MemOperand(base, offset)); } else { CHECK_EQ(4u, size) << dest; - ___ Ldr(dest.AsVIXLRegister(), MemOperand(base, offset)); + ___ Ldr(dst, MemOperand(base, offset)); } } else if (dest.IsRegisterPair()) { CHECK_EQ(8u, size) << dest; - ___ Ldr(dest.AsVIXLRegisterPairLow(), MemOperand(base, offset)); - ___ Ldr(dest.AsVIXLRegisterPairHigh(), MemOperand(base, offset + 4)); + ___ Ldr(AsVIXLRegisterPairLow(dest), MemOperand(base, offset)); + ___ Ldr(AsVIXLRegisterPairHigh(dest), MemOperand(base, offset + 4)); } else if (dest.IsSRegister()) { - ___ Vldr(dest.AsVIXLSRegister(), MemOperand(base, offset)); + ___ Vldr(AsVIXLSRegister(dest), MemOperand(base, offset)); } else { CHECK(dest.IsDRegister()) << dest; - ___ Vldr(dest.AsVIXLDRegister(), MemOperand(base, offset)); + ___ Vldr(AsVIXLDRegister(dest), MemOperand(base, offset)); } } diff --git a/compiler/utils/arm/managed_register_arm.cc b/compiler/utils/arm/managed_register_arm.cc index 1fdc110dcfd83ada9e6383bcc95b98a997249bc4..deff658b4f0cbec39af8ab88feea3ff1fb9f0110 100644 --- a/compiler/utils/arm/managed_register_arm.cc +++ b/compiler/utils/arm/managed_register_arm.cc @@ -16,7 +16,7 @@ #include "managed_register_arm.h" -#include "globals.h" +#include "base/globals.h" namespace art { namespace arm { diff --git a/compiler/utils/arm/managed_register_arm.h b/compiler/utils/arm/managed_register_arm.h index 26f23b2ed6c6cdadbea670db1735ae96a885068c..e42572dc3207c2c1af766d8c4b0ee183f988a2bd 100644 --- a/compiler/utils/arm/managed_register_arm.h +++ b/compiler/utils/arm/managed_register_arm.h @@ -20,15 +20,8 @@ #include #include "constants_arm.h" -#include "debug/dwarf/register.h" #include "utils/managed_register.h" -// TODO(VIXL): Make VIXL compile with -Wshadow. -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wshadow" -#include "aarch32/macro-assembler-aarch32.h" -#pragma GCC diagnostic pop - namespace art { namespace arm { @@ -97,31 +90,16 @@ class ArmManagedRegister : public ManagedRegister { return static_cast(id_); } - vixl::aarch32::Register AsVIXLRegister() const { - CHECK(IsCoreRegister()); - return vixl::aarch32::Register(id_); - } - constexpr SRegister AsSRegister() const { CHECK(IsSRegister()); return static_cast(id_ - kNumberOfCoreRegIds); } - vixl::aarch32::SRegister AsVIXLSRegister() const { - CHECK(IsSRegister()); - return vixl::aarch32::SRegister(id_ - kNumberOfCoreRegIds); - } - constexpr DRegister AsDRegister() const { CHECK(IsDRegister()); return static_cast(id_ - kNumberOfCoreRegIds - kNumberOfSRegIds); } - vixl::aarch32::DRegister AsVIXLDRegister() const { - CHECK(IsDRegister()); - return vixl::aarch32::DRegister(id_ - kNumberOfCoreRegIds - kNumberOfSRegIds); - } - constexpr SRegister AsOverlappingDRegisterLow() const { CHECK(IsOverlappingDRegister()); DRegister d_reg = AsDRegister(); @@ -150,20 +128,12 @@ class ArmManagedRegister : public ManagedRegister { return FromRegId(AllocIdLow()).AsCoreRegister(); } - vixl::aarch32::Register AsVIXLRegisterPairLow() const { - return vixl::aarch32::Register(AsRegisterPairLow()); - } - constexpr Register AsRegisterPairHigh() const { CHECK(IsRegisterPair()); // Appropriate mapping of register ids allows to use AllocIdHigh(). return FromRegId(AllocIdHigh()).AsCoreRegister(); } - vixl::aarch32::Register AsVIXLRegisterPairHigh() const { - return vixl::aarch32::Register(AsRegisterPairHigh()); - } - constexpr bool IsCoreRegister() const { CHECK(IsValidManagedRegister()); return (0 <= id_) && (id_ < kNumberOfCoreRegIds); @@ -255,16 +225,16 @@ class ArmManagedRegister : public ManagedRegister { return FromDRegister(static_cast(r)); } - private: - constexpr bool IsValidManagedRegister() const { - return (0 <= id_) && (id_ < kNumberOfRegIds); - } - int RegId() const { CHECK(!IsNoRegister()); return id_; } + private: + constexpr bool IsValidManagedRegister() const { + return (0 <= id_) && (id_ < kNumberOfRegIds); + } + int AllocId() const { CHECK(IsValidManagedRegister() && !IsOverlappingDRegister() && !IsRegisterPair()); diff --git a/compiler/utils/arm/managed_register_arm_test.cc b/compiler/utils/arm/managed_register_arm_test.cc index 43b0b516dc30dcbe4f28feabb307fffc6a584413..6f440a7c81a1456fe55eb31a426f3cd547b1d155 100644 --- a/compiler/utils/arm/managed_register_arm_test.cc +++ b/compiler/utils/arm/managed_register_arm_test.cc @@ -15,7 +15,7 @@ */ #include "managed_register_arm.h" -#include "globals.h" +#include "base/globals.h" #include "gtest/gtest.h" namespace art { diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.cc b/compiler/utils/arm64/jni_macro_assembler_arm64.cc index a5aa1c12b3c0d5097d22e8e60727bc8327d7c360..d6ce03387c8149eae7d3c4c70311604c021a8c0b 100644 --- a/compiler/utils/arm64/jni_macro_assembler_arm64.cc +++ b/compiler/utils/arm64/jni_macro_assembler_arm64.cc @@ -719,11 +719,10 @@ void Arm64JNIMacroAssembler::BuildFrame(size_t frame_size, // Write out entry spills int32_t offset = frame_size + static_cast(kArm64PointerSize); - for (size_t i = 0; i < entry_spills.size(); ++i) { - Arm64ManagedRegister reg = entry_spills.at(i).AsArm64(); + for (const ManagedRegisterSpill& spill : entry_spills) { + Arm64ManagedRegister reg = spill.AsArm64(); if (reg.IsNoRegister()) { // only increment stack offset. - ManagedRegisterSpill spill = entry_spills.at(i); offset += spill.getSize(); } else if (reg.IsXRegister()) { StoreToOffset(reg.AsXRegister(), SP, offset); diff --git a/compiler/utils/arm64/managed_register_arm64.cc b/compiler/utils/arm64/managed_register_arm64.cc index 47924bf99f120c35cd69bbf95d3075046d149efd..5632265646d511394ab35186b32eaf095f763f81 100644 --- a/compiler/utils/arm64/managed_register_arm64.cc +++ b/compiler/utils/arm64/managed_register_arm64.cc @@ -15,7 +15,7 @@ */ #include "managed_register_arm64.h" -#include "globals.h" +#include "base/globals.h" namespace art { namespace arm64 { diff --git a/compiler/utils/arm64/managed_register_arm64.h b/compiler/utils/arm64/managed_register_arm64.h index 9ce7ec9a977b9c4834509c6535ad8e9a8d6a5984..0513890aa8c0592311116efaf60a64c93afbdb40 100644 --- a/compiler/utils/arm64/managed_register_arm64.h +++ b/compiler/utils/arm64/managed_register_arm64.h @@ -20,7 +20,6 @@ #include #include "arch/arm64/registers_arm64.h" -#include "debug/dwarf/register.h" #include "utils/managed_register.h" namespace art { diff --git a/compiler/utils/arm64/managed_register_arm64_test.cc b/compiler/utils/arm64/managed_register_arm64_test.cc index 2a79313be50a490ee690f1b2665fcef6ad670ad3..d151ac99e7f51c62197dd5333257e3e25961907e 100644 --- a/compiler/utils/arm64/managed_register_arm64_test.cc +++ b/compiler/utils/arm64/managed_register_arm64_test.cc @@ -17,7 +17,7 @@ #include "managed_register_arm64.h" #include "assembler_arm64.h" -#include "globals.h" +#include "base/globals.h" #include "gtest/gtest.h" namespace art { diff --git a/compiler/utils/assembler.cc b/compiler/utils/assembler.cc index 944c64b5918fab2081ece5045b2ed0accacf860b..d1d2a3d5567953b23806adacb9f8fef4fdb14f19 100644 --- a/compiler/utils/assembler.cc +++ b/compiler/utils/assembler.cc @@ -20,8 +20,8 @@ #include #include "base/casts.h" -#include "globals.h" -#include "memory_region.h" +#include "base/globals.h" +#include "base/memory_region.h" namespace art { diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h index 5b0cd6baa8d206d9b392486f7f3effdd06089d85..379a6396eb68dea2d8fb9d90d9be41b620ec3594 100644 --- a/compiler/utils/assembler.h +++ b/compiler/utils/assembler.h @@ -29,10 +29,10 @@ #include "base/array_ref.h" #include "base/enums.h" #include "base/macros.h" +#include "base/memory_region.h" #include "debug/dwarf/debug_frame_opcode_writer.h" #include "label.h" #include "managed_register.h" -#include "memory_region.h" #include "mips/constants_mips.h" #include "offsets.h" #include "x86/constants_x86.h" diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h index 0cb8bbb2d54bad6e19848773bf6fcd4fe1676ef6..7c800b355febf92ac05474cdf3996ff28229a8de 100644 --- a/compiler/utils/assembler_test.h +++ b/compiler/utils/assembler_test.h @@ -26,6 +26,7 @@ #include #include +#include "base/malloc_arena_pool.h" #include "assembler_test_base.h" #include "common_runtime_test.h" // For ScratchFile @@ -1606,7 +1607,7 @@ class AssemblerTest : public testing::Test { static constexpr size_t kWarnManyCombinationsThreshold = 500; - ArenaPool pool_; + MallocArenaPool pool_; std::unique_ptr allocator_; std::unique_ptr assembler_; std::unique_ptr test_helper_; diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc index 655d17d4fbf35764272bda88edc615458ac0cb09..053e202523aa88e8176bcf9d282bbac7d696a754 100644 --- a/compiler/utils/assembler_thumb_test.cc +++ b/compiler/utils/assembler_thumb_test.cc @@ -27,6 +27,7 @@ #include "utils/arm/jni_macro_assembler_arm_vixl.h" #include "base/hex_dump.h" +#include "base/malloc_arena_pool.h" #include "common_runtime_test.h" namespace art { @@ -169,7 +170,7 @@ class ArmVIXLAssemblerTest : public ::testing::Test { public: ArmVIXLAssemblerTest() : pool(), allocator(&pool), assembler(&allocator) { } - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator; ArmVIXLJNIMacroAssembler assembler; }; diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc index 674dc9a78ba552a654ef1b604c66a9d03b7439d1..85e4326494494c919c731ea6c8fea85ecf3477da 100644 --- a/compiler/utils/assembler_thumb_test_expected.cc.inc +++ b/compiler/utils/assembler_thumb_test_expected.cc.inc @@ -153,7 +153,7 @@ const char* const VixlJniHelpersResults[] = { " 21c: f8d9 8034 ldr.w r8, [r9, #52] ; 0x34\n", " 220: 4770 bx lr\n", " 222: 4660 mov r0, ip\n", - " 224: f8d9 c2c4 ldr.w ip, [r9, #708] ; 0x2c4\n", + " 224: f8d9 c2d4 ldr.w ip, [r9, #724] ; 0x2d4\n", " 228: 47e0 blx ip\n", nullptr }; diff --git a/compiler/utils/atomic_dex_ref_map-inl.h b/compiler/utils/atomic_dex_ref_map-inl.h index ce3302bb62843cfb44c05cd08eb351e135d80fc2..9915498accaf703f02aa44ae09854fc3020dd2be 100644 --- a/compiler/utils/atomic_dex_ref_map-inl.h +++ b/compiler/utils/atomic_dex_ref_map-inl.h @@ -70,7 +70,7 @@ inline bool AtomicDexRefMap::Get(const DexFileRefer if (array == nullptr) { return false; } - *out = (*array)[ref.index].LoadRelaxed(); + *out = (*array)[ref.index].load(std::memory_order_relaxed); return true; } @@ -81,7 +81,7 @@ inline bool AtomicDexRefMap::Remove(const DexFileRe if (array == nullptr) { return false; } - *out = (*array)[ref.index].ExchangeSequentiallyConsistent(nullptr); + *out = (*array)[ref.index].exchange(nullptr, std::memory_order_seq_cst); return true; } @@ -120,7 +120,7 @@ inline void AtomicDexRefMap::Visit(const Visitor& v const DexFile* dex_file = pair.first; const ElementArray& elements = pair.second; for (size_t i = 0; i < elements.size(); ++i) { - visitor(DexFileReference(dex_file, i), elements[i].LoadRelaxed()); + visitor(DexFileReference(dex_file, i), elements[i].load(std::memory_order_relaxed)); } } } @@ -129,7 +129,7 @@ template inline void AtomicDexRefMap::ClearEntries() { for (auto& it : arrays_) { for (auto& element : it.second) { - element.StoreRelaxed(nullptr); + element.store(nullptr, std::memory_order_relaxed); } } } diff --git a/compiler/utils/dedupe_set-inl.h b/compiler/utils/dedupe_set-inl.h index c866504e6202b84428ea1e7309de9992414341f8..4e892f26164dacc516f266989f4fc532a48f6744 100644 --- a/compiler/utils/dedupe_set-inl.h +++ b/compiler/utils/dedupe_set-inl.h @@ -71,13 +71,13 @@ class DedupeSet::Shard { const StoreKey* Add(Thread* self, size_t hash, const InKey& in_key) REQUIRES(!lock_) { MutexLock lock(self, lock_); HashedKey hashed_in_key(hash, &in_key); - auto it = keys_.Find(hashed_in_key); + auto it = keys_.find(hashed_in_key); if (it != keys_.end()) { DCHECK(it->Key() != nullptr); return it->Key(); } const StoreKey* store_key = alloc_.Copy(in_key); - keys_.Insert(HashedKey { hash, store_key }); + keys_.insert(HashedKey { hash, store_key }); return store_key; } @@ -90,7 +90,7 @@ class DedupeSet::Shard { // Note: The total_probe_distance will be updated with the current state. // It may have been higher before a re-hash. global_stats->total_probe_distance += keys_.TotalProbeDistance(); - global_stats->total_size += keys_.Size(); + global_stats->total_size += keys_.size(); for (const HashedKey& key : keys_) { auto it = stats.find(key.Hash()); if (it == stats.end()) { diff --git a/compiler/utils/jni_macro_assembler.cc b/compiler/utils/jni_macro_assembler.cc index 3f7691b6a8671be95fe533757bb3ee3187f05760..5f405f348cc62f58b05dedbcc5ee70c7a67ea9a8 100644 --- a/compiler/utils/jni_macro_assembler.cc +++ b/compiler/utils/jni_macro_assembler.cc @@ -38,8 +38,8 @@ #include "x86_64/jni_macro_assembler_x86_64.h" #endif #include "base/casts.h" -#include "globals.h" -#include "memory_region.h" +#include "base/globals.h" +#include "base/memory_region.h" namespace art { diff --git a/compiler/utils/jni_macro_assembler_test.h b/compiler/utils/jni_macro_assembler_test.h index 1aefc84c78ca7ff400174ba518aebfc84d2bad4b..b70c18b3e2f11d4159d66c12b0c3805eaa538d6d 100644 --- a/compiler/utils/jni_macro_assembler_test.h +++ b/compiler/utils/jni_macro_assembler_test.h @@ -20,6 +20,7 @@ #include "jni_macro_assembler.h" #include "assembler_test_base.h" +#include "base/malloc_arena_pool.h" #include "common_runtime_test.h" // For ScratchFile #include @@ -139,7 +140,7 @@ class JNIMacroAssemblerTest : public testing::Test { test_helper_->Driver(*data, assembly_text, test_name); } - ArenaPool pool_; + MallocArenaPool pool_; std::unique_ptr allocator_; std::unique_ptr assembler_; std::unique_ptr test_helper_; diff --git a/compiler/utils/managed_register.h b/compiler/utils/managed_register.h index 2b7b2aa7ce665cbc0ecdb32c80afd8ad5da77e47..db9c36cc75830dedfefb2b4e3e308cf53602b92d 100644 --- a/compiler/utils/managed_register.h +++ b/compiler/utils/managed_register.h @@ -101,11 +101,11 @@ class ManagedRegisterSpill : public ManagedRegister { ManagedRegisterSpill(const ManagedRegister& other, int32_t size) : ManagedRegister(other), size_(size), spill_offset_(-1) { } - int32_t getSpillOffset() { + int32_t getSpillOffset() const { return spill_offset_; } - int32_t getSize() { + int32_t getSize() const { return size_; } diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc index 2218ef9af29e07a880480fa5e937b6f5b9dceaed..c0b6f988d4943fc2e1f5e30464fe9db3570de123 100644 --- a/compiler/utils/mips/assembler_mips.cc +++ b/compiler/utils/mips/assembler_mips.cc @@ -18,9 +18,9 @@ #include "base/bit_utils.h" #include "base/casts.h" +#include "base/memory_region.h" #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_entrypoints_enum.h" -#include "memory_region.h" #include "thread.h" namespace art { @@ -2793,6 +2793,26 @@ void MipsAssembler::Hadd_uD(VectorRegister wd, VectorRegister ws, VectorRegister DsFsmInstr(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x15)).FprOuts(wd).FprIns(ws, wt); } +void MipsAssembler::PcntB(VectorRegister wd, VectorRegister ws) { + CHECK(HasMsa()); + DsFsmInstr(EmitMsa2R(0xc1, 0x0, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws); +} + +void MipsAssembler::PcntH(VectorRegister wd, VectorRegister ws) { + CHECK(HasMsa()); + DsFsmInstr(EmitMsa2R(0xc1, 0x1, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws); +} + +void MipsAssembler::PcntW(VectorRegister wd, VectorRegister ws) { + CHECK(HasMsa()); + DsFsmInstr(EmitMsa2R(0xc1, 0x2, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws); +} + +void MipsAssembler::PcntD(VectorRegister wd, VectorRegister ws) { + CHECK(HasMsa()); + DsFsmInstr(EmitMsa2R(0xc1, 0x3, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws); +} + void MipsAssembler::ReplicateFPToVectorRegister(VectorRegister dst, FRegister src, bool is_double) { @@ -4781,10 +4801,9 @@ void MipsAssembler::BuildFrame(size_t frame_size, // Write out entry spills. int32_t offset = frame_size + kFramePointerSize; - for (size_t i = 0; i < entry_spills.size(); ++i) { - MipsManagedRegister reg = entry_spills.at(i).AsMips(); + for (const ManagedRegisterSpill& spill : entry_spills) { + MipsManagedRegister reg = spill.AsMips(); if (reg.IsNoRegister()) { - ManagedRegisterSpill spill = entry_spills.at(i); offset += spill.getSize(); } else if (reg.IsCoreRegister()) { StoreToOffset(kStoreWord, reg.AsCoreRegister(), SP, offset); diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h index 7de8e2e3665de1862150bb8c6f72bf1dbec5e1f5..af3d7a06ba50a3f4938c32af0a4fd2bb13b3c78d 100644 --- a/compiler/utils/mips/assembler_mips.h +++ b/compiler/utils/mips/assembler_mips.h @@ -24,10 +24,10 @@ #include "arch/mips/instruction_set_features_mips.h" #include "base/arena_containers.h" #include "base/enums.h" +#include "base/globals.h" #include "base/macros.h" #include "base/stl_util_identity.h" #include "constants_mips.h" -#include "globals.h" #include "heap_poisoning.h" #include "managed_register_mips.h" #include "offsets.h" @@ -756,6 +756,11 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler Base; + // These tests were taking too long, so we hide the DriverStr() from AssemblerTest<> + // and reimplement it without the verification against `assembly_string`. b/73903608 + void DriverStr(const std::string& assembly_string ATTRIBUTE_UNUSED, + const std::string& test_name ATTRIBUTE_UNUSED) { + GetAssembler()->FinalizeCode(); + std::vector data(GetAssembler()->CodeSize()); + MemoryRegion code(data.data(), data.size()); + GetAssembler()->FinalizeInstructions(code); + } + AssemblerMIPS32r5Test() : instruction_set_features_(MipsInstructionSetFeatures::FromVariant("mips32r5", nullptr)) { } diff --git a/compiler/utils/mips/assembler_mips32r6_test.cc b/compiler/utils/mips/assembler_mips32r6_test.cc index 937ee25bcb1739891126d48edd7f2b2c444010f7..3d876ca61381c3151d2af120dfcea90b2e8dd513 100644 --- a/compiler/utils/mips/assembler_mips32r6_test.cc +++ b/compiler/utils/mips/assembler_mips32r6_test.cc @@ -45,6 +45,16 @@ class AssemblerMIPS32r6Test : public AssemblerTest Base; + // These tests were taking too long, so we hide the DriverStr() from AssemblerTest<> + // and reimplement it without the verification against `assembly_string`. b/73903608 + void DriverStr(const std::string& assembly_string ATTRIBUTE_UNUSED, + const std::string& test_name ATTRIBUTE_UNUSED) { + GetAssembler()->FinalizeCode(); + std::vector data(GetAssembler()->CodeSize()); + MemoryRegion code(data.data(), data.size()); + GetAssembler()->FinalizeInstructions(code); + } + AssemblerMIPS32r6Test() : instruction_set_features_(MipsInstructionSetFeatures::FromVariant("mips32r6", nullptr)) { } @@ -2277,6 +2287,22 @@ TEST_F(AssemblerMIPS32r6Test, FillW) { DriverStr(RepeatVR(&mips::MipsAssembler::FillW, "fill.w ${reg1}, ${reg2}"), "fill.w"); } +TEST_F(AssemblerMIPS32r6Test, PcntB) { + DriverStr(RepeatVV(&mips::MipsAssembler::PcntB, "pcnt.b ${reg1}, ${reg2}"), "pcnt.b"); +} + +TEST_F(AssemblerMIPS32r6Test, PcntH) { + DriverStr(RepeatVV(&mips::MipsAssembler::PcntH, "pcnt.h ${reg1}, ${reg2}"), "pcnt.h"); +} + +TEST_F(AssemblerMIPS32r6Test, PcntW) { + DriverStr(RepeatVV(&mips::MipsAssembler::PcntW, "pcnt.w ${reg1}, ${reg2}"), "pcnt.w"); +} + +TEST_F(AssemblerMIPS32r6Test, PcntD) { + DriverStr(RepeatVV(&mips::MipsAssembler::PcntD, "pcnt.d ${reg1}, ${reg2}"), "pcnt.d"); +} + TEST_F(AssemblerMIPS32r6Test, LdiB) { DriverStr(RepeatVIb(&mips::MipsAssembler::LdiB, -8, "ldi.b ${reg}, {imm}"), "ldi.b"); } diff --git a/compiler/utils/mips/assembler_mips_test.cc b/compiler/utils/mips/assembler_mips_test.cc index b027d3a5495824c38d2a9324e699ba574adf578e..f94d074299ceeb04c869b2ac83a395e0a600a2ae 100644 --- a/compiler/utils/mips/assembler_mips_test.cc +++ b/compiler/utils/mips/assembler_mips_test.cc @@ -43,6 +43,16 @@ class AssemblerMIPSTest : public AssemblerTest Base; + // These tests were taking too long, so we hide the DriverStr() from AssemblerTest<> + // and reimplement it without the verification against `assembly_string`. b/73903608 + void DriverStr(const std::string& assembly_string ATTRIBUTE_UNUSED, + const std::string& test_name ATTRIBUTE_UNUSED) { + GetAssembler()->FinalizeCode(); + std::vector data(GetAssembler()->CodeSize()); + MemoryRegion code(data.data(), data.size()); + GetAssembler()->FinalizeInstructions(code); + } + protected: // Get the typically used name for this architecture, e.g., aarch64, x86-64, ... std::string GetArchitectureString() OVERRIDE { diff --git a/compiler/utils/mips/constants_mips.h b/compiler/utils/mips/constants_mips.h index 016c0dbb2ed70a380d8acfa8ef04d1a2c918e44d..07d8b7de0ef0b64a73cfc8ce5cdea22dc04fdb7f 100644 --- a/compiler/utils/mips/constants_mips.h +++ b/compiler/utils/mips/constants_mips.h @@ -22,8 +22,8 @@ #include #include "arch/mips/registers_mips.h" +#include "base/globals.h" #include "base/macros.h" -#include "globals.h" namespace art { namespace mips { diff --git a/compiler/utils/mips/managed_register_mips.cc b/compiler/utils/mips/managed_register_mips.cc index 5a8c0481a55f2ce418c3cca151edea2f2177cd8a..9b3ed79d2f5bae674233ca5167c4f07392b53dec 100644 --- a/compiler/utils/mips/managed_register_mips.cc +++ b/compiler/utils/mips/managed_register_mips.cc @@ -16,7 +16,7 @@ #include "managed_register_mips.h" -#include "globals.h" +#include "base/globals.h" namespace art { namespace mips { diff --git a/compiler/utils/mips/managed_register_mips.h b/compiler/utils/mips/managed_register_mips.h index 66204e70e3566a191fb0e30e8a97cbf1c6e2f708..18d5821e6131a696fae4d927cb5d0a18437e336b 100644 --- a/compiler/utils/mips/managed_register_mips.h +++ b/compiler/utils/mips/managed_register_mips.h @@ -18,7 +18,6 @@ #define ART_COMPILER_UTILS_MIPS_MANAGED_REGISTER_MIPS_H_ #include "constants_mips.h" -#include "debug/dwarf/register.h" #include "utils/managed_register.h" namespace art { diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc index e1b0e75108b1cca689a3516d37d9f8fb9b3c3200..5b1c5d9e01de339833d819300658d077062d41ab 100644 --- a/compiler/utils/mips64/assembler_mips64.cc +++ b/compiler/utils/mips64/assembler_mips64.cc @@ -18,9 +18,9 @@ #include "base/bit_utils.h" #include "base/casts.h" +#include "base/memory_region.h" #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_entrypoints_enum.h" -#include "memory_region.h" #include "thread.h" namespace art { @@ -2279,6 +2279,26 @@ void Mips64Assembler::Hadd_uD(VectorRegister wd, VectorRegister ws, VectorRegist EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x15); } +void Mips64Assembler::PcntB(VectorRegister wd, VectorRegister ws) { + CHECK(HasMsa()); + EmitMsa2R(0xc1, 0x0, ws, wd, 0x1e); +} + +void Mips64Assembler::PcntH(VectorRegister wd, VectorRegister ws) { + CHECK(HasMsa()); + EmitMsa2R(0xc1, 0x1, ws, wd, 0x1e); +} + +void Mips64Assembler::PcntW(VectorRegister wd, VectorRegister ws) { + CHECK(HasMsa()); + EmitMsa2R(0xc1, 0x2, ws, wd, 0x1e); +} + +void Mips64Assembler::PcntD(VectorRegister wd, VectorRegister ws) { + CHECK(HasMsa()); + EmitMsa2R(0xc1, 0x3, ws, wd, 0x1e); +} + void Mips64Assembler::ReplicateFPToVectorRegister(VectorRegister dst, FpuRegister src, bool is_double) { @@ -3613,9 +3633,8 @@ void Mips64Assembler::BuildFrame(size_t frame_size, // Write out entry spills. int32_t offset = frame_size + kFramePointerSize; - for (size_t i = 0; i < entry_spills.size(); ++i) { - Mips64ManagedRegister reg = entry_spills[i].AsMips64(); - ManagedRegisterSpill spill = entry_spills.at(i); + for (const ManagedRegisterSpill& spill : entry_spills) { + Mips64ManagedRegister reg = spill.AsMips64(); int32_t size = spill.getSize(); if (reg.IsNoRegister()) { // only increment stack offset. diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h index 7a61f39e64aebabebbb5113483ecd308413fc716..19f23b7e95523811594c206631ae678ddcc55e33 100644 --- a/compiler/utils/mips64/assembler_mips64.h +++ b/compiler/utils/mips64/assembler_mips64.h @@ -24,10 +24,10 @@ #include "arch/mips64/instruction_set_features_mips64.h" #include "base/arena_containers.h" #include "base/enums.h" +#include "base/globals.h" #include "base/macros.h" #include "base/stl_util_identity.h" #include "constants_mips64.h" -#include "globals.h" #include "heap_poisoning.h" #include "managed_register_mips64.h" #include "offsets.h" @@ -863,6 +863,11 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler Base; + // These tests were taking too long, so we hide the DriverStr() from AssemblerTest<> + // and reimplement it without the verification against `assembly_string`. b/73903608 + void DriverStr(const std::string& assembly_string ATTRIBUTE_UNUSED, + const std::string& test_name ATTRIBUTE_UNUSED) { + GetAssembler()->FinalizeCode(); + std::vector data(GetAssembler()->CodeSize()); + MemoryRegion code(data.data(), data.size()); + GetAssembler()->FinalizeInstructions(code); + } + AssemblerMIPS64Test() : instruction_set_features_(Mips64InstructionSetFeatures::FromVariant("default", nullptr)) {} @@ -3529,6 +3539,22 @@ TEST_F(AssemblerMIPS64Test, FillD) { DriverStr(RepeatVR(&mips64::Mips64Assembler::FillD, "fill.d ${reg1}, ${reg2}"), "fill.d"); } +TEST_F(AssemblerMIPS64Test, PcntB) { + DriverStr(RepeatVV(&mips64::Mips64Assembler::PcntB, "pcnt.b ${reg1}, ${reg2}"), "pcnt.b"); +} + +TEST_F(AssemblerMIPS64Test, PcntH) { + DriverStr(RepeatVV(&mips64::Mips64Assembler::PcntH, "pcnt.h ${reg1}, ${reg2}"), "pcnt.h"); +} + +TEST_F(AssemblerMIPS64Test, PcntW) { + DriverStr(RepeatVV(&mips64::Mips64Assembler::PcntW, "pcnt.w ${reg1}, ${reg2}"), "pcnt.w"); +} + +TEST_F(AssemblerMIPS64Test, PcntD) { + DriverStr(RepeatVV(&mips64::Mips64Assembler::PcntD, "pcnt.d ${reg1}, ${reg2}"), "pcnt.d"); +} + TEST_F(AssemblerMIPS64Test, LdiB) { DriverStr(RepeatVIb(&mips64::Mips64Assembler::LdiB, -8, "ldi.b ${reg}, {imm}"), "ldi.b"); } diff --git a/compiler/utils/mips64/constants_mips64.h b/compiler/utils/mips64/constants_mips64.h index 310f23c28740d0ae405568b2d20cb808b56cd3c3..41eb77c9aedb6b35b8683a76280a52245cb95360 100644 --- a/compiler/utils/mips64/constants_mips64.h +++ b/compiler/utils/mips64/constants_mips64.h @@ -22,8 +22,8 @@ #include #include "arch/mips64/registers_mips64.h" +#include "base/globals.h" #include "base/macros.h" -#include "globals.h" namespace art { namespace mips64 { diff --git a/compiler/utils/mips64/managed_register_mips64.cc b/compiler/utils/mips64/managed_register_mips64.cc index 42d061ec15bdb00cdc7eba6452ec0f819fa6e73f..01cb6ddfe2588f02cd33c0f502ef4ad2d46fbd7d 100644 --- a/compiler/utils/mips64/managed_register_mips64.cc +++ b/compiler/utils/mips64/managed_register_mips64.cc @@ -16,7 +16,7 @@ #include "managed_register_mips64.h" -#include "globals.h" +#include "base/globals.h" namespace art { namespace mips64 { diff --git a/compiler/utils/mips64/managed_register_mips64.h b/compiler/utils/mips64/managed_register_mips64.h index 3980199b1e3b7586c1030654496c486f4c4ce80d..94166d32b72ece86f89044085ee3c2c4227ae556 100644 --- a/compiler/utils/mips64/managed_register_mips64.h +++ b/compiler/utils/mips64/managed_register_mips64.h @@ -18,7 +18,6 @@ #define ART_COMPILER_UTILS_MIPS64_MANAGED_REGISTER_MIPS64_H_ #include "constants_mips64.h" -#include "debug/dwarf/register.h" #include "utils/managed_register.h" namespace art { diff --git a/compiler/utils/mips64/managed_register_mips64_test.cc b/compiler/utils/mips64/managed_register_mips64_test.cc index 8b72d7e61dfe1e363a9f790891511f69963a1ea6..bbfeeee20f0dca4d1cbf0e62640605b7de0ae945 100644 --- a/compiler/utils/mips64/managed_register_mips64_test.cc +++ b/compiler/utils/mips64/managed_register_mips64_test.cc @@ -15,7 +15,8 @@ */ #include "managed_register_mips64.h" -#include "globals.h" + +#include "base/globals.h" #include "gtest/gtest.h" namespace art { diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index ea160c8993cc31acfded51bcbd39e7a60877674e..86f9010ea324322e28eec0962b6c7f1e092e0579 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -17,8 +17,8 @@ #include "assembler_x86.h" #include "base/casts.h" +#include "base/memory_region.h" #include "entrypoints/quick/quick_entrypoints.h" -#include "memory_region.h" #include "thread.h" namespace art { @@ -913,6 +913,78 @@ void X86Assembler::psubq(XmmRegister dst, XmmRegister src) { } +void X86Assembler::paddusb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xDC); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::paddsb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xEC); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::paddusw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xDD); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::paddsw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xED); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::psubusb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xD8); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::psubsb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xE8); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::psubusw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xD9); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::psubsw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xE9); + EmitXmmRegisterOperand(dst, src); +} + + void X86Assembler::cvtsi2ss(XmmRegister dst, Register src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF3); diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index a0856770836ed7f57da2961e9f9bf3909b23a13d..e42c4c986a5d34d56724fbc27bc96e5e9cffec1e 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -23,9 +23,9 @@ #include "base/array_ref.h" #include "base/bit_utils.h" #include "base/enums.h" +#include "base/globals.h" #include "base/macros.h" #include "constants_x86.h" -#include "globals.h" #include "heap_poisoning.h" #include "managed_register_x86.h" #include "offsets.h" @@ -449,6 +449,15 @@ class X86Assembler FINAL : public Assembler { void paddq(XmmRegister dst, XmmRegister src); void psubq(XmmRegister dst, XmmRegister src); + void paddusb(XmmRegister dst, XmmRegister src); + void paddsb(XmmRegister dst, XmmRegister src); + void paddusw(XmmRegister dst, XmmRegister src); + void paddsw(XmmRegister dst, XmmRegister src); + void psubusb(XmmRegister dst, XmmRegister src); + void psubsb(XmmRegister dst, XmmRegister src); + void psubusw(XmmRegister dst, XmmRegister src); + void psubsw(XmmRegister dst, XmmRegister src); + void cvtsi2ss(XmmRegister dst, Register src); void cvtsi2sd(XmmRegister dst, Register src); diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc index 2fd1b271828f88c478e5a934bcb521687d5da185..cd007b32d419b49614bc394b93355e362dfa5c69 100644 --- a/compiler/utils/x86/assembler_x86_test.cc +++ b/compiler/utils/x86/assembler_x86_test.cc @@ -17,13 +17,14 @@ #include "assembler_x86.h" #include "base/arena_allocator.h" +#include "base/malloc_arena_pool.h" #include "base/stl_util.h" #include "utils/assembler_test.h" namespace art { TEST(AssemblerX86, CreateBuffer) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); AssemblerBuffer buffer(&allocator); AssemblerBuffer::EnsureCapacity ensured(&buffer); @@ -600,6 +601,38 @@ TEST_F(AssemblerX86Test, PSubQ) { DriverStr(RepeatFF(&x86::X86Assembler::psubq, "psubq %{reg2}, %{reg1}"), "psubq"); } +TEST_F(AssemblerX86Test, PAddUSB) { + DriverStr(RepeatFF(&x86::X86Assembler::paddusb, "paddusb %{reg2}, %{reg1}"), "paddusb"); +} + +TEST_F(AssemblerX86Test, PAddSB) { + DriverStr(RepeatFF(&x86::X86Assembler::paddsb, "paddsb %{reg2}, %{reg1}"), "paddsb"); +} + +TEST_F(AssemblerX86Test, PAddUSW) { + DriverStr(RepeatFF(&x86::X86Assembler::paddusw, "paddusw %{reg2}, %{reg1}"), "paddusw"); +} + +TEST_F(AssemblerX86Test, PAddSW) { + DriverStr(RepeatFF(&x86::X86Assembler::psubsw, "psubsw %{reg2}, %{reg1}"), "psubsw"); +} + +TEST_F(AssemblerX86Test, PSubUSB) { + DriverStr(RepeatFF(&x86::X86Assembler::psubusb, "psubusb %{reg2}, %{reg1}"), "psubusb"); +} + +TEST_F(AssemblerX86Test, PSubSB) { + DriverStr(RepeatFF(&x86::X86Assembler::psubsb, "psubsb %{reg2}, %{reg1}"), "psubsb"); +} + +TEST_F(AssemblerX86Test, PSubUSW) { + DriverStr(RepeatFF(&x86::X86Assembler::psubusw, "psubusw %{reg2}, %{reg1}"), "psubusw"); +} + +TEST_F(AssemblerX86Test, PSubSW) { + DriverStr(RepeatFF(&x86::X86Assembler::psubsw, "psubsw %{reg2}, %{reg1}"), "psubsw"); +} + TEST_F(AssemblerX86Test, XorPD) { DriverStr(RepeatFF(&x86::X86Assembler::xorpd, "xorpd %{reg2}, %{reg1}"), "xorpd"); } diff --git a/compiler/utils/x86/constants_x86.h b/compiler/utils/x86/constants_x86.h index 2e03b9fc3ce11e40188510eff9cfd1a141747de4..a782b16c6b9ef601c59da4f6876473f8353b2d78 100644 --- a/compiler/utils/x86/constants_x86.h +++ b/compiler/utils/x86/constants_x86.h @@ -22,8 +22,8 @@ #include #include "arch/x86/registers_x86.h" +#include "base/globals.h" #include "base/macros.h" -#include "globals.h" namespace art { namespace x86 { @@ -40,21 +40,6 @@ enum ByteRegister { kNoByteRegister = -1 // Signals an illegal register. }; - -enum XmmRegister { - XMM0 = 0, - XMM1 = 1, - XMM2 = 2, - XMM3 = 3, - XMM4 = 4, - XMM5 = 5, - XMM6 = 6, - XMM7 = 7, - kNumberOfXmmRegisters = 8, - kNoXmmRegister = -1 // Signals an illegal register. -}; -std::ostream& operator<<(std::ostream& os, const XmmRegister& reg); - enum X87Register { ST0 = 0, ST1 = 1, diff --git a/compiler/utils/x86/jni_macro_assembler_x86.cc b/compiler/utils/x86/jni_macro_assembler_x86.cc index 7e29c4aa26fa7d1bb0be72580a869535a8b8b9c8..dd99f03aa799f427af11572fee87af24d2c304a8 100644 --- a/compiler/utils/x86/jni_macro_assembler_x86.cc +++ b/compiler/utils/x86/jni_macro_assembler_x86.cc @@ -67,8 +67,7 @@ void X86JNIMacroAssembler::BuildFrame(size_t frame_size, cfi().AdjustCFAOffset(kFramePointerSize); DCHECK_EQ(static_cast(cfi().GetCurrentCFAOffset()), frame_size); - for (size_t i = 0; i < entry_spills.size(); ++i) { - ManagedRegisterSpill spill = entry_spills.at(i); + for (const ManagedRegisterSpill& spill : entry_spills) { if (spill.AsX86().IsCpuRegister()) { int offset = frame_size + spill.getSpillOffset(); __ movl(Address(ESP, offset), spill.AsX86().AsCpuRegister()); diff --git a/compiler/utils/x86/managed_register_x86.cc b/compiler/utils/x86/managed_register_x86.cc index 69e6fce5c47848b318e3c710d2d5d920f7181d09..cc7cedf93e5fa68b2dbdf5f4c194734106f75a6f 100644 --- a/compiler/utils/x86/managed_register_x86.cc +++ b/compiler/utils/x86/managed_register_x86.cc @@ -16,7 +16,7 @@ #include "managed_register_x86.h" -#include "globals.h" +#include "base/globals.h" namespace art { namespace x86 { diff --git a/compiler/utils/x86/managed_register_x86.h b/compiler/utils/x86/managed_register_x86.h index c0c2b650e91adee54ac57364c865162da4d67a03..8810bfa2f166c6bef842e02fa365aa654db42f6c 100644 --- a/compiler/utils/x86/managed_register_x86.h +++ b/compiler/utils/x86/managed_register_x86.h @@ -18,7 +18,6 @@ #define ART_COMPILER_UTILS_X86_MANAGED_REGISTER_X86_H_ #include "constants_x86.h" -#include "debug/dwarf/register.h" #include "utils/managed_register.h" namespace art { diff --git a/compiler/utils/x86/managed_register_x86_test.cc b/compiler/utils/x86/managed_register_x86_test.cc index 0ed5c36fe4cde0e92c1559824759a7488fbb8355..28af5313c755dc0aed3468d8d2e5b5d010d740bc 100644 --- a/compiler/utils/x86/managed_register_x86_test.cc +++ b/compiler/utils/x86/managed_register_x86_test.cc @@ -16,7 +16,7 @@ #include "managed_register_x86.h" -#include "globals.h" +#include "base/globals.h" #include "gtest/gtest.h" namespace art { diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index ff5a357c5e3e22c5858ec1e15c0dc81aa2359b8d..bd31561937dd133b9ceb8215d4cf3f0ff08d250d 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -17,8 +17,8 @@ #include "assembler_x86_64.h" #include "base/casts.h" +#include "base/memory_region.h" #include "entrypoints/quick/quick_entrypoints.h" -#include "memory_region.h" #include "thread.h" namespace art { @@ -1011,6 +1011,86 @@ void X86_64Assembler::psubq(XmmRegister dst, XmmRegister src) { } +void X86_64Assembler::paddusb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xDC); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::paddsb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xEC); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::paddusw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xDD); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::paddsw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xED); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::psubusb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xD8); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::psubsb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xE8); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::psubusw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xD9); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::psubsw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xE9); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + void X86_64Assembler::cvtsi2ss(XmmRegister dst, CpuRegister src) { cvtsi2ss(dst, src, false); } diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index 7a5fdb502f6259c45d3316a754ae93619c1d5905..e4d72a7ba28fbcb1f1850ba1d9dc0d3252a930a0 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -22,9 +22,9 @@ #include "base/arena_containers.h" #include "base/array_ref.h" #include "base/bit_utils.h" +#include "base/globals.h" #include "base/macros.h" #include "constants_x86_64.h" -#include "globals.h" #include "heap_poisoning.h" #include "managed_register_x86_64.h" #include "offsets.h" @@ -485,6 +485,15 @@ class X86_64Assembler FINAL : public Assembler { void paddq(XmmRegister dst, XmmRegister src); void psubq(XmmRegister dst, XmmRegister src); + void paddusb(XmmRegister dst, XmmRegister src); + void paddsb(XmmRegister dst, XmmRegister src); + void paddusw(XmmRegister dst, XmmRegister src); + void paddsw(XmmRegister dst, XmmRegister src); + void psubusb(XmmRegister dst, XmmRegister src); + void psubsb(XmmRegister dst, XmmRegister src); + void psubusw(XmmRegister dst, XmmRegister src); + void psubsw(XmmRegister dst, XmmRegister src); + void cvtsi2ss(XmmRegister dst, CpuRegister src); // Note: this is the r/m32 version. void cvtsi2ss(XmmRegister dst, CpuRegister src, bool is64bit); void cvtsi2ss(XmmRegister dst, const Address& src, bool is64bit); diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index 6b1e53c35ab676130b87b801f295e94e760b6374..0589df55d2335d0c04b5c698ed3aee8af88d6b96 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -21,6 +21,7 @@ #include #include "base/bit_utils.h" +#include "base/malloc_arena_pool.h" #include "base/stl_util.h" #include "jni_macro_assembler_x86_64.h" #include "utils/assembler_test.h" @@ -29,7 +30,7 @@ namespace art { TEST(AssemblerX86_64, CreateBuffer) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); AssemblerBuffer buffer(&allocator); AssemblerBuffer::EnsureCapacity ensured(&buffer); @@ -1282,6 +1283,38 @@ TEST_F(AssemblerX86_64Test, Psubq) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubq, "psubq %{reg2}, %{reg1}"), "psubq"); } +TEST_F(AssemblerX86_64Test, Paddusb) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddusb, "paddusb %{reg2}, %{reg1}"), "paddusb"); +} + +TEST_F(AssemblerX86_64Test, Paddsb) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddsb, "paddsb %{reg2}, %{reg1}"), "paddsb"); +} + +TEST_F(AssemblerX86_64Test, Paddusw) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddusw, "paddusw %{reg2}, %{reg1}"), "paddusw"); +} + +TEST_F(AssemblerX86_64Test, Paddsw) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddsw, "paddsw %{reg2}, %{reg1}"), "paddsw"); +} + +TEST_F(AssemblerX86_64Test, Psubusb) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubusb, "psubusb %{reg2}, %{reg1}"), "psubusb"); +} + +TEST_F(AssemblerX86_64Test, Psubsb) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubsb, "psubsb %{reg2}, %{reg1}"), "psubsb"); +} + +TEST_F(AssemblerX86_64Test, Psubusw) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubusw, "psubusw %{reg2}, %{reg1}"), "psubusw"); +} + +TEST_F(AssemblerX86_64Test, Psubsw) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubsw, "psubsw %{reg2}, %{reg1}"), "psubsw"); +} + TEST_F(AssemblerX86_64Test, Cvtsi2ss) { DriverStr(RepeatFr(&x86_64::X86_64Assembler::cvtsi2ss, "cvtsi2ss %{reg2}, %{reg1}"), "cvtsi2ss"); } diff --git a/compiler/utils/x86_64/constants_x86_64.h b/compiler/utils/x86_64/constants_x86_64.h index 2af3e7be16a1b83fba6f55d19ae5e339253c260e..b02e246842f382e0af88e20cb3c4697f7d195aba 100644 --- a/compiler/utils/x86_64/constants_x86_64.h +++ b/compiler/utils/x86_64/constants_x86_64.h @@ -22,8 +22,8 @@ #include #include "arch/x86_64/registers_x86_64.h" +#include "base/globals.h" #include "base/macros.h" -#include "globals.h" namespace art { namespace x86_64 { diff --git a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc index 5766f9d44b9790c629886db0cb466c54ea884e87..f6b2f9df34448c78dd82fe548434c55d0a918fbc 100644 --- a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc +++ b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc @@ -17,8 +17,8 @@ #include "jni_macro_assembler_x86_64.h" #include "base/casts.h" +#include "base/memory_region.h" #include "entrypoints/quick/quick_entrypoints.h" -#include "memory_region.h" #include "thread.h" namespace art { @@ -75,8 +75,7 @@ void X86_64JNIMacroAssembler::BuildFrame(size_t frame_size, __ movq(Address(CpuRegister(RSP), 0), method_reg.AsX86_64().AsCpuRegister()); - for (size_t i = 0; i < entry_spills.size(); ++i) { - ManagedRegisterSpill spill = entry_spills.at(i); + for (const ManagedRegisterSpill& spill : entry_spills) { if (spill.AsX86_64().IsCpuRegister()) { if (spill.getSize() == 8) { __ movq(Address(CpuRegister(RSP), frame_size + spill.getSpillOffset()), diff --git a/compiler/utils/x86_64/managed_register_x86_64.cc b/compiler/utils/x86_64/managed_register_x86_64.cc index b8c2db2d2ef4e192ef7bdccc114592f4c409bb71..c0eec9d86ccb9e3b509d39bc26c94d3316fb2945 100644 --- a/compiler/utils/x86_64/managed_register_x86_64.cc +++ b/compiler/utils/x86_64/managed_register_x86_64.cc @@ -16,7 +16,7 @@ #include "managed_register_x86_64.h" -#include "globals.h" +#include "base/globals.h" namespace art { namespace x86_64 { diff --git a/compiler/utils/x86_64/managed_register_x86_64.h b/compiler/utils/x86_64/managed_register_x86_64.h index 32af6726700d04fd7fca24784004d7e4e37c21a0..67608829655ce68c8bd245822af36c857d705c69 100644 --- a/compiler/utils/x86_64/managed_register_x86_64.h +++ b/compiler/utils/x86_64/managed_register_x86_64.h @@ -18,7 +18,6 @@ #define ART_COMPILER_UTILS_X86_64_MANAGED_REGISTER_X86_64_H_ #include "constants_x86_64.h" -#include "debug/dwarf/register.h" #include "utils/managed_register.h" namespace art { diff --git a/compiler/utils/x86_64/managed_register_x86_64_test.cc b/compiler/utils/x86_64/managed_register_x86_64_test.cc index e43d717385655f781a2f1b02a3be936c0a35a445..46a405ffaf8347da7f6af34741aa8dfca3dc41a6 100644 --- a/compiler/utils/x86_64/managed_register_x86_64_test.cc +++ b/compiler/utils/x86_64/managed_register_x86_64_test.cc @@ -15,7 +15,7 @@ */ #include "managed_register_x86_64.h" -#include "globals.h" +#include "base/globals.h" #include "gtest/gtest.h" namespace art { diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 76448d819c2654b6c7096b7f726f0929f2e46cee..c22354971054854cbbf822fe79ba2a62e03a887b 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -18,9 +18,12 @@ #include "verifier/verifier_deps.h" #include "art_method-inl.h" +#include "base/indenter.h" #include "class_linker.h" #include "common_compiler_test.h" #include "compiler_callbacks.h" +#include "dex/class_accessor-inl.h" +#include "dex/class_iterator.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_types.h" #include "dex/verification_results.h" @@ -28,7 +31,6 @@ #include "driver/compiler_driver-inl.h" #include "driver/compiler_options.h" #include "handle_scope-inl.h" -#include "indenter.h" #include "mirror/class_loader.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" @@ -63,17 +65,16 @@ class VerifierDepsTest : public CommonCompilerTest { callbacks_.reset(new VerifierDepsCompilerCallbacks()); } - mirror::Class* FindClassByName(const std::string& name, ScopedObjectAccess* soa) + ObjPtr FindClassByName(ScopedObjectAccess& soa, const std::string& name) REQUIRES_SHARED(Locks::mutator_lock_) { - StackHandleScope<1> hs(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); Handle class_loader_handle( - hs.NewHandle(soa->Decode(class_loader_))); - mirror::Class* klass = class_linker_->FindClass(Thread::Current(), - name.c_str(), - class_loader_handle); + hs.NewHandle(soa.Decode(class_loader_))); + ObjPtr klass = + class_linker_->FindClass(soa.Self(), name.c_str(), class_loader_handle); if (klass == nullptr) { - DCHECK(Thread::Current()->IsExceptionPending()); - Thread::Current()->ClearException(); + DCHECK(soa.Self()->IsExceptionPending()); + soa.Self()->ClearException(); } return klass; } @@ -112,35 +113,35 @@ class VerifierDepsTest : public CommonCompilerTest { callbacks->SetVerifierDeps(verifier_deps_.get()); } - void LoadDexFile(ScopedObjectAccess* soa, const char* name1, const char* name2 = nullptr) + void LoadDexFile(ScopedObjectAccess& soa, const char* name1, const char* name2 = nullptr) REQUIRES_SHARED(Locks::mutator_lock_) { class_loader_ = (name2 == nullptr) ? LoadDex(name1) : LoadMultiDex(name1, name2); dex_files_ = GetDexFiles(class_loader_); primary_dex_file_ = dex_files_.front(); SetVerifierDeps(dex_files_); - StackHandleScope<1> hs(soa->Self()); + StackHandleScope<1> hs(soa.Self()); Handle loader = - hs.NewHandle(soa->Decode(class_loader_)); + hs.NewHandle(soa.Decode(class_loader_)); for (const DexFile* dex_file : dex_files_) { class_linker_->RegisterDexFile(*dex_file, loader.Get()); } for (const DexFile* dex_file : dex_files_) { compiler_driver_->GetVerificationResults()->AddDexFile(dex_file); } - compiler_driver_->SetDexFilesForOatFile(dex_files_); + SetDexFilesForOatFile(dex_files_); } - void LoadDexFile(ScopedObjectAccess* soa) REQUIRES_SHARED(Locks::mutator_lock_) { + void LoadDexFile(ScopedObjectAccess& soa) REQUIRES_SHARED(Locks::mutator_lock_) { LoadDexFile(soa, "VerifierDeps"); CHECK_EQ(dex_files_.size(), 1u); - klass_Main_ = FindClassByName("LMain;", soa); + klass_Main_ = FindClassByName(soa, "LMain;"); CHECK(klass_Main_ != nullptr); } bool VerifyMethod(const std::string& method_name) { ScopedObjectAccess soa(Thread::Current()); - LoadDexFile(&soa); + LoadDexFile(soa); StackHandleScope<2> hs(soa.Self()); Handle class_loader_handle( @@ -148,54 +149,50 @@ class VerifierDepsTest : public CommonCompilerTest { Handle dex_cache_handle(hs.NewHandle(klass_Main_->GetDexCache())); const DexFile::ClassDef* class_def = klass_Main_->GetClassDef(); - const uint8_t* class_data = primary_dex_file_->GetClassData(*class_def); - CHECK(class_data != nullptr); + ClassAccessor accessor(*primary_dex_file_, *class_def); - ClassDataItemIterator it(*primary_dex_file_, class_data); - it.SkipAllFields(); + bool has_failures = true; + bool found_method = false; - ArtMethod* method = nullptr; - while (it.HasNextDirectMethod()) { + for (const ClassAccessor::Method& method : accessor.GetMethods()) { ArtMethod* resolved_method = class_linker_->ResolveMethod( - it.GetMemberIndex(), + method.GetIndex(), dex_cache_handle, class_loader_handle, /* referrer */ nullptr, - it.GetMethodInvokeType(*class_def)); + method.GetInvokeType(class_def->access_flags_)); CHECK(resolved_method != nullptr); if (method_name == resolved_method->GetName()) { - method = resolved_method; - break; + soa.Self()->SetVerifierDeps(callbacks_->GetVerifierDeps()); + MethodVerifier verifier(soa.Self(), + primary_dex_file_, + dex_cache_handle, + class_loader_handle, + *class_def, + method.GetCodeItem(), + method.GetIndex(), + resolved_method, + method.GetAccessFlags(), + true /* can_load_classes */, + true /* allow_soft_failures */, + true /* need_precise_constants */, + false /* verify to dump */, + true /* allow_thread_suspension */); + verifier.Verify(); + soa.Self()->SetVerifierDeps(nullptr); + has_failures = verifier.HasFailures(); + found_method = true; } - it.Next(); } - CHECK(method != nullptr); - - Thread::Current()->SetVerifierDeps(callbacks_->GetVerifierDeps()); - MethodVerifier verifier(Thread::Current(), - primary_dex_file_, - dex_cache_handle, - class_loader_handle, - *class_def, - it.GetMethodCodeItem(), - it.GetMemberIndex(), - method, - it.GetMethodAccessFlags(), - true /* can_load_classes */, - true /* allow_soft_failures */, - true /* need_precise_constants */, - false /* verify to dump */, - true /* allow_thread_suspension */); - verifier.Verify(); - Thread::Current()->SetVerifierDeps(nullptr); - return !verifier.HasFailures(); + CHECK(found_method) << "Expected to find method " << method_name; + return !has_failures; } void VerifyDexFile(const char* multidex = nullptr) { { ScopedObjectAccess soa(Thread::Current()); - LoadDexFile(&soa, "VerifierDeps", multidex); + LoadDexFile(soa, "VerifierDeps", multidex); } SetupCompilerDriver(); VerifyWithCompilerDriver(/* verifier_deps */ nullptr); @@ -206,13 +203,14 @@ class VerifierDepsTest : public CommonCompilerTest { bool is_strict, bool is_assignable) { ScopedObjectAccess soa(Thread::Current()); - LoadDexFile(&soa); - mirror::Class* klass_dst = FindClassByName(dst, &soa); + LoadDexFile(soa); + StackHandleScope<1> hs(soa.Self()); + Handle klass_dst = hs.NewHandle(FindClassByName(soa, dst)); DCHECK(klass_dst != nullptr) << dst; - mirror::Class* klass_src = FindClassByName(src, &soa); + ObjPtr klass_src = FindClassByName(soa, src); DCHECK(klass_src != nullptr) << src; verifier_deps_->AddAssignability(*primary_dex_file_, - klass_dst, + klass_dst.Get(), klass_src, is_strict, is_assignable); @@ -236,6 +234,8 @@ class VerifierDepsTest : public CommonCompilerTest { if (cls == nullptr) { CHECK(soa.Self()->IsExceptionPending()); soa.Self()->ClearException(); + } else if (&cls->GetDexFile() != dex_file) { + // Ignore classes from different dex files. } else if (unverified_classes.find(class_def.class_idx_) == unverified_classes.end()) { ASSERT_EQ(cls->GetStatus(), ClassStatus::kVerified); } else { @@ -453,12 +453,12 @@ class VerifierDepsTest : public CommonCompilerTest { std::vector dex_files_; const DexFile* primary_dex_file_; jobject class_loader_; - mirror::Class* klass_Main_; + ObjPtr klass_Main_; }; TEST_F(VerifierDepsTest, StringToId) { ScopedObjectAccess soa(Thread::Current()); - LoadDexFile(&soa); + LoadDexFile(soa); dex::StringIndex id_Main1 = verifier_deps_->GetIdFromString(*primary_dex_file_, "LMain;"); ASSERT_LT(id_Main1.index_, primary_dex_file_->NumStringIds()); @@ -1441,7 +1441,7 @@ TEST_F(VerifierDepsTest, CompilerDriver) { for (bool verify_failure : { false, true }) { { ScopedObjectAccess soa(Thread::Current()); - LoadDexFile(&soa, "VerifierDeps", multi); + LoadDexFile(soa, "VerifierDeps", multi); } VerifyWithCompilerDriver(/* verifier_deps */ nullptr); @@ -1450,7 +1450,7 @@ TEST_F(VerifierDepsTest, CompilerDriver) { { ScopedObjectAccess soa(Thread::Current()); - LoadDexFile(&soa, "VerifierDeps", multi); + LoadDexFile(soa, "VerifierDeps", multi); } verifier::VerifierDeps decoded_deps(dex_files_, ArrayRef(buffer)); if (verify_failure) { diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index b793406ba0218f152e41453e38c5c5be0b9bb88d..88e69cdb2aa3e444c957bae6b7e00ddde719d98e 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -24,7 +24,44 @@ art_cc_defaults { "linker/image_writer.cc", "linker/multi_oat_relative_patcher.cc", "linker/oat_writer.cc", + "linker/relative_patcher.cc", ], + + codegen: { + arm: { + srcs: [ + "linker/arm/relative_patcher_arm_base.cc", + "linker/arm/relative_patcher_thumb2.cc", + ], + }, + arm64: { + srcs: [ + "linker/arm64/relative_patcher_arm64.cc", + ], + }, + mips: { + srcs: [ + "linker/mips/relative_patcher_mips.cc", + ], + }, + mips64: { + srcs: [ + "linker/mips64/relative_patcher_mips64.cc", + ], + }, + x86: { + srcs: [ + "linker/x86/relative_patcher_x86.cc", + "linker/x86/relative_patcher_x86_base.cc", + ], + }, + x86_64: { + srcs: [ + "linker/x86_64/relative_patcher_x86_64.cc", + ], + }, + }, + target: { android: { // For atrace. @@ -69,6 +106,7 @@ art_cc_static_library { "libart-compiler", "libart-dexlayout", "libart", + "libprofile", ], } @@ -82,6 +120,7 @@ art_cc_static_library { "libartd-compiler", "libartd-dexlayout", "libartd", + "libprofiled", ], } @@ -159,10 +198,12 @@ art_cc_binary { "dex2oat-pgo-defaults", ], shared_libs: [ + "libprofile", "libart-compiler", "libart-dexlayout", "libart", "libdexfile", + "libartbase", "libbase", "liblz4", "libsigchain", @@ -195,10 +236,12 @@ art_cc_binary { "dex2oat-defaults", ], shared_libs: [ + "libprofiled", "libartd-compiler", "libartd-dexlayout", "libartd", "libdexfiled", + "libartbased", "libbase", "liblz4", "libsigchain", @@ -208,8 +251,8 @@ art_cc_binary { ], } -art_cc_binary { - name: "dex2oats", +cc_defaults { + name: "dex2oats-defaults", device_supported: false, static_executable: true, defaults: ["dex2oat-defaults"], @@ -226,47 +269,49 @@ art_cc_binary { // Try to get rid of it. "-z muldefs", ], + static_libs: art_static_dependencies, +} + +art_cc_binary { + name: "dex2oats", + defaults: ["dex2oats-defaults"], static_libs: [ "libart-dex2oat", "libart-compiler", "libart-dexlayout", "libart", + "libartbase", "libdexfile", + "libprofile", "libvixl-arm", "libvixl-arm64", - ] + art_static_dependencies, + ], } art_cc_binary { name: "dex2oatds", - device_supported: false, - static_executable: true, defaults: [ "art_debug_defaults", - "dex2oat-defaults", + "dex2oats-defaults", ], target: { - darwin: { - enabled: false, + linux_glibc_x86_64: { + use_clang_lld: true, }, }, - ldflags: [ - // We need this because GC stress mode makes use of - // _Unwind_GetIP and _Unwind_Backtrace and the symbols are also - // defined in libgcc_eh.a(unwind-dw2.o) - // TODO: Having this is not ideal as it might obscure errors. - // Try to get rid of it. - "-z muldefs", - ], + // b/79417743, oatdump 32-bit tests failed with clang lld + use_clang_lld: false, static_libs: [ "libartd-dex2oat", "libartd-compiler", "libartd-dexlayout", "libartd", + "libartbased", + "libprofiled", "libdexfiled", "libvixld-arm", "libvixld-arm64", - ] + art_static_dependencies, + ], } art_cc_test { @@ -284,11 +329,47 @@ art_cc_test { "linker/multi_oat_relative_patcher_test.cc", "linker/oat_writer_test.cc", ], + + codegen: { + arm: { + srcs: [ + "linker/arm/relative_patcher_thumb2_test.cc", + ], + }, + arm64: { + srcs: [ + "linker/arm64/relative_patcher_arm64_test.cc", + ], + }, + mips: { + srcs: [ + "linker/mips/relative_patcher_mips_test.cc", + "linker/mips/relative_patcher_mips32r6_test.cc", + ], + }, + mips64: { + srcs: [ + "linker/mips64/relative_patcher_mips64_test.cc", + ], + }, + x86: { + srcs: [ + "linker/x86/relative_patcher_x86_test.cc", + ], + }, + x86_64: { + srcs: [ + "linker/x86_64/relative_patcher_x86_64_test.cc", + ], + }, + }, + header_libs: ["dex2oat_headers"], include_dirs: [ "external/zlib", ], shared_libs: [ + "libprofiled", "libartd-compiler", "libartd-dexlayout", "libbase", diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index fe927bbc1c233791e8d3c63ad761030978d6eaa3..cbc6424466990203ce086a6f9f1035b235f83d56 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -27,7 +27,6 @@ #include #include #include -#include #include #if defined(__linux__) && defined(__arm__) @@ -55,6 +54,7 @@ #include "base/timing_logger.h" #include "base/unix_file/fd_file.h" #include "base/utils.h" +#include "base/zip_archive.h" #include "class_linker.h" #include "class_loader_context.h" #include "cmdline_parser.h" @@ -77,8 +77,7 @@ #include "gc/space/space-inl.h" #include "gc/verification.h" #include "interpreter/unstarted_runtime.h" -#include "java_vm_ext.h" -#include "jit/profile_compilation_info.h" +#include "jni/java_vm_ext.h" #include "linker/buffered_output_stream.h" #include "linker/elf_writer.h" #include "linker/elf_writer_quick.h" @@ -92,13 +91,13 @@ #include "mirror/object_array-inl.h" #include "oat_file.h" #include "oat_file_assistant.h" +#include "profile/profile_compilation_info.h" #include "runtime.h" #include "runtime_options.h" #include "scoped_thread_state_change-inl.h" #include "vdex_file.h" #include "verifier/verifier_deps.h" #include "well_known_classes.h" -#include "zip_archive.h" namespace art { @@ -291,7 +290,7 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(" Default: default"); UsageError(""); UsageError(" --compile-pic: Force indirect use of code, methods, and classes"); - UsageError(" Default: disabled"); + UsageError(" Default: disabled for apps (ignored for boot image which is always PIC)"); UsageError(""); UsageError(" --compiler-backend=(Quick|Optimizing): select compiler backend"); UsageError(" set."); @@ -350,6 +349,9 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(""); UsageError(" --dump-timings: display a breakdown of where time was spent"); UsageError(""); + UsageError(" --dump-pass-timings: display a breakdown of time spent in optimization"); + UsageError(" passes for each compiled method."); + UsageError(""); UsageError(" -g"); UsageError(" --generate-debug-info: Generate debug information for native debugging,"); UsageError(" such as stack unwinding information, ELF symbols and DWARF sections."); @@ -608,7 +610,6 @@ class Dex2Oat FINAL { public: explicit Dex2Oat(TimingLogger* timings) : compiler_kind_(Compiler::kOptimizing), - instruction_set_(kRuntimeISA == InstructionSet::kArm ? InstructionSet::kThumb2 : kRuntimeISA), // Take the default set of instruction features from the build. image_file_location_oat_checksum_(0), image_file_location_oat_data_begin_(0), @@ -629,10 +630,6 @@ class Dex2Oat FINAL { image_classes_zip_filename_(nullptr), image_classes_filename_(nullptr), image_storage_mode_(ImageHeader::kStorageModeUncompressed), - compiled_classes_zip_filename_(nullptr), - compiled_classes_filename_(nullptr), - compiled_methods_zip_filename_(nullptr), - compiled_methods_filename_(nullptr), passes_to_run_filename_(nullptr), dirty_image_objects_filename_(nullptr), multi_image_(false), @@ -644,7 +641,6 @@ class Dex2Oat FINAL { driver_(nullptr), opened_dex_files_maps_(), opened_dex_files_(), - no_inline_from_dex_files_(), avoid_storing_invocation_(false), swap_fd_(kInvalidFd), app_image_fd_(kInvalidFd), @@ -658,7 +654,7 @@ class Dex2Oat FINAL { // 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(); @@ -699,35 +695,39 @@ class Dex2Oat FINAL { } bool VerifyProfileData() { - return profile_compilation_info_->VerifyProfileData(dex_files_); + return profile_compilation_info_->VerifyProfileData(compiler_options_->dex_files_for_oat_file_); } void ParseInstructionSetVariant(const std::string& option, ParserOptions* parser_options) { - instruction_set_features_ = InstructionSetFeatures::FromVariant( - instruction_set_, option, &parser_options->error_msg); - if (instruction_set_features_.get() == nullptr) { + compiler_options_->instruction_set_features_ = InstructionSetFeatures::FromVariant( + compiler_options_->instruction_set_, option, &parser_options->error_msg); + if (compiler_options_->instruction_set_features_ == nullptr) { Usage("%s", parser_options->error_msg.c_str()); } } void ParseInstructionSetFeatures(const std::string& option, ParserOptions* parser_options) { - if (instruction_set_features_ == nullptr) { - instruction_set_features_ = InstructionSetFeatures::FromVariant( - instruction_set_, "default", &parser_options->error_msg); - if (instruction_set_features_.get() == nullptr) { + if (compiler_options_->instruction_set_features_ == nullptr) { + compiler_options_->instruction_set_features_ = InstructionSetFeatures::FromVariant( + compiler_options_->instruction_set_, "default", &parser_options->error_msg); + if (compiler_options_->instruction_set_features_ == nullptr) { Usage("Problem initializing default instruction set features variant: %s", parser_options->error_msg.c_str()); } } - instruction_set_features_ = - instruction_set_features_->AddFeaturesFromString(option, &parser_options->error_msg); - if (instruction_set_features_ == nullptr) { + compiler_options_->instruction_set_features_ = + compiler_options_->instruction_set_features_->AddFeaturesFromString( + option, &parser_options->error_msg); + if (compiler_options_->instruction_set_features_ == nullptr) { Usage("Error parsing '%s': %s", option.c_str(), parser_options->error_msg.c_str()); } } void ProcessOptions(ParserOptions* parser_options) { compiler_options_->boot_image_ = !image_filenames_.empty(); + if (compiler_options_->boot_image_) { + compiler_options_->compile_pic_ = true; + } compiler_options_->app_image_ = app_image_fd_ != -1 || !app_image_file_name_.empty(); if (IsBootImage() && image_filenames_.size() == 1) { @@ -756,7 +756,7 @@ class Dex2Oat FINAL { } if ((output_vdex_fd_ == -1) != (oat_fd_ == -1)) { - Usage("VDEX and OAT output must be specified either with one --oat-filename " + Usage("VDEX and OAT output must be specified either with one --oat-file " "or with --oat-fd and --output-vdex-fd file descriptors"); } @@ -818,18 +818,6 @@ class Dex2Oat FINAL { Usage("--image-classes-zip should be used with --image-classes"); } - if (compiled_classes_filename_ != nullptr && !IsBootImage()) { - Usage("--compiled-classes should only be used with --image"); - } - - if (compiled_classes_filename_ != nullptr && !boot_image_filename_.empty()) { - Usage("--compiled-classes should not be used with --boot-image"); - } - - if (compiled_classes_zip_filename_ != nullptr && compiled_classes_filename_ == nullptr) { - Usage("--compiled-classes-zip should be used with --compiled-classes"); - } - if (dex_filenames_.empty() && zip_fd_ == -1) { Usage("Input must be supplied with either --dex-file or --zip-fd"); } @@ -873,9 +861,7 @@ class Dex2Oat FINAL { } if (have_profile_file || have_profile_fd) { - if (compiled_classes_filename_ != nullptr || - compiled_classes_zip_filename_ != nullptr || - image_classes_filename_ != nullptr || + if (image_classes_filename_ != nullptr || image_classes_zip_filename_ != nullptr) { Usage("Profile based image creation is not supported with image or compiled classes"); } @@ -887,23 +873,23 @@ class Dex2Oat FINAL { // If no instruction set feature was given, use the default one for the target // instruction set. - if (instruction_set_features_.get() == nullptr) { - instruction_set_features_ = InstructionSetFeatures::FromVariant( - instruction_set_, "default", &parser_options->error_msg); - if (instruction_set_features_.get() == nullptr) { + if (compiler_options_->instruction_set_features_.get() == nullptr) { + compiler_options_->instruction_set_features_ = InstructionSetFeatures::FromVariant( + compiler_options_->instruction_set_, "default", &parser_options->error_msg); + if (compiler_options_->instruction_set_features_ == nullptr) { Usage("Problem initializing default instruction set features variant: %s", parser_options->error_msg.c_str()); } } - if (instruction_set_ == kRuntimeISA) { + if (compiler_options_->instruction_set_ == kRuntimeISA) { std::unique_ptr runtime_features( InstructionSetFeatures::FromCppDefines()); - if (!instruction_set_features_->Equals(runtime_features.get())) { + if (!compiler_options_->GetInstructionSetFeatures()->Equals(runtime_features.get())) { LOG(WARNING) << "Mismatch between dex2oat instruction set features (" - << *instruction_set_features_ << ") and those of dex2oat executable (" - << *runtime_features <<") for the command line:\n" - << CommandLine(); + << *compiler_options_->GetInstructionSetFeatures() + << ") and those of dex2oat executable (" << *runtime_features + << ") for the command line:\n" << CommandLine(); } } @@ -913,7 +899,7 @@ class Dex2Oat FINAL { // Checks are all explicit until we know the architecture. // Set the compilation target's implicit checks options. - switch (instruction_set_) { + switch (compiler_options_->GetInstructionSet()) { case InstructionSet::kArm: case InstructionSet::kThumb2: case InstructionSet::kArm64: @@ -969,9 +955,9 @@ class Dex2Oat FINAL { compiler_options_->force_determinism_ = force_determinism_; if (passes_to_run_filename_ != nullptr) { - passes_to_run_.reset(ReadCommentedInputFromFile>( + passes_to_run_ = ReadCommentedInputFromFile>( passes_to_run_filename_, - nullptr)); // No post-processing. + nullptr); // No post-processing. if (passes_to_run_.get() == nullptr) { Usage("Failed to read list of passes to run."); } @@ -1210,10 +1196,6 @@ class Dex2Oat FINAL { AssignIfExists(args, M::Threads, &thread_count_); AssignIfExists(args, M::ImageClasses, &image_classes_filename_); AssignIfExists(args, M::ImageClassesZip, &image_classes_zip_filename_); - AssignIfExists(args, M::CompiledClasses, &compiled_classes_filename_); - AssignIfExists(args, M::CompiledClassesZip, &compiled_classes_zip_filename_); - AssignIfExists(args, M::CompiledMethods, &compiled_methods_filename_); - AssignIfExists(args, M::CompiledMethodsZip, &compiled_methods_zip_filename_); AssignIfExists(args, M::Passes, &passes_to_run_filename_); AssignIfExists(args, M::BootImage, &parser_options->boot_image_filename); AssignIfExists(args, M::AndroidRoot, &android_root_); @@ -1236,10 +1218,10 @@ class Dex2Oat FINAL { AssignIfExists(args, M::Backend, &compiler_kind_); parser_options->requested_specific_compiler = args.Exists(M::Backend); - AssignIfExists(args, M::TargetInstructionSet, &instruction_set_); + AssignIfExists(args, M::TargetInstructionSet, &compiler_options_->instruction_set_); // arm actually means thumb2. - if (instruction_set_ == InstructionSet::kArm) { - instruction_set_ = InstructionSet::kThumb2; + if (compiler_options_->instruction_set_ == InstructionSet::kArm) { + compiler_options_->instruction_set_ = InstructionSet::kThumb2; } AssignTrueIfExists(args, M::Host, &is_host_); @@ -1276,10 +1258,10 @@ class Dex2Oat FINAL { if (stored_class_loader_context_ == nullptr) { Usage("Option --stored-class-loader-context has an incorrect format: %s", stored_context_arg.c_str()); - } else if (!class_loader_context_->VerifyClassLoaderContextMatch( + } else if (class_loader_context_->VerifyClassLoaderContextMatch( stored_context_arg, /*verify_names*/ false, - /*verify_checksums*/ false)) { + /*verify_checksums*/ false) != ClassLoaderContext::VerificationResult::kVerifies) { Usage( "Option --stored-class-loader-context '%s' mismatches --class-loader-context '%s'", stored_context_arg.c_str(), @@ -1509,23 +1491,21 @@ class Dex2Oat FINAL { if (!IsImage()) { return; } - // If we don't have a profile, treat it as an empty set of classes. b/77340429 - if (image_classes_ == nullptr) { - // May be non-null when --image-classes is passed in, in that case avoid clearing the list. - image_classes_.reset(new std::unordered_set()); - } if (profile_compilation_info_ != nullptr) { + // TODO: The following comment looks outdated or misplaced. // Filter out class path classes since we don't want to include these in the image. - image_classes_.reset( - new std::unordered_set( - profile_compilation_info_->GetClassDescriptors(dex_files_))); - VLOG(compiler) << "Loaded " << image_classes_->size() + HashSet image_classes = profile_compilation_info_->GetClassDescriptors( + compiler_options_->dex_files_for_oat_file_); + VLOG(compiler) << "Loaded " << image_classes.size() << " image class descriptors from profile"; if (VLOG_IS_ON(compiler)) { - for (const std::string& s : *image_classes_) { + for (const std::string& s : image_classes) { LOG(INFO) << "Image class " << s; } } + // Note: If we have a profile, classes previously loaded for the --image-classes + // option are overwritten here. + compiler_options_->image_classes_.swap(image_classes); } } @@ -1534,8 +1514,7 @@ class Dex2Oat FINAL { dex2oat::ReturnCode Setup() { TimingLogger::ScopedTiming t("dex2oat Setup", timings_); - if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods() || - !PrepareDirtyObjects()) { + if (!PrepareImageClasses() || !PrepareDirtyObjects()) { return dex2oat::ReturnCode::kOther; } @@ -1652,8 +1631,6 @@ class Dex2Oat FINAL { if (!oat_writers_[i]->WriteAndOpenDexFiles( vdex_files_[i].get(), rodata_.back(), - instruction_set_, - instruction_set_features_.get(), key_value_store_.get(), verify, update_input_vdex_, @@ -1677,10 +1654,11 @@ class Dex2Oat FINAL { } } - dex_files_ = MakeNonOwningPointerVector(opened_dex_files_); + compiler_options_->dex_files_for_oat_file_ = MakeNonOwningPointerVector(opened_dex_files_); + const std::vector& dex_files = compiler_options_->dex_files_for_oat_file_; // If we need to downgrade the compiler-filter for size reasons. - if (!IsBootImage() && IsVeryLarge(dex_files_)) { + if (!IsBootImage() && IsVeryLarge(dex_files)) { // Disable app image to make sure dex2oat unloading is enabled. compiler_options_->DisableAppImage(); @@ -1713,7 +1691,7 @@ class Dex2Oat FINAL { CHECK(driver_ == nullptr); // If we use a swap file, ensure we are above the threshold to make it necessary. if (swap_fd_ != -1) { - if (!UseSwap(IsBootImage(), dex_files_)) { + if (!UseSwap(IsBootImage(), dex_files)) { close(swap_fd_); swap_fd_ = -1; VLOG(compiler) << "Decided to run without swap."; @@ -1756,7 +1734,7 @@ class Dex2Oat FINAL { // Verification results are only required for modes that have any compilation. Avoid // adding the dex files if possible to prevent allocating large arrays. if (verification_results_ != nullptr) { - for (const auto& dex_file : dex_files_) { + for (const auto& dex_file : dex_files) { // Pre-register dex files so that we can access verification results without locks during // compilation and verification. verification_results_->AddDexFile(dex_file); @@ -1774,7 +1752,7 @@ class Dex2Oat FINAL { // Doesn't return the class loader since it's not meant to be used for image compilation. void CompileDexFilesIndividually() { CHECK(!IsImage()) << "Not supported with image"; - for (const DexFile* dex_file : dex_files_) { + for (const DexFile* dex_file : compiler_options_->dex_files_for_oat_file_) { std::vector dex_files(1u, dex_file); VLOG(compiler) << "Compiling " << dex_file->GetLocation(); jobject class_loader = CompileDexFiles(dex_files); @@ -1805,7 +1783,7 @@ class Dex2Oat FINAL { // mode (to reduce RAM used by the compiler). return !IsImage() && !update_input_vdex_ && - dex_files_.size() > 1 && + compiler_options_->dex_files_for_oat_file_.size() > 1 && !CompilerFilter::IsAotCompilationEnabled(compiler_options_->GetCompilerFilter()); } @@ -1832,10 +1810,12 @@ class Dex2Oat FINAL { class_path_files = class_loader_context_->FlattenOpenedDexFiles(); } - std::vector*> dex_file_vectors = { + const std::vector& dex_files = compiler_options_->dex_files_for_oat_file_; + std::vector no_inline_from_dex_files; + const std::vector* dex_file_vectors[] = { &class_linker->GetBootClassPath(), &class_path_files, - &dex_files_ + &dex_files }; for (const std::vector* dex_file_vector : dex_file_vectors) { for (const DexFile* dex_file : *dex_file_vector) { @@ -1854,29 +1834,24 @@ class Dex2Oat FINAL { if (android::base::StartsWith(dex_location, filter.c_str())) { VLOG(compiler) << "Disabling inlining from " << dex_file->GetLocation(); - no_inline_from_dex_files_.push_back(dex_file); + no_inline_from_dex_files.push_back(dex_file); break; } } } } - if (!no_inline_from_dex_files_.empty()) { - compiler_options_->no_inline_from_ = &no_inline_from_dex_files_; + if (!no_inline_from_dex_files.empty()) { + compiler_options_->no_inline_from_.swap(no_inline_from_dex_files); } } driver_.reset(new CompilerDriver(compiler_options_.get(), verification_results_.get(), compiler_kind_, - instruction_set_, - instruction_set_features_.get(), - image_classes_.release(), - compiled_classes_.release(), - compiled_methods_.release(), + &compiler_options_->image_classes_, thread_count_, swap_fd_, profile_compilation_info_.get())); - driver_->SetDexFilesForOatFile(dex_files_); if (!IsBootImage()) { driver_->SetClasspathDexFiles(class_loader_context_->FlattenOpenedDexFiles()); } @@ -1892,9 +1867,10 @@ class Dex2Oat FINAL { } // Setup vdex for compilation. + const std::vector& dex_files = compiler_options_->dex_files_for_oat_file_; if (!DoEagerUnquickeningOfVdex() && input_vdex_file_ != nullptr) { callbacks_->SetVerifierDeps( - new verifier::VerifierDeps(dex_files_, input_vdex_file_->GetVerifierDepsData())); + new verifier::VerifierDeps(dex_files, input_vdex_file_->GetVerifierDepsData())); // TODO: we unquicken unconditionally, as we don't know // if the boot image has changed. How exactly we'll know is under @@ -1904,11 +1880,11 @@ class Dex2Oat FINAL { // We do not decompile a RETURN_VOID_NO_BARRIER into a RETURN_VOID, as the quickening // optimization does not depend on the boot image (the optimization relies on not // having final fields in a class, which does not change for an app). - input_vdex_file_->Unquicken(dex_files_, /* decompile_return_instruction */ false); + input_vdex_file_->Unquicken(dex_files, /* decompile_return_instruction */ false); } else { // Create the main VerifierDeps, here instead of in the compiler since we want to aggregate // the results for all the dex files, not just the results for the current dex file. - callbacks_->SetVerifierDeps(new verifier::VerifierDeps(dex_files_)); + callbacks_->SetVerifierDeps(new verifier::VerifierDeps(dex_files)); } // Invoke the compilation. if (compile_individually) { @@ -1916,7 +1892,7 @@ class Dex2Oat FINAL { // Return a null classloader since we already freed released it. return nullptr; } - return CompileDexFiles(dex_files_); + return CompileDexFiles(dex_files); } // Create the class loader, use it to compile, and return. @@ -1925,7 +1901,8 @@ class Dex2Oat FINAL { jobject class_loader = nullptr; if (!IsBootImage()) { - class_loader = class_loader_context_->CreateClassLoader(dex_files_); + class_loader = + class_loader_context_->CreateClassLoader(compiler_options_->dex_files_for_oat_file_); callbacks_->SetDexFiles(&dex_files); } @@ -2043,7 +2020,7 @@ class Dex2Oat FINAL { VLOG(compiler) << "App image base=" << reinterpret_cast(image_base_); } - image_writer_.reset(new linker::ImageWriter(*driver_, + image_writer_.reset(new linker::ImageWriter(*compiler_options_, image_base_, compiler_options_->GetCompilePic(), IsAppImage(), @@ -2054,7 +2031,7 @@ class Dex2Oat FINAL { // We need to prepare method offsets in the image address space for direct method patching. TimingLogger::ScopedTiming t2("dex2oat Prepare image address space", timings_); - if (!image_writer_->PrepareImageAddressSpace()) { + if (!image_writer_->PrepareImageAddressSpace(timings_)) { LOG(ERROR) << "Failed to prepare image address space."; return false; } @@ -2098,17 +2075,17 @@ class Dex2Oat FINAL { { TimingLogger::ScopedTiming t2("dex2oat Write ELF", timings_); - linker::MultiOatRelativePatcher patcher(instruction_set_, instruction_set_features_.get()); + linker::MultiOatRelativePatcher patcher(compiler_options_->GetInstructionSet(), + compiler_options_->GetInstructionSetFeatures(), + driver_->GetCompiledMethodStorage()); for (size_t i = 0, size = oat_files_.size(); i != size; ++i) { std::unique_ptr& elf_writer = elf_writers_[i]; std::unique_ptr& oat_writer = oat_writers_[i]; oat_writer->PrepareLayout(&patcher); - - size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset(); - size_t text_size = oat_writer->GetOatSize() - rodata_size; - elf_writer->PrepareDynamicSection(rodata_size, - text_size, + elf_writer->PrepareDynamicSection(oat_writer->GetOatHeader().GetExecutableOffset(), + oat_writer->GetCodeSize(), + oat_writer->GetDataBimgRelRoSize(), oat_writer->GetBssSize(), oat_writer->GetBssMethodsOffset(), oat_writer->GetBssRootsOffset(), @@ -2158,6 +2135,16 @@ class Dex2Oat FINAL { } elf_writer->EndText(text); + if (oat_writer->GetDataBimgRelRoSize() != 0u) { + linker::OutputStream* data_bimg_rel_ro = elf_writer->StartDataBimgRelRo(); + if (!oat_writer->WriteDataBimgRelRo(data_bimg_rel_ro)) { + LOG(ERROR) << "Failed to write .data.bimg.rel.ro section to the ELF file " + << oat_file->GetPath(); + return false; + } + elf_writer->EndDataBimgRelRo(data_bimg_rel_ro); + } + if (!oat_writer->WriteHeader(elf_writer->GetStream(), image_file_location_oat_checksum_, image_file_location_oat_data_begin_, @@ -2385,7 +2372,7 @@ class Dex2Oat FINAL { return dex_files_size >= min_dex_file_cumulative_size_for_swap_; } - bool IsVeryLarge(std::vector& dex_files) { + bool IsVeryLarge(const std::vector& dex_files) { size_t dex_files_size = 0; for (const auto* dex_file : dex_files) { dex_files_size += dex_file->GetHeader().file_size_; @@ -2395,42 +2382,27 @@ class Dex2Oat FINAL { bool PrepareImageClasses() { // If --image-classes was specified, calculate the full list of classes to include in the image. + DCHECK(compiler_options_->image_classes_.empty()); if (image_classes_filename_ != nullptr) { - image_classes_ = + std::unique_ptr> image_classes = ReadClasses(image_classes_zip_filename_, image_classes_filename_, "image"); - if (image_classes_ == nullptr) { + if (image_classes == nullptr) { return false; } - } else if (IsBootImage()) { - image_classes_.reset(new std::unordered_set); + compiler_options_->image_classes_.swap(*image_classes); } return true; } - bool PrepareCompiledClasses() { - // If --compiled-classes was specified, calculate the full list of classes to compile in the - // image. - if (compiled_classes_filename_ != nullptr) { - compiled_classes_ = - ReadClasses(compiled_classes_zip_filename_, compiled_classes_filename_, "compiled"); - if (compiled_classes_ == nullptr) { - return false; - } - } else { - compiled_classes_.reset(nullptr); // By default compile everything. - } - return true; - } - - static std::unique_ptr> ReadClasses(const char* zip_filename, - const char* classes_filename, - const char* tag) { - std::unique_ptr> classes; + static std::unique_ptr> ReadClasses(const char* zip_filename, + const char* classes_filename, + const char* tag) { + std::unique_ptr> classes; std::string error_msg; if (zip_filename != nullptr) { - classes.reset(ReadImageClassesFromZip(zip_filename, classes_filename, &error_msg)); + classes = ReadImageClassesFromZip(zip_filename, classes_filename, &error_msg); } else { - classes.reset(ReadImageClassesFromFile(classes_filename)); + classes = ReadImageClassesFromFile(classes_filename); } if (classes == nullptr) { LOG(ERROR) << "Failed to create list of " << tag << " classes from '" @@ -2439,37 +2411,11 @@ class Dex2Oat FINAL { return classes; } - bool PrepareCompiledMethods() { - // If --compiled-methods was specified, read the methods to compile from the given file(s). - if (compiled_methods_filename_ != nullptr) { - std::string error_msg; - if (compiled_methods_zip_filename_ != nullptr) { - compiled_methods_.reset(ReadCommentedInputFromZip>( - compiled_methods_zip_filename_, - compiled_methods_filename_, - nullptr, // No post-processing. - &error_msg)); - } else { - compiled_methods_.reset(ReadCommentedInputFromFile>( - compiled_methods_filename_, - nullptr)); // No post-processing. - } - if (compiled_methods_.get() == nullptr) { - LOG(ERROR) << "Failed to create list of compiled methods from '" - << compiled_methods_filename_ << "': " << error_msg; - return false; - } - } else { - compiled_methods_.reset(nullptr); // By default compile everything. - } - return true; - } - bool PrepareDirtyObjects() { if (dirty_image_objects_filename_ != nullptr) { - dirty_image_objects_.reset(ReadCommentedInputFromFile>( + dirty_image_objects_ = ReadCommentedInputFromFile>( dirty_image_objects_filename_, - nullptr)); + nullptr); if (dirty_image_objects_ == nullptr) { LOG(ERROR) << "Failed to create list of dirty objects from '" << dirty_image_objects_filename_ << "'"; @@ -2539,17 +2485,14 @@ class Dex2Oat FINAL { elf_writers_.reserve(oat_files_.size()); oat_writers_.reserve(oat_files_.size()); for (const std::unique_ptr& oat_file : oat_files_) { - elf_writers_.emplace_back(linker::CreateElfWriterQuick(instruction_set_, - instruction_set_features_.get(), - compiler_options_.get(), - oat_file.get())); + elf_writers_.emplace_back(linker::CreateElfWriterQuick(*compiler_options_, oat_file.get())); elf_writers_.back()->Start(); bool do_oat_writer_layout = DoDexLayoutOptimizations() || DoOatLayoutOptimizations(); if (profile_compilation_info_ != nullptr && profile_compilation_info_->IsEmpty()) { do_oat_writer_layout = false; } oat_writers_.emplace_back(new linker::OatWriter( - IsBootImage(), + *compiler_options_, timings_, do_oat_writer_layout ? profile_compilation_info_.get() : nullptr, compact_dex_level_)); @@ -2557,8 +2500,9 @@ class Dex2Oat FINAL { } void SaveDexInput() { - for (size_t i = 0; i < dex_files_.size(); ++i) { - const DexFile* dex_file = dex_files_[i]; + const std::vector& dex_files = compiler_options_->dex_files_for_oat_file_; + for (size_t i = 0, size = dex_files.size(); i != size; ++i) { + const DexFile* dex_file = dex_files[i]; std::string tmp_file_name(StringPrintf("/data/local/tmp/dex2oat.%d.%zd.dex", getpid(), i)); std::unique_ptr tmp_file(OS::CreateEmptyFile(tmp_file_name.c_str())); @@ -2596,7 +2540,8 @@ class Dex2Oat FINAL { raw_options.push_back(std::make_pair("compilercallbacks", callbacks)); raw_options.push_back( - std::make_pair("imageinstructionset", GetInstructionSetString(instruction_set_))); + std::make_pair("imageinstructionset", + GetInstructionSetString(compiler_options_->GetInstructionSet()))); // Only allow no boot image for the runtime if we're compiling one. When we compile an app, // we don't want fallback mode, it will abort as we do not push a boot classpath (it might @@ -2659,7 +2604,7 @@ class Dex2Oat FINAL { SetThreadName(kIsDebugBuild ? "dex2oatd" : "dex2oat"); runtime_.reset(Runtime::Current()); - runtime_->SetInstructionSet(instruction_set_); + runtime_->SetInstructionSet(compiler_options_->GetInstructionSet()); for (uint32_t i = 0; i < static_cast(CalleeSaveType::kLastCalleeSaveType); ++i) { CalleeSaveType type = CalleeSaveType(i); if (!runtime_->HasCalleeSaveMethod(type)) { @@ -2671,11 +2616,11 @@ class Dex2Oat FINAL { // set up. interpreter::UnstartedRuntime::Initialize(); - runtime_->GetClassLinker()->RunRootClinits(); + Thread* self = Thread::Current(); + runtime_->RunRootClinits(self); // Runtime::Create acquired the mutator_lock_ that is normally given away when we // Runtime::Start, give it away now so that we don't starve GC. - Thread* self = Thread::Current(); self->TransitionFromRunnableToSuspended(kNative); return true; @@ -2731,45 +2676,43 @@ class Dex2Oat FINAL { } // Reads the class names (java.lang.Object) and returns a set of descriptors (Ljava/lang/Object;) - static std::unordered_set* ReadImageClassesFromFile( + static std::unique_ptr> ReadImageClassesFromFile( const char* image_classes_filename) { std::function process = DotToDescriptor; - return ReadCommentedInputFromFile>(image_classes_filename, - &process); + return ReadCommentedInputFromFile>(image_classes_filename, &process); } // Reads the class names (java.lang.Object) and returns a set of descriptors (Ljava/lang/Object;) - static std::unordered_set* ReadImageClassesFromZip( + static std::unique_ptr> ReadImageClassesFromZip( const char* zip_filename, const char* image_classes_filename, std::string* error_msg) { std::function process = DotToDescriptor; - return ReadCommentedInputFromZip>(zip_filename, - image_classes_filename, - &process, - error_msg); + return ReadCommentedInputFromZip>(zip_filename, + image_classes_filename, + &process, + error_msg); } // Read lines from the given file, dropping comments and empty lines. Post-process each line with // the given function. template - static T* ReadCommentedInputFromFile( + static std::unique_ptr ReadCommentedInputFromFile( const char* input_filename, std::function* process) { std::unique_ptr input_file(new std::ifstream(input_filename, std::ifstream::in)); if (input_file.get() == nullptr) { LOG(ERROR) << "Failed to open input file " << input_filename; return nullptr; } - std::unique_ptr result( - ReadCommentedInputStream(*input_file, process)); + std::unique_ptr result = ReadCommentedInputStream(*input_file, process); input_file->close(); - return result.release(); + return result; } // Read lines from the given file from the given zip file, dropping comments and empty lines. // Post-process each line with the given function. template - static T* ReadCommentedInputFromZip( + static std::unique_ptr ReadCommentedInputFromZip( const char* zip_filename, const char* input_filename, std::function* process, @@ -2801,7 +2744,7 @@ class Dex2Oat FINAL { // Read lines from the given stream, dropping comments and empty lines. Post-process each line // with the given function. template - static T* ReadCommentedInputStream( + static std::unique_ptr ReadCommentedInputStream( std::istream& in_stream, std::function* process) { std::unique_ptr output(new T()); @@ -2818,7 +2761,7 @@ class Dex2Oat FINAL { output->insert(output->end(), dot); } } - return output.release(); + return output; } void LogCompletionTime() { @@ -2856,9 +2799,6 @@ class Dex2Oat FINAL { std::unique_ptr compiler_options_; Compiler::Kind compiler_kind_; - InstructionSet instruction_set_; - std::unique_ptr instruction_set_features_; - uint32_t image_file_location_oat_checksum_; uintptr_t image_file_location_oat_data_begin_; int32_t image_patch_delta_; @@ -2905,22 +2845,13 @@ class Dex2Oat FINAL { const char* image_classes_zip_filename_; const char* image_classes_filename_; ImageHeader::StorageMode image_storage_mode_; - const char* compiled_classes_zip_filename_; - const char* compiled_classes_filename_; - const char* compiled_methods_zip_filename_; - const char* compiled_methods_filename_; const char* passes_to_run_filename_; const char* dirty_image_objects_filename_; - std::unique_ptr> image_classes_; - std::unique_ptr> compiled_classes_; - std::unique_ptr> compiled_methods_; - std::unique_ptr> dirty_image_objects_; + std::unique_ptr> dirty_image_objects_; std::unique_ptr> passes_to_run_; bool multi_image_; bool is_host_; std::string android_root_; - // Dex files we are compiling, does not include the class path dex files. - std::vector dex_files_; std::string no_inline_from_string_; CompactDexLevel compact_dex_level_ = kDefaultCompactDexLevel; @@ -2934,9 +2865,6 @@ class Dex2Oat FINAL { std::vector> opened_dex_files_maps_; std::vector> opened_dex_files_; - // Note that this might contain pointers owned by class_loader_context_. - std::vector no_inline_from_dex_files_; - bool avoid_storing_invocation_; std::string swap_file_name_; int swap_fd_; @@ -2966,7 +2894,7 @@ class Dex2Oat FINAL { // By default, copy the dex to the vdex file only if dex files are // compressed in APK. - CopyOption copy_dex_files_ = CopyOption::kOnlyIfCompressed; + linker::CopyOption copy_dex_files_ = linker::CopyOption::kOnlyIfCompressed; // The reason for invoking the compiler. std::string compilation_reason_; @@ -3176,9 +3104,9 @@ static dex2oat::ReturnCode Dex2oat(int argc, char** argv) { int main(int argc, char** argv) { int result = static_cast(art::Dex2oat(argc, argv)); // Everything was done, do an explicit exit here to avoid running Runtime destructors that take - // time (bug 10645725) unless we're a debug or instrumented build or running on valgrind. Note: - // The Dex2Oat class should not destruct the runtime in this case. - if (!art::kIsDebugBuild && !art::kIsPGOInstrumentation && (RUNNING_ON_MEMORY_TOOL == 0)) { + // time (bug 10645725) unless we're a debug or instrumented build or running on a memory tool. + // Note: The Dex2Oat class should not destruct the runtime in this case. + if (!art::kIsDebugBuild && !art::kIsPGOInstrumentation && !art::kRunningOnMemoryTool) { _exit(result); } return result; diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc index 6f1224916f53310c24d57dbb1a7a10b749ede846..4247e176aa6d33d32fa650da8b1116db054f4332 100644 --- a/dex2oat/dex2oat_image_test.cc +++ b/dex2oat/dex2oat_image_test.cc @@ -34,7 +34,7 @@ #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" #include "dex/method_reference.h" -#include "jit/profile_compilation_info.h" +#include "profile/profile_compilation_info.h" #include "runtime.h" namespace art { @@ -186,52 +186,24 @@ class Dex2oatImageTest : public CommonRuntimeTest { return RunDex2Oat(argv, error_msg); } - int RunDex2Oat(const std::vector& args, std::string* error_msg) { - int link[2]; - - if (pipe(link) == -1) { - return false; - } - - pid_t pid = fork(); - if (pid == -1) { - return false; - } - - if (pid == 0) { - // We need dex2oat to actually log things. - setenv("ANDROID_LOG_TAGS", "*:f", 1); - dup2(link[1], STDERR_FILENO); - close(link[0]); - close(link[1]); - std::vector c_args; - for (const std::string& str : args) { - c_args.push_back(str.c_str()); - } - c_args.push_back(nullptr); - execv(c_args[0], const_cast(c_args.data())); - exit(1); - UNREACHABLE(); - } else { - close(link[1]); - char buffer[128]; - memset(buffer, 0, 128); - ssize_t bytes_read = 0; - - while (TEMP_FAILURE_RETRY(bytes_read = read(link[0], buffer, 128)) > 0) { - *error_msg += std::string(buffer, bytes_read); - } - close(link[0]); - int status = -1; - if (waitpid(pid, &status, 0) != -1) { - return (status == 0); - } + bool RunDex2Oat(const std::vector& args, std::string* error_msg) { + // We only want fatal logging for the error message. + auto post_fork_fn = []() { return setenv("ANDROID_LOG_TAGS", "*:f", 1) == 0; }; + ForkAndExecResult res = ForkAndExec(args, post_fork_fn, error_msg); + if (res.stage != ForkAndExecResult::kFinished) { + *error_msg = strerror(errno); return false; } + return res.StandardSuccess(); } }; TEST_F(Dex2oatImageTest, TestModesAndFilters) { + // This test crashes on the gtest-heap-poisoning configuration + // (AddressSanitizer + CMS/RosAlloc + heap-poisoning); see b/111061592. + // Temporarily disable this test on this configuration to keep + // our automated build/testing green while we work on a fix. + TEST_DISABLED_FOR_MEMORY_TOOL_WITH_HEAP_POISONING_WITHOUT_READ_BARRIERS(); if (kIsTargetBuild) { // This test is too slow for target builds. return; @@ -239,9 +211,7 @@ TEST_F(Dex2oatImageTest, TestModesAndFilters) { ImageSizes base_sizes = CompileImageAndGetSizes({}); ImageSizes image_classes_sizes; ImageSizes compiled_classes_sizes; - ImageSizes compiled_all_classes_sizes; ImageSizes compiled_methods_sizes; - ImageSizes compiled_all_methods_sizes; ImageSizes profile_sizes; std::cout << "Base compile sizes " << base_sizes << std::endl; // Test image classes @@ -257,65 +227,28 @@ TEST_F(Dex2oatImageTest, TestModesAndFilters) { // Sanity check that dex is the same size. EXPECT_EQ(image_classes_sizes.vdex_size, base_sizes.vdex_size); } - // Test compiled classes with all the classes. - { - ScratchFile classes; - // Only compile every even class. - GenerateClasses(classes.GetFile(), /*frequency*/ 1u); - compiled_all_classes_sizes = CompileImageAndGetSizes( - {"--compiled-classes=" + classes.GetFilename()}); - classes.Close(); - std::cout << "Compiled all classes sizes " << compiled_all_classes_sizes << std::endl; - // Check that oat size is smaller since we didn't compile everything. - EXPECT_EQ(compiled_all_classes_sizes.art_size, base_sizes.art_size); - // TODO(mathieuc): Find a reliable way to check compiled code. - // EXPECT_EQ(compiled_all_classes_sizes.oat_size, base_sizes.oat_size); - EXPECT_EQ(compiled_all_classes_sizes.vdex_size, base_sizes.vdex_size); - } // Test compiled classes. { ScratchFile classes; // Only compile every even class. GenerateClasses(classes.GetFile(), /*frequency*/ 2u); compiled_classes_sizes = CompileImageAndGetSizes( - {"--image-classes=" + classes.GetFilename(), - "--compiled-classes=" + classes.GetFilename()}); + {"--image-classes=" + classes.GetFilename()}); classes.Close(); std::cout << "Compiled classes sizes " << compiled_classes_sizes << std::endl; - // Check that oat size is smaller since we didn't compile everything. - // TODO(mathieuc): Find a reliable way to check compiled code. - // EXPECT_LT(compiled_classes_sizes.oat_size, base_sizes.oat_size); // Art file should be smaller than image classes version since we included fewer classes in the // list. EXPECT_LT(compiled_classes_sizes.art_size, image_classes_sizes.art_size); } - // Test compiled methods. - { - ScratchFile methods; - // Only compile every even class. - GenerateMethods(methods.GetFile(), /*frequency*/ 1u); - compiled_all_methods_sizes = CompileImageAndGetSizes( - {"--compiled-methods=" + methods.GetFilename()}); - methods.Close(); - std::cout << "Compiled all methods sizes " << compiled_all_methods_sizes << std::endl; - EXPECT_EQ(compiled_all_classes_sizes.art_size, base_sizes.art_size); - // TODO(mathieuc): Find a reliable way to check compiled code. b/63746626 - // EXPECT_EQ(compiled_all_classes_sizes.oat_size, base_sizes.oat_size); - EXPECT_EQ(compiled_all_classes_sizes.vdex_size, base_sizes.vdex_size); - } static size_t kMethodFrequency = 3; static size_t kTypeFrequency = 4; // Test compiling fewer methods and classes. { - ScratchFile methods; ScratchFile classes; // Only compile every even class. - GenerateMethods(methods.GetFile(), kMethodFrequency); GenerateClasses(classes.GetFile(), kTypeFrequency); compiled_methods_sizes = CompileImageAndGetSizes( - {"--image-classes=" + classes.GetFilename(), - "--compiled-methods=" + methods.GetFilename()}); - methods.Close(); + {"--image-classes=" + classes.GetFilename()}); classes.Close(); std::cout << "Compiled fewer methods sizes " << compiled_methods_sizes << std::endl; } diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc index 5843691a242e224e207864bd521b16203df30bb9..bf9edf738490da85da0ab4a93151a641fba2116b 100644 --- a/dex2oat/dex2oat_options.cc +++ b/dex2oat/dex2oat_options.cc @@ -157,19 +157,7 @@ static void AddSwapMappings(Builder& builder) { static void AddCompilerMappings(Builder& builder) { builder. - Define("--compiled-classes=_") - .WithType() - .IntoKey(M::CompiledClasses) - .Define("--compiled-classes-zip=_") - .WithType() - .IntoKey(M::CompiledClassesZip) - .Define("--compiled-methods=_") - .WithType() - .IntoKey(M::CompiledMethods) - .Define("--compiled-methods-zip=_") - .WithType() - .IntoKey(M::CompiledMethodsZip) - .Define("--run-passes=_") + Define("--run-passes=_") .WithType() .IntoKey(M::Passes) .Define("--profile-file=_") @@ -234,10 +222,10 @@ static Parser CreateArgumentParser() { .Define("--force-determinism") .IntoKey(M::ForceDeterminism) .Define("--copy-dex-files=_") - .WithType() - .WithValueMap({{"true", CopyOption::kOnlyIfCompressed}, - {"false", CopyOption::kNever}, - {"always", CopyOption::kAlways}}) + .WithType() + .WithValueMap({{"true", linker::CopyOption::kOnlyIfCompressed}, + {"false", linker::CopyOption::kNever}, + {"always", linker::CopyOption::kAlways}}) .IntoKey(M::CopyDexFiles) .Define("--classpath-dir=_") .WithType() diff --git a/dex2oat/dex2oat_options.def b/dex2oat/dex2oat_options.def index 1a913a9bbf3da1cc8eb211a16cf96793308ce859..fe5c4e69a7dce21b3c1d4eac5f1615524f7529d4 100644 --- a/dex2oat/dex2oat_options.def +++ b/dex2oat/dex2oat_options.def @@ -56,10 +56,6 @@ DEX2OAT_OPTIONS_KEY (std::vector, ImageFilenames) DEX2OAT_OPTIONS_KEY (std::string, ImageClasses) DEX2OAT_OPTIONS_KEY (std::string, ImageClassesZip) DEX2OAT_OPTIONS_KEY (ImageHeader::StorageMode, ImageFormat) -DEX2OAT_OPTIONS_KEY (std::string, CompiledClasses) -DEX2OAT_OPTIONS_KEY (std::string, CompiledClassesZip) -DEX2OAT_OPTIONS_KEY (std::string, CompiledMethods) -DEX2OAT_OPTIONS_KEY (std::string, CompiledMethodsZip) DEX2OAT_OPTIONS_KEY (std::string, Passes) DEX2OAT_OPTIONS_KEY (std::string, Base) // TODO: Hex string parsing. DEX2OAT_OPTIONS_KEY (std::string, BootImage) @@ -74,7 +70,7 @@ DEX2OAT_OPTIONS_KEY (Unit, Host) DEX2OAT_OPTIONS_KEY (Unit, DumpTiming) DEX2OAT_OPTIONS_KEY (Unit, DumpPasses) DEX2OAT_OPTIONS_KEY (Unit, DumpStats) -DEX2OAT_OPTIONS_KEY (CopyOption, CopyDexFiles) +DEX2OAT_OPTIONS_KEY (linker::CopyOption, CopyDexFiles) DEX2OAT_OPTIONS_KEY (Unit, AvoidStoringInvocation) DEX2OAT_OPTIONS_KEY (std::string, SwapFile) DEX2OAT_OPTIONS_KEY (int, SwapFileFd) diff --git a/dex2oat/dex2oat_options.h b/dex2oat/dex2oat_options.h index cc124c1afa91cc1e58b65bb8952c1fad3e09a8d0..27d3d25f2ae41527b27949183b50549b252afc41 100644 --- a/dex2oat/dex2oat_options.h +++ b/dex2oat/dex2oat_options.h @@ -28,6 +28,7 @@ #include "dex/compact_dex_level.h" #include "driver/compiler_options_map.h" #include "image.h" +#include "linker/oat_writer.h" namespace art { diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index a229d7dd7194c1769773f82fbbd8ed4084f691d8..1196d113d4c066d8067c8356bb5c9c5393b5d99f 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -38,9 +38,9 @@ #include "dex/dex_file_loader.h" #include "dex2oat_environment_test.h" #include "dex2oat_return_codes.h" -#include "jit/profile_compilation_info.h" #include "oat.h" #include "oat_file.h" +#include "profile/profile_compilation_info.h" #include "vdex_file.h" #include "ziparchive/zip_writer.h" @@ -230,47 +230,15 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { LOG(ERROR) << all_args; } - int link[2]; - - if (pipe(link) == -1) { - return false; - } - - pid_t pid = fork(); - if (pid == -1) { - return false; - } - - if (pid == 0) { - // We need dex2oat to actually log things. - setenv("ANDROID_LOG_TAGS", "*:d", 1); - dup2(link[1], STDERR_FILENO); - close(link[0]); - close(link[1]); - std::vector c_args; - for (const std::string& str : argv) { - c_args.push_back(str.c_str()); - } - c_args.push_back(nullptr); - execv(c_args[0], const_cast(c_args.data())); - exit(1); - UNREACHABLE(); - } else { - close(link[1]); - char buffer[128]; - memset(buffer, 0, 128); - ssize_t bytes_read = 0; - - while (TEMP_FAILURE_RETRY(bytes_read = read(link[0], buffer, 128)) > 0) { - output_ += std::string(buffer, bytes_read); - } - close(link[0]); - int status = -1; - if (waitpid(pid, &status, 0) != -1) { - success_ = (status == 0); - } - return status; + // We need dex2oat to actually log things. + auto post_fork_fn = []() { return setenv("ANDROID_LOG_TAGS", "*:d", 1) == 0; }; + ForkAndExecResult res = ForkAndExec(argv, post_fork_fn, &output_); + if (res.stage != ForkAndExecResult::kFinished) { + *error_msg = strerror(errno); + return -1; } + success_ = res.StandardSuccess(); + return res.status_code; } std::string output_ = ""; @@ -472,8 +440,8 @@ class Dex2oatSwapUseTest : public Dex2oatSwapTest { }; 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 +1022,6 @@ TEST_F(Dex2oatWatchdogTest, TestWatchdogOK) { } 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). @@ -1770,7 +1736,7 @@ TEST_F(Dex2oatTest, CompactDexGenerationFailureMultiDex) { writer.Finish(); ASSERT_EQ(apk_file.GetFile()->Flush(), 0); } - const std::string dex_location = apk_file.GetFilename(); + const std::string& dex_location = apk_file.GetFilename(); const std::string odex_location = GetOdexDir() + "/output.odex"; GenerateOdexForTest(dex_location, odex_location, @@ -1912,29 +1878,35 @@ TEST_F(Dex2oatTest, DontExtract) { ASSERT_EQ(dm_file.GetFile()->Flush(), 0); } + auto generate_and_check = [&](CompilerFilter::Filter filter) { + GenerateOdexForTest(dex_location, + odex_location, + filter, + { "--dump-timings", + "--dm-file=" + dm_file.GetFilename(), + // Pass -Xuse-stderr-logger have dex2oat output in output_ on target. + "--runtime-arg", + "-Xuse-stderr-logger" }, + true, // expect_success + false, // use_fd + [](const OatFile& o) { + CHECK(o.ContainsDexCode()); + }); + // Check the output for "Fast verify", this is printed from --dump-timings. + std::istringstream iss(output_); + std::string line; + bool found_fast_verify = false; + const std::string kFastVerifyString = "Fast Verify"; + while (std::getline(iss, line) && !found_fast_verify) { + found_fast_verify = found_fast_verify || line.find(kFastVerifyString) != std::string::npos; + } + EXPECT_TRUE(found_fast_verify) << "Expected to find " << kFastVerifyString << "\n" << output_; + }; + // Generate a quickened dex by using the input dm file to verify. - GenerateOdexForTest(dex_location, - odex_location, - CompilerFilter::Filter::kQuicken, - { "--dump-timings", - "--dm-file=" + dm_file.GetFilename(), - // Pass -Xuse-stderr-logger have dex2oat output in output_ on target. - "--runtime-arg", - "-Xuse-stderr-logger" }, - true, // expect_success - false, // use_fd - [](const OatFile& o) { - CHECK(o.ContainsDexCode()); - }); - // Check the output for "Fast verify", this is printed from --dump-timings. - std::istringstream iss(output_); - std::string line; - bool found_fast_verify = false; - const std::string kFastVerifyString = "Fast Verify"; - while (std::getline(iss, line) && !found_fast_verify) { - found_fast_verify = found_fast_verify || line.find(kFastVerifyString) != std::string::npos; - } - EXPECT_TRUE(found_fast_verify) << "Expected to find " << kFastVerifyString << "\n" << output_; + generate_and_check(CompilerFilter::Filter::kQuicken); + // Use verify compiler filter to sanity check that FastVerify works for that filter too. + generate_and_check(CompilerFilter::Filter::kVerify); } // Test that dex files with quickened opcodes aren't dequickened. @@ -1970,7 +1942,7 @@ TEST_F(Dex2oatTest, QuickenedInput) { << "Failed to find candidate code item with only one code unit in last instruction."; }); - std::string dex_location = temp_dex.GetFilename(); + const std::string& dex_location = temp_dex.GetFilename(); std::string odex_location = GetOdexDir() + "/quickened.odex"; std::string vdex_location = GetOdexDir() + "/quickened.vdex"; std::unique_ptr vdex_output(OS::CreateEmptyFile(vdex_location.c_str())); @@ -2045,7 +2017,7 @@ TEST_F(Dex2oatTest, CompactDexInvalidSource) { writer.Finish(); ASSERT_EQ(invalid_dex.GetFile()->Flush(), 0); } - const std::string dex_location = invalid_dex.GetFilename(); + const std::string& dex_location = invalid_dex.GetFilename(); const std::string odex_location = GetOdexDir() + "/output.odex"; std::string error_msg; int status = GenerateOdexForTestWithStatus( diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/dex2oat/linker/arm/relative_patcher_arm_base.cc similarity index 92% rename from compiler/linker/arm/relative_patcher_arm_base.cc rename to dex2oat/linker/arm/relative_patcher_arm_base.cc index 6e0286afac192e885a88a09b50053d61333c1921..a2ba3392785419b99309b72dfe96a00ede606723 100644 --- a/compiler/linker/arm/relative_patcher_arm_base.cc +++ b/dex2oat/linker/arm/relative_patcher_arm_base.cc @@ -30,8 +30,9 @@ namespace linker { class ArmBaseRelativePatcher::ThunkData { public: - ThunkData(std::vector code, uint32_t max_next_offset) - : code_(std::move(code)), + ThunkData(ArrayRef code, const std::string& debug_name, uint32_t max_next_offset) + : code_(code), + debug_name_(debug_name), offsets_(), max_next_offset_(max_next_offset), pending_offset_(0u) { @@ -45,7 +46,11 @@ class ArmBaseRelativePatcher::ThunkData { } ArrayRef GetCode() const { - return ArrayRef(code_); + return code_; + } + + const std::string& GetDebugName() const { + return debug_name_; } bool NeedsNextThunk() const { @@ -142,10 +147,11 @@ class ArmBaseRelativePatcher::ThunkData { } private: - std::vector code_; // The code of the thunk. - std::vector offsets_; // Offsets at which the thunk needs to be written. - uint32_t max_next_offset_; // The maximum offset at which the next thunk can be placed. - uint32_t pending_offset_; // The index of the next offset to write. + const ArrayRef code_; // The code of the thunk. + const std::string debug_name_; // The debug name of the thunk. + std::vector offsets_; // Offsets at which the thunk needs to be written. + uint32_t max_next_offset_; // The maximum offset at which the next thunk can be placed. + uint32_t pending_offset_; // The index of the next offset to write. }; class ArmBaseRelativePatcher::PendingThunkComparator { @@ -239,14 +245,13 @@ std::vector ArmBaseRelativePatcher::GenerateThunkDebugIn std::vector result; result.reserve(number_of_thunks); for (auto&& entry : thunks_) { - const ThunkKey& key = entry.first; const ThunkData& data = entry.second; size_t start = data.IndexOfFirstThunkAtOrAfter(executable_offset); if (start == data.NumberOfThunks()) { continue; } // Get the base name to use for the first occurrence of the thunk. - std::string base_name = GetThunkDebugName(key); + const std::string& base_name = data.GetDebugName(); for (size_t i = start, num = data.NumberOfThunks(); i != num; ++i) { debug::MethodDebugInfo info = {}; if (i == 0u) { @@ -267,9 +272,11 @@ std::vector ArmBaseRelativePatcher::GenerateThunkDebugIn return result; } -ArmBaseRelativePatcher::ArmBaseRelativePatcher(RelativePatcherTargetProvider* provider, +ArmBaseRelativePatcher::ArmBaseRelativePatcher(RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider, InstructionSet instruction_set) - : provider_(provider), + : thunk_provider_(thunk_provider), + target_provider_(target_provider), instruction_set_(instruction_set), thunks_(), unprocessed_method_call_patches_(), @@ -398,7 +405,7 @@ void ArmBaseRelativePatcher::ProcessPatches(const CompiledMethod* compiled_metho unprocessed_method_call_patches_.emplace_back(patch_offset, patch.TargetMethod()); if (method_call_thunk_ == nullptr) { uint32_t max_next_offset = CalculateMaxNextOffset(patch_offset, key); - auto it = thunks_.Put(key, ThunkData(CompileThunk(key), max_next_offset)); + auto it = thunks_.Put(key, ThunkDataForPatch(patch, max_next_offset)); method_call_thunk_ = &it->second; AddUnreservedThunk(method_call_thunk_); } else { @@ -409,7 +416,7 @@ void ArmBaseRelativePatcher::ProcessPatches(const CompiledMethod* compiled_metho auto lb = thunks_.lower_bound(key); if (lb == thunks_.end() || thunks_.key_comp()(key, lb->first)) { uint32_t max_next_offset = CalculateMaxNextOffset(patch_offset, key); - auto it = thunks_.PutBefore(lb, key, ThunkData(CompileThunk(key), max_next_offset)); + auto it = thunks_.PutBefore(lb, key, ThunkDataForPatch(patch, max_next_offset)); AddUnreservedThunk(&it->second); } else { old_data = &lb->second; @@ -477,7 +484,7 @@ void ArmBaseRelativePatcher::ResolveMethodCalls(uint32_t quick_code_offset, break; } } else { - auto result = provider_->FindMethodOffset(target_method); + auto result = target_provider_->FindMethodOffset(target_method); if (!result.first) { break; } @@ -518,5 +525,14 @@ inline uint32_t ArmBaseRelativePatcher::CalculateMaxNextOffset(uint32_t patch_of GetInstructionSetAlignment(instruction_set_)); } +inline ArmBaseRelativePatcher::ThunkData ArmBaseRelativePatcher::ThunkDataForPatch( + const LinkerPatch& patch, uint32_t max_next_offset) { + ArrayRef code; + std::string debug_name; + thunk_provider_->GetThunkCode(patch, &code, &debug_name); + DCHECK(!code.empty()); + return ThunkData(code, debug_name, max_next_offset); +} + } // namespace linker } // namespace art diff --git a/compiler/linker/arm/relative_patcher_arm_base.h b/dex2oat/linker/arm/relative_patcher_arm_base.h similarity index 90% rename from compiler/linker/arm/relative_patcher_arm_base.h rename to dex2oat/linker/arm/relative_patcher_arm_base.h index ee09bf96b3dff5be19e8048832039df54a7199ce..f5a1395bdd92502f84216898599b30377628017f 100644 --- a/compiler/linker/arm/relative_patcher_arm_base.h +++ b/dex2oat/linker/arm/relative_patcher_arm_base.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_ -#define ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_ +#ifndef ART_DEX2OAT_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_ +#define ART_DEX2OAT_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_ #include #include @@ -37,7 +37,8 @@ class ArmBaseRelativePatcher : public RelativePatcher { std::vector GenerateThunkDebugInfo(uint32_t executable_offset) OVERRIDE; protected: - ArmBaseRelativePatcher(RelativePatcherTargetProvider* provider, + ArmBaseRelativePatcher(RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider, InstructionSet instruction_set); ~ArmBaseRelativePatcher(); @@ -94,8 +95,6 @@ class ArmBaseRelativePatcher : public RelativePatcher { uint32_t CalculateMethodCallDisplacement(uint32_t patch_offset, uint32_t target_offset); - virtual std::vector CompileThunk(const ThunkKey& key) = 0; - virtual std::string GetThunkDebugName(const ThunkKey& key) = 0; virtual uint32_t MaxPositiveDisplacement(const ThunkKey& key) = 0; virtual uint32_t MaxNegativeDisplacement(const ThunkKey& key) = 0; @@ -108,8 +107,10 @@ class ArmBaseRelativePatcher : public RelativePatcher { void ResolveMethodCalls(uint32_t quick_code_offset, MethodReference method_ref); uint32_t CalculateMaxNextOffset(uint32_t patch_offset, const ThunkKey& key); + ThunkData ThunkDataForPatch(const LinkerPatch& patch, uint32_t max_next_offset); - RelativePatcherTargetProvider* const provider_; + RelativePatcherThunkProvider* const thunk_provider_; + RelativePatcherTargetProvider* const target_provider_; const InstructionSet instruction_set_; // The data for all thunks. @@ -154,4 +155,4 @@ class ArmBaseRelativePatcher : public RelativePatcher { } // namespace linker } // namespace art -#endif // ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_ +#endif // ART_DEX2OAT_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_ diff --git a/dex2oat/linker/arm/relative_patcher_thumb2.cc b/dex2oat/linker/arm/relative_patcher_thumb2.cc new file mode 100644 index 0000000000000000000000000000000000000000..697fb09f73980673b7b9a03e8caadfcb0696e79c --- /dev/null +++ b/dex2oat/linker/arm/relative_patcher_thumb2.cc @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "linker/arm/relative_patcher_thumb2.h" + +#include + +#include "arch/arm/asm_support_arm.h" +#include "art_method.h" +#include "base/bit_utils.h" +#include "base/malloc_arena_pool.h" +#include "compiled_method.h" +#include "entrypoints/quick/quick_entrypoints_enum.h" +#include "linker/linker_patch.h" +#include "lock_word.h" +#include "mirror/array-inl.h" +#include "mirror/object.h" +#include "read_barrier.h" +#include "utils/arm/assembler_arm_vixl.h" + +namespace art { +namespace linker { + +// PC displacement from patch location; Thumb2 PC is always at instruction address + 4. +static constexpr int32_t kPcDisplacement = 4; + +// Maximum positive and negative displacement for method call measured from the patch location. +// (Signed 25 bit displacement with the last bit 0 has range [-2^24, 2^24-2] measured from +// the Thumb2 PC pointing right after the BL, i.e. 4 bytes later than the patch location.) +constexpr uint32_t kMaxMethodCallPositiveDisplacement = (1u << 24) - 2 + kPcDisplacement; +constexpr uint32_t kMaxMethodCallNegativeDisplacement = (1u << 24) - kPcDisplacement; + +// Maximum positive and negative displacement for a conditional branch measured from the patch +// location. (Signed 21 bit displacement with the last bit 0 has range [-2^20, 2^20-2] measured +// from the Thumb2 PC pointing right after the B.cond, i.e. 4 bytes later than the patch location.) +constexpr uint32_t kMaxBcondPositiveDisplacement = (1u << 20) - 2u + kPcDisplacement; +constexpr uint32_t kMaxBcondNegativeDisplacement = (1u << 20) - kPcDisplacement; + +Thumb2RelativePatcher::Thumb2RelativePatcher(RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider) + : ArmBaseRelativePatcher(thunk_provider, target_provider, InstructionSet::kThumb2) { +} + +void Thumb2RelativePatcher::PatchCall(std::vector* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) { + DCHECK_LE(literal_offset + 4u, code->size()); + DCHECK_EQ(literal_offset & 1u, 0u); + DCHECK_EQ(patch_offset & 1u, 0u); + DCHECK_EQ(target_offset & 1u, 1u); // Thumb2 mode bit. + uint32_t displacement = CalculateMethodCallDisplacement(patch_offset, target_offset & ~1u); + displacement -= kPcDisplacement; // The base PC is at the end of the 4-byte patch. + DCHECK_EQ(displacement & 1u, 0u); + DCHECK((displacement >> 24) == 0u || (displacement >> 24) == 255u); // 25-bit signed. + uint32_t signbit = (displacement >> 31) & 0x1; + uint32_t i1 = (displacement >> 23) & 0x1; + uint32_t i2 = (displacement >> 22) & 0x1; + uint32_t imm10 = (displacement >> 12) & 0x03ff; + uint32_t imm11 = (displacement >> 1) & 0x07ff; + uint32_t j1 = i1 ^ (signbit ^ 1); + uint32_t j2 = i2 ^ (signbit ^ 1); + uint32_t value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm10 << 16) | imm11; + value |= 0xf000d000; // BL + + // Check that we're just overwriting an existing BL. + DCHECK_EQ(GetInsn32(code, literal_offset) & 0xf800d000, 0xf000d000); + // Write the new BL. + SetInsn32(code, literal_offset, value); +} + +void Thumb2RelativePatcher::PatchPcRelativeReference(std::vector* code, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) { + uint32_t literal_offset = patch.LiteralOffset(); + uint32_t pc_literal_offset = patch.PcInsnOffset(); + uint32_t pc_base = patch_offset + (pc_literal_offset - literal_offset) + 4u /* PC adjustment */; + uint32_t diff = target_offset - pc_base; + + uint32_t insn = GetInsn32(code, literal_offset); + DCHECK_EQ(insn & 0xff7ff0ffu, 0xf2400000u); // MOVW/MOVT, unpatched (imm16 == 0). + uint32_t diff16 = ((insn & 0x00800000u) != 0u) ? (diff >> 16) : (diff & 0xffffu); + uint32_t imm4 = (diff16 >> 12) & 0xfu; + uint32_t imm = (diff16 >> 11) & 0x1u; + uint32_t imm3 = (diff16 >> 8) & 0x7u; + uint32_t imm8 = diff16 & 0xffu; + insn = (insn & 0xfbf08f00u) | (imm << 26) | (imm4 << 16) | (imm3 << 12) | imm8; + SetInsn32(code, literal_offset, insn); +} + +void Thumb2RelativePatcher::PatchBakerReadBarrierBranch(std::vector* code, + const LinkerPatch& patch, + uint32_t patch_offset) { + DCHECK_ALIGNED(patch_offset, 2u); + uint32_t literal_offset = patch.LiteralOffset(); + DCHECK_ALIGNED(literal_offset, 2u); + DCHECK_LT(literal_offset, code->size()); + uint32_t insn = GetInsn32(code, literal_offset); + DCHECK_EQ(insn, 0xf0408000); // BNE +0 (unpatched) + ThunkKey key = GetBakerThunkKey(patch); + uint32_t target_offset = GetThunkTargetOffset(key, patch_offset); + DCHECK_ALIGNED(target_offset, 4u); + uint32_t disp = target_offset - (patch_offset + kPcDisplacement); + DCHECK((disp >> 20) == 0u || (disp >> 20) == 0xfffu); // 21-bit signed. + insn |= ((disp << (26 - 20)) & 0x04000000u) | // Shift bit 20 to 26, "S". + ((disp >> (19 - 11)) & 0x00000800u) | // Shift bit 19 to 13, "J1". + ((disp >> (18 - 13)) & 0x00002000u) | // Shift bit 18 to 11, "J2". + ((disp << (16 - 12)) & 0x003f0000u) | // Shift bits 12-17 to 16-25, "imm6". + ((disp >> (1 - 0)) & 0x000007ffu); // Shift bits 1-12 to 0-11, "imm11". + SetInsn32(code, literal_offset, insn); +} + +uint32_t Thumb2RelativePatcher::MaxPositiveDisplacement(const ThunkKey& key) { + switch (key.GetType()) { + case ThunkType::kMethodCall: + return kMaxMethodCallPositiveDisplacement; + case ThunkType::kBakerReadBarrier: + return kMaxBcondPositiveDisplacement; + } +} + +uint32_t Thumb2RelativePatcher::MaxNegativeDisplacement(const ThunkKey& key) { + switch (key.GetType()) { + case ThunkType::kMethodCall: + return kMaxMethodCallNegativeDisplacement; + case ThunkType::kBakerReadBarrier: + return kMaxBcondNegativeDisplacement; + } +} + +void Thumb2RelativePatcher::SetInsn32(std::vector* code, uint32_t offset, uint32_t value) { + DCHECK_LE(offset + 4u, code->size()); + DCHECK_ALIGNED(offset, 2u); + uint8_t* addr = &(*code)[offset]; + addr[0] = (value >> 16) & 0xff; + addr[1] = (value >> 24) & 0xff; + addr[2] = (value >> 0) & 0xff; + addr[3] = (value >> 8) & 0xff; +} + +uint32_t Thumb2RelativePatcher::GetInsn32(ArrayRef code, uint32_t offset) { + DCHECK_LE(offset + 4u, code.size()); + DCHECK_ALIGNED(offset, 2u); + const uint8_t* addr = &code[offset]; + return + (static_cast(addr[0]) << 16) + + (static_cast(addr[1]) << 24) + + (static_cast(addr[2]) << 0)+ + (static_cast(addr[3]) << 8); +} + +template +uint32_t Thumb2RelativePatcher::GetInsn32(Vector* code, uint32_t offset) { + static_assert(std::is_same::value, "Invalid value type"); + return GetInsn32(ArrayRef(*code), offset); +} + +uint32_t Thumb2RelativePatcher::GetInsn16(ArrayRef code, uint32_t offset) { + DCHECK_LE(offset + 2u, code.size()); + DCHECK_ALIGNED(offset, 2u); + const uint8_t* addr = &code[offset]; + return (static_cast(addr[0]) << 0) + (static_cast(addr[1]) << 8); +} + +template +uint32_t Thumb2RelativePatcher::GetInsn16(Vector* code, uint32_t offset) { + static_assert(std::is_same::value, "Invalid value type"); + return GetInsn16(ArrayRef(*code), offset); +} + +} // namespace linker +} // namespace art diff --git a/dex2oat/linker/arm/relative_patcher_thumb2.h b/dex2oat/linker/arm/relative_patcher_thumb2.h new file mode 100644 index 0000000000000000000000000000000000000000..3a4292846658689151bab7f9700d1640aa28dffe --- /dev/null +++ b/dex2oat/linker/arm/relative_patcher_thumb2.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_DEX2OAT_LINKER_ARM_RELATIVE_PATCHER_THUMB2_H_ +#define ART_DEX2OAT_LINKER_ARM_RELATIVE_PATCHER_THUMB2_H_ + +#include "arch/arm/registers_arm.h" +#include "base/array_ref.h" +#include "linker/arm/relative_patcher_arm_base.h" + +namespace art { + +namespace arm { +class ArmVIXLAssembler; +} // namespace arm + +namespace linker { + +class Thumb2RelativePatcher FINAL : public ArmBaseRelativePatcher { + public: + explicit Thumb2RelativePatcher(RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider); + + void PatchCall(std::vector* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; + void PatchPcRelativeReference(std::vector* code, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; + void PatchBakerReadBarrierBranch(std::vector* code, + const LinkerPatch& patch, + uint32_t patch_offset) OVERRIDE; + + protected: + uint32_t MaxPositiveDisplacement(const ThunkKey& key) OVERRIDE; + uint32_t MaxNegativeDisplacement(const ThunkKey& key) OVERRIDE; + + private: + void SetInsn32(std::vector* code, uint32_t offset, uint32_t value); + static uint32_t GetInsn32(ArrayRef code, uint32_t offset); + + template + static uint32_t GetInsn32(Vector* code, uint32_t offset); + + static uint32_t GetInsn16(ArrayRef code, uint32_t offset); + + template + static uint32_t GetInsn16(Vector* code, uint32_t offset); + + friend class Thumb2RelativePatcherTest; + + DISALLOW_COPY_AND_ASSIGN(Thumb2RelativePatcher); +}; + +} // namespace linker +} // namespace art + +#endif // ART_DEX2OAT_LINKER_ARM_RELATIVE_PATCHER_THUMB2_H_ diff --git a/compiler/linker/arm/relative_patcher_thumb2_test.cc b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc similarity index 92% rename from compiler/linker/arm/relative_patcher_thumb2_test.cc rename to dex2oat/linker/arm/relative_patcher_thumb2_test.cc index 2c22a352c23961f939aeff768b35b9aa99e7bd9c..3d7277aab3fdc5072905b9f1f556b69e92d01135 100644 --- a/compiler/linker/arm/relative_patcher_thumb2_test.cc +++ b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc @@ -16,12 +16,15 @@ #include "linker/arm/relative_patcher_thumb2.h" +#include "arch/arm/instruction_set_features_arm.h" #include "base/casts.h" #include "linker/relative_patcher_test.h" #include "lock_word.h" #include "mirror/array-inl.h" #include "mirror/object.h" #include "oat_quick_method_header.h" +#include "optimizing/code_generator_arm_vixl.h" +#include "optimizing/optimizing_unit_test.h" namespace art { namespace linker { @@ -189,9 +192,39 @@ class Thumb2RelativePatcherTest : public RelativePatcherTest { return result.second - 1 /* thumb mode */; } + std::vector CompileThunk(const LinkerPatch& patch, + /*out*/ std::string* debug_name = nullptr) { + OptimizingUnitTestHelper helper; + HGraph* graph = helper.CreateGraph(); + std::string error_msg; + arm::CodeGeneratorARMVIXL codegen(graph, *compiler_options_); + ArenaVector code(helper.GetAllocator()->Adapter()); + codegen.EmitThunkCode(patch, &code, debug_name); + return std::vector(code.begin(), code.end()); + } + + void AddCompiledMethod( + MethodReference method_ref, + const ArrayRef& code, + const ArrayRef& patches = ArrayRef()) { + RelativePatcherTest::AddCompiledMethod(method_ref, code, patches); + + // Make sure the ThunkProvider has all the necessary thunks. + for (const LinkerPatch& patch : patches) { + if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch || + patch.GetType() == LinkerPatch::Type::kCallRelative) { + std::string debug_name; + std::vector thunk_code = CompileThunk(patch, &debug_name); + thunk_provider_.SetThunkCode(patch, ArrayRef(thunk_code), debug_name); + } + } + } + std::vector CompileMethodCallThunk() { - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetMethodCallKey(); - return static_cast(patcher_.get())->CompileThunk(key); + LinkerPatch patch = LinkerPatch::RelativeCodePatch(/* literal_offset */ 0u, + /* target_dex_file*/ nullptr, + /* target_method_idx */ 0u); + return CompileThunk(patch); } uint32_t MethodCallThunkSize() { @@ -228,27 +261,38 @@ class Thumb2RelativePatcherTest : public RelativePatcherTest { void TestStringReference(uint32_t string_offset); void CheckPcRelativePatch(const ArrayRef& patches, uint32_t target_offset); + static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, + uint32_t holder_reg, + bool narrow) { + return arm::CodeGeneratorARMVIXL::EncodeBakerReadBarrierFieldData(base_reg, holder_reg, narrow); + } + + static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { + return arm::CodeGeneratorARMVIXL::EncodeBakerReadBarrierArrayData(base_reg); + } + + static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg, bool narrow) { + return arm::CodeGeneratorARMVIXL::EncodeBakerReadBarrierGcRootData(root_reg, narrow); + } + std::vector CompileBakerOffsetThunk(uint32_t base_reg, uint32_t holder_reg, bool narrow) { const LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - 0u, Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData(base_reg, holder_reg, narrow)); - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); - return down_cast(patcher_.get())->CompileThunk(key); + /* literal_offset */ 0u, EncodeBakerReadBarrierFieldData(base_reg, holder_reg, narrow)); + return CompileThunk(patch); } std::vector CompileBakerArrayThunk(uint32_t base_reg) { LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - 0u, Thumb2RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg)); - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); - return down_cast(patcher_.get())->CompileThunk(key); + /* literal_offset */ 0u, EncodeBakerReadBarrierArrayData(base_reg)); + return CompileThunk(patch); } std::vector CompileBakerGcRootThunk(uint32_t root_reg, bool narrow) { LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - 0u, Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg, narrow)); - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); - return down_cast(patcher_.get())->CompileThunk(key); + /* literal_offset */ 0u, EncodeBakerReadBarrierGcRootData(root_reg, narrow)); + return CompileThunk(patch); } uint32_t GetOutputInsn32(uint32_t offset) { @@ -578,23 +622,28 @@ TEST_F(Thumb2RelativePatcherTest, StringReference4) { ASSERT_LT(GetMethodOffset(1u), 0xfcu); } +const uint32_t kBakerValidRegs[] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 9, 10, 11, // r8 (rMR), IP, SP, LR and PC are reserved. +}; + +const uint32_t kBakerValidRegsNarrow[] = { + 0, 1, 2, 3, 4, 5, 6, 7, +}; + void Thumb2RelativePatcherTest::TestBakerFieldWide(uint32_t offset, uint32_t ref_reg) { - uint32_t valid_regs[] = { - 0, 1, 2, 3, 5, 6, 7, // R4 is reserved for entrypoint address. - 8, 9, 10, 11, // IP, SP, LR and PC are reserved. - }; DCHECK_ALIGNED(offset, 4u); DCHECK_LT(offset, 4 * KB); constexpr size_t kMethodCodeSize = 8u; constexpr size_t kLiteralOffset = 0u; uint32_t method_idx = 0u; - for (uint32_t base_reg : valid_regs) { - for (uint32_t holder_reg : valid_regs) { + for (uint32_t base_reg : kBakerValidRegs) { + for (uint32_t holder_reg : kBakerValidRegs) { uint32_t ldr = kLdrWInsn | offset | (base_reg << 16) | (ref_reg << 12); const std::vector raw_code = RawCode({kBneWPlus0, ldr}); ASSERT_EQ(kMethodCodeSize, raw_code.size()); ArrayRef code(raw_code); - uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( + uint32_t encoded_data = EncodeBakerReadBarrierFieldData( base_reg, holder_reg, /* narrow */ false); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset, encoded_data), @@ -608,8 +657,8 @@ void Thumb2RelativePatcherTest::TestBakerFieldWide(uint32_t offset, uint32_t ref // All thunks are at the end. uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment); method_idx = 0u; - for (uint32_t base_reg : valid_regs) { - for (uint32_t holder_reg : valid_regs) { + for (uint32_t base_reg : kBakerValidRegs) { + for (uint32_t holder_reg : kBakerValidRegs) { ++method_idx; uint32_t bne = BneWWithOffset(GetMethodOffset(method_idx) + kLiteralOffset, thunk_offset); uint32_t ldr = kLdrWInsn | offset | (base_reg << 16) | (ref_reg << 12); @@ -678,25 +727,21 @@ void Thumb2RelativePatcherTest::TestBakerFieldWide(uint32_t offset, uint32_t ref } void Thumb2RelativePatcherTest::TestBakerFieldNarrow(uint32_t offset, uint32_t ref_reg) { - uint32_t valid_regs[] = { - 0, 1, 2, 3, 5, 6, 7, // R4 is reserved for entrypoint address. - 8, 9, 10, 11, // IP, SP, LR and PC are reserved. - }; DCHECK_ALIGNED(offset, 4u); DCHECK_LT(offset, 32u); constexpr size_t kMethodCodeSize = 6u; constexpr size_t kLiteralOffset = 0u; uint32_t method_idx = 0u; - for (uint32_t base_reg : valid_regs) { + for (uint32_t base_reg : kBakerValidRegs) { if (base_reg >= 8u) { continue; } - for (uint32_t holder_reg : valid_regs) { + for (uint32_t holder_reg : kBakerValidRegs) { uint32_t ldr = kLdrInsn | (offset << (6 - 2)) | (base_reg << 3) | ref_reg; const std::vector raw_code = RawCode({kBneWPlus0, ldr}); ASSERT_EQ(kMethodCodeSize, raw_code.size()); ArrayRef code(raw_code); - uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( + uint32_t encoded_data = EncodeBakerReadBarrierFieldData( base_reg, holder_reg, /* narrow */ true); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset, encoded_data), @@ -710,11 +755,11 @@ void Thumb2RelativePatcherTest::TestBakerFieldNarrow(uint32_t offset, uint32_t r // All thunks are at the end. uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment); method_idx = 0u; - for (uint32_t base_reg : valid_regs) { + for (uint32_t base_reg : kBakerValidRegs) { if (base_reg >= 8u) { continue; } - for (uint32_t holder_reg : valid_regs) { + for (uint32_t holder_reg : kBakerValidRegs) { ++method_idx; uint32_t bne = BneWWithOffset(GetMethodOffset(method_idx) + kLiteralOffset, thunk_offset); uint32_t ldr = kLdrInsn | (offset << (6 - 2)) | (base_reg << 3) | ref_reg; @@ -809,7 +854,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddle) { constexpr uint32_t kLiteralOffset1 = 6u; const std::vector raw_code1 = RawCode({kNopWInsn, kNopInsn, kBneWPlus0, kLdrWInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( + uint32_t encoded_data = EncodeBakerReadBarrierFieldData( /* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), @@ -877,7 +922,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkBeforeFiller) { constexpr uint32_t kLiteralOffset1 = 4u; const std::vector raw_code1 = RawCode({kNopWInsn, kBneWPlus0, kLdrWInsn, kNopInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( + uint32_t encoded_data = EncodeBakerReadBarrierFieldData( /* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), @@ -907,7 +952,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddleUnreachableFromLast constexpr uint32_t kLiteralOffset1 = 6u; const std::vector raw_code1 = RawCode({kNopWInsn, kNopInsn, kBneWPlus0, kLdrWInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( + uint32_t encoded_data = EncodeBakerReadBarrierFieldData( /* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), @@ -974,10 +1019,6 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddleUnreachableFromLast } TEST_F(Thumb2RelativePatcherTest, BakerArray) { - uint32_t valid_regs[] = { - 0, 1, 2, 3, 5, 6, 7, // R4 is reserved for entrypoint address. - 8, 9, 10, 11, // IP, SP, LR and PC are reserved. - }; auto ldr = [](uint32_t base_reg) { uint32_t index_reg = (base_reg == 0u) ? 1u : 0u; uint32_t ref_reg = (base_reg == 2) ? 3u : 2u; @@ -986,14 +1027,14 @@ TEST_F(Thumb2RelativePatcherTest, BakerArray) { constexpr size_t kMethodCodeSize = 8u; constexpr size_t kLiteralOffset = 0u; uint32_t method_idx = 0u; - for (uint32_t base_reg : valid_regs) { + for (uint32_t base_reg : kBakerValidRegs) { ++method_idx; const std::vector raw_code = RawCode({kBneWPlus0, ldr(base_reg)}); ASSERT_EQ(kMethodCodeSize, raw_code.size()); ArrayRef code(raw_code); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch( - kLiteralOffset, Thumb2RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg)), + kLiteralOffset, EncodeBakerReadBarrierArrayData(base_reg)), }; AddCompiledMethod(MethodRef(method_idx), code, ArrayRef(patches)); } @@ -1002,7 +1043,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerArray) { // All thunks are at the end. uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment); method_idx = 0u; - for (uint32_t base_reg : valid_regs) { + for (uint32_t base_reg : kBakerValidRegs) { ++method_idx; uint32_t bne = BneWWithOffset(GetMethodOffset(method_idx) + kLiteralOffset, thunk_offset); const std::vector expected_code = RawCode({bne, ldr(base_reg)}); @@ -1059,14 +1100,10 @@ TEST_F(Thumb2RelativePatcherTest, BakerArray) { } TEST_F(Thumb2RelativePatcherTest, BakerGcRootWide) { - uint32_t valid_regs[] = { - 0, 1, 2, 3, 5, 6, 7, // R4 is reserved for entrypoint address. - 8, 9, 10, 11, // IP, SP, LR and PC are reserved. - }; constexpr size_t kMethodCodeSize = 8u; constexpr size_t kLiteralOffset = 4u; uint32_t method_idx = 0u; - for (uint32_t root_reg : valid_regs) { + for (uint32_t root_reg : kBakerValidRegs) { ++method_idx; uint32_t ldr = kLdrWInsn | (/* offset */ 8) | (/* base_reg */ 0 << 16) | (root_reg << 12); const std::vector raw_code = RawCode({ldr, kBneWPlus0}); @@ -1074,8 +1111,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootWide) { ArrayRef code(raw_code); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch( - kLiteralOffset, - Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg, /* narrow */ false)), + kLiteralOffset, EncodeBakerReadBarrierGcRootData(root_reg, /* narrow */ false)), }; AddCompiledMethod(MethodRef(method_idx), code, ArrayRef(patches)); } @@ -1084,7 +1120,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootWide) { // All thunks are at the end. uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment); method_idx = 0u; - for (uint32_t root_reg : valid_regs) { + for (uint32_t root_reg : kBakerValidRegs) { ++method_idx; uint32_t bne = BneWWithOffset(GetMethodOffset(method_idx) + kLiteralOffset, thunk_offset); uint32_t ldr = kLdrWInsn | (/* offset */ 8) | (/* base_reg */ 0 << 16) | (root_reg << 12); @@ -1119,14 +1155,10 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootWide) { } TEST_F(Thumb2RelativePatcherTest, BakerGcRootNarrow) { - uint32_t valid_regs[] = { - 0, 1, 2, 3, 5, 6, 7, // R4 is reserved for entrypoint address. - // Not appplicable to high registers. - }; constexpr size_t kMethodCodeSize = 6u; constexpr size_t kLiteralOffset = 2u; uint32_t method_idx = 0u; - for (uint32_t root_reg : valid_regs) { + for (uint32_t root_reg : kBakerValidRegsNarrow) { ++method_idx; uint32_t ldr = kLdrInsn | (/* offset */ 8 << (6 - 2)) | (/* base_reg */ 0 << 3) | root_reg; const std::vector raw_code = RawCode({ldr, kBneWPlus0}); @@ -1134,8 +1166,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootNarrow) { ArrayRef code(raw_code); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch( - kLiteralOffset, - Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg, /* narrow */ true)), + kLiteralOffset, EncodeBakerReadBarrierGcRootData(root_reg, /* narrow */ true)), }; AddCompiledMethod(MethodRef(method_idx), code, ArrayRef(patches)); } @@ -1144,7 +1175,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootNarrow) { // All thunks are at the end. uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment); method_idx = 0u; - for (uint32_t root_reg : valid_regs) { + for (uint32_t root_reg : kBakerValidRegsNarrow) { ++method_idx; uint32_t bne = BneWWithOffset(GetMethodOffset(method_idx) + kLiteralOffset, thunk_offset); uint32_t ldr = kLdrInsn | (/* offset */ 8 << (6 - 2)) | (/* base_reg */ 0 << 3) | root_reg; @@ -1182,8 +1213,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootOffsetBits) { patches.reserve(num_patches); const uint32_t ldr = kLdrWInsn | (/* offset */ 8) | (/* base_reg */ 0 << 16) | (/* root_reg */ 0 << 12); - uint32_t encoded_data = - Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 0, /* narrow */ false); + uint32_t encoded_data = EncodeBakerReadBarrierGcRootData(/* root_reg */ 0, /* narrow */ false); for (size_t i = 0; i != num_patches; ++i) { PushBackInsn(&code, ldr); PushBackInsn(&code, kBneWPlus0); @@ -1264,10 +1294,8 @@ TEST_F(Thumb2RelativePatcherTest, BakerAndMethodCallInteraction) { ldr1, kBneWPlus0, // First GC root LDR with read barrier. ldr2, kBneWPlus0, // Second GC root LDR with read barrier. }); - uint32_t encoded_data1 = - Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 1, /* narrow */ false); - uint32_t encoded_data2 = - Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 2, /* narrow */ false); + uint32_t encoded_data1 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 1, /* narrow */ false); + uint32_t encoded_data2 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 2, /* narrow */ false); const LinkerPatch last_method_patches[] = { LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset1, encoded_data1), LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset2, encoded_data2), diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/dex2oat/linker/arm64/relative_patcher_arm64.cc similarity index 58% rename from compiler/linker/arm64/relative_patcher_arm64.cc rename to dex2oat/linker/arm64/relative_patcher_arm64.cc index 52a07965b92d4b540d164b39667424afe7808208..dd0fcfe0bec96362a2bc1939e70e29e19bd77d72 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.cc +++ b/dex2oat/linker/arm64/relative_patcher_arm64.cc @@ -20,6 +20,7 @@ #include "arch/arm64/instruction_set_features_arm64.h" #include "art_method.h" #include "base/bit_utils.h" +#include "base/malloc_arena_pool.h" #include "compiled_method-inl.h" #include "driver/compiler_driver.h" #include "entrypoints/quick/quick_entrypoints_enum.h" @@ -60,13 +61,13 @@ inline bool IsAdrpPatch(const LinkerPatch& patch) { case LinkerPatch::Type::kCallRelative: case LinkerPatch::Type::kBakerReadBarrierBranch: return false; + case LinkerPatch::Type::kIntrinsicReference: + case LinkerPatch::Type::kDataBimgRelRo: case LinkerPatch::Type::kMethodRelative: case LinkerPatch::Type::kMethodBssEntry: case LinkerPatch::Type::kTypeRelative: - case LinkerPatch::Type::kTypeClassTable: case LinkerPatch::Type::kTypeBssEntry: case LinkerPatch::Type::kStringRelative: - case LinkerPatch::Type::kStringInternTable: case LinkerPatch::Type::kStringBssEntry: return patch.LiteralOffset() == patch.PcInsnOffset(); } @@ -83,9 +84,10 @@ inline uint32_t MaxExtraSpace(size_t num_adrp, size_t code_size) { } // anonymous namespace -Arm64RelativePatcher::Arm64RelativePatcher(RelativePatcherTargetProvider* provider, +Arm64RelativePatcher::Arm64RelativePatcher(RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider, const Arm64InstructionSetFeatures* features) - : ArmBaseRelativePatcher(provider, InstructionSet::kArm64), + : ArmBaseRelativePatcher(thunk_provider, target_provider, InstructionSet::kArm64), fix_cortex_a53_843419_(features->NeedFixCortexA53_843419()), reserved_adrp_thunks_(0u), processed_adrp_thunks_(0u) { @@ -257,12 +259,14 @@ void Arm64RelativePatcher::PatchPcRelativeReference(std::vector* code, if ((insn & 0xfffffc00) == 0x91000000) { // ADD immediate, 64-bit with imm12 == 0 (unset). if (!kEmitCompilerReadBarrier) { - DCHECK(patch.GetType() == LinkerPatch::Type::kMethodRelative || + DCHECK(patch.GetType() == LinkerPatch::Type::kIntrinsicReference || + patch.GetType() == LinkerPatch::Type::kMethodRelative || patch.GetType() == LinkerPatch::Type::kTypeRelative || patch.GetType() == LinkerPatch::Type::kStringRelative) << patch.GetType(); } else { // With the read barrier (non-Baker) enabled, it could be kStringBssEntry or kTypeBssEntry. - DCHECK(patch.GetType() == LinkerPatch::Type::kMethodRelative || + DCHECK(patch.GetType() == LinkerPatch::Type::kIntrinsicReference || + patch.GetType() == LinkerPatch::Type::kMethodRelative || patch.GetType() == LinkerPatch::Type::kTypeRelative || patch.GetType() == LinkerPatch::Type::kStringRelative || patch.GetType() == LinkerPatch::Type::kTypeBssEntry || @@ -271,10 +275,9 @@ void Arm64RelativePatcher::PatchPcRelativeReference(std::vector* code, shift = 0u; // No shift for ADD. } else { // LDR/STR 32-bit or 64-bit with imm12 == 0 (unset). - DCHECK(patch.GetType() == LinkerPatch::Type::kMethodBssEntry || - patch.GetType() == LinkerPatch::Type::kTypeClassTable || + DCHECK(patch.GetType() == LinkerPatch::Type::kDataBimgRelRo || + patch.GetType() == LinkerPatch::Type::kMethodBssEntry || patch.GetType() == LinkerPatch::Type::kTypeBssEntry || - patch.GetType() == LinkerPatch::Type::kStringInternTable || patch.GetType() == LinkerPatch::Type::kStringBssEntry) << patch.GetType(); DCHECK_EQ(insn & 0xbfbffc00, 0xb9000000) << std::hex << insn; } @@ -315,44 +318,6 @@ void Arm64RelativePatcher::PatchBakerReadBarrierBranch(std::vector* cod uint32_t insn = GetInsn(code, literal_offset); DCHECK_EQ(insn & 0xffffffe0u, 0xb5000000); // CBNZ Xt, +0 (unpatched) ThunkKey key = GetBakerThunkKey(patch); - if (kIsDebugBuild) { - const uint32_t encoded_data = key.GetCustomValue1(); - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - // Check that the next instruction matches the expected LDR. - switch (kind) { - case BakerReadBarrierKind::kField: { - DCHECK_GE(code->size() - literal_offset, 8u); - uint32_t next_insn = GetInsn(code, literal_offset + 4u); - // LDR (immediate) with correct base_reg. - CheckValidReg(next_insn & 0x1fu); // Check destination register. - const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(next_insn & 0xffc003e0u, 0xb9400000u | (base_reg << 5)); - break; - } - case BakerReadBarrierKind::kArray: { - DCHECK_GE(code->size() - literal_offset, 8u); - uint32_t next_insn = GetInsn(code, literal_offset + 4u); - // LDR (register) with the correct base_reg, size=10 (32-bit), option=011 (extend = LSL), - // and S=1 (shift amount = 2 for 32-bit version), i.e. LDR Wt, [Xn, Xm, LSL #2]. - CheckValidReg(next_insn & 0x1fu); // Check destination register. - const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(next_insn & 0xffe0ffe0u, 0xb8607800u | (base_reg << 5)); - CheckValidReg((next_insn >> 16) & 0x1f); // Check index register - break; - } - case BakerReadBarrierKind::kGcRoot: { - DCHECK_GE(literal_offset, 4u); - uint32_t prev_insn = GetInsn(code, literal_offset - 4u); - // LDR (immediate) with correct root_reg. - const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(prev_insn & 0xffc0001fu, 0xb9400000u | root_reg); - break; - } - default: - LOG(FATAL) << "Unexpected kind: " << static_cast(kind); - UNREACHABLE(); - } - } uint32_t target_offset = GetThunkTargetOffset(key, patch_offset); DCHECK_ALIGNED(target_offset, 4u); uint32_t disp = target_offset - patch_offset; @@ -361,216 +326,6 @@ void Arm64RelativePatcher::PatchBakerReadBarrierBranch(std::vector* cod SetInsn(code, literal_offset, insn); } -#define __ assembler.GetVIXLAssembler()-> - -static void EmitGrayCheckAndFastPath(arm64::Arm64Assembler& assembler, - vixl::aarch64::Register base_reg, - vixl::aarch64::MemOperand& lock_word, - vixl::aarch64::Label* slow_path) { - using namespace vixl::aarch64; // NOLINT(build/namespaces) - // Load the lock word containing the rb_state. - __ Ldr(ip0.W(), lock_word); - // Given the numeric representation, it's enough to check the low bit of the rb_state. - static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0"); - static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1"); - __ Tbnz(ip0.W(), LockWord::kReadBarrierStateShift, slow_path); - static_assert( - BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET == BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET, - "Field and array LDR offsets must be the same to reuse the same code."); - // Adjust the return address back to the LDR (1 instruction; 2 for heap poisoning). - static_assert(BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4), - "Field LDR must be 1 instruction (4B) before the return address label; " - " 2 instructions (8B) for heap poisoning."); - __ Add(lr, lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET); - // Introduce a dependency on the lock_word including rb_state, - // to prevent load-load reordering, and without using - // a memory barrier (which would be more expensive). - __ Add(base_reg, base_reg, Operand(ip0, LSR, 32)); - __ Br(lr); // And return back to the function. - // Note: The fake dependency is unnecessary for the slow path. -} - -// Load the read barrier introspection entrypoint in register `entrypoint`. -static void LoadReadBarrierMarkIntrospectionEntrypoint(arm64::Arm64Assembler& assembler, - vixl::aarch64::Register entrypoint) { - using vixl::aarch64::MemOperand; - using vixl::aarch64::ip0; - // Thread Register. - const vixl::aarch64::Register tr = vixl::aarch64::x19; - - // entrypoint = Thread::Current()->pReadBarrierMarkReg16, i.e. pReadBarrierMarkIntrospection. - DCHECK_EQ(ip0.GetCode(), 16u); - const int32_t entry_point_offset = - Thread::ReadBarrierMarkEntryPointsOffset(ip0.GetCode()); - __ Ldr(entrypoint, MemOperand(tr, entry_point_offset)); -} - -void Arm64RelativePatcher::CompileBakerReadBarrierThunk(arm64::Arm64Assembler& assembler, - uint32_t encoded_data) { - using namespace vixl::aarch64; // NOLINT(build/namespaces) - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - switch (kind) { - case BakerReadBarrierKind::kField: { - // Check if the holder is gray and, if not, add fake dependency to the base register - // and return to the LDR instruction to load the reference. Otherwise, use introspection - // to load the reference and call the entrypoint (in IP1) that performs further checks - // on the reference and marks it if needed. - auto base_reg = - Register::GetXRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(base_reg.GetCode()); - auto holder_reg = - Register::GetXRegFromCode(BakerReadBarrierSecondRegField::Decode(encoded_data)); - CheckValidReg(holder_reg.GetCode()); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip0, ip1); - // If base_reg differs from holder_reg, the offset was too large and we must have - // emitted an explicit null check before the load. Otherwise, we need to null-check - // the holder as we do not necessarily do that check before going to the thunk. - vixl::aarch64::Label throw_npe; - if (holder_reg.Is(base_reg)) { - __ Cbz(holder_reg.W(), &throw_npe); - } - vixl::aarch64::Label slow_path; - MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value()); - EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path); - __ Bind(&slow_path); - MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET); - __ Ldr(ip0.W(), ldr_address); // Load the LDR (immediate) unsigned offset. - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); - __ Ubfx(ip0.W(), ip0.W(), 10, 12); // Extract the offset. - __ Ldr(ip0.W(), MemOperand(base_reg, ip0, LSL, 2)); // Load the reference. - // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference. - __ Br(ip1); // Jump to the entrypoint. - if (holder_reg.Is(base_reg)) { - // Add null check slow path. The stack map is at the address pointed to by LR. - __ Bind(&throw_npe); - int32_t offset = GetThreadOffset(kQuickThrowNullPointer).Int32Value(); - __ Ldr(ip0, MemOperand(/* Thread* */ vixl::aarch64::x19, offset)); - __ Br(ip0); - } - break; - } - case BakerReadBarrierKind::kArray: { - auto base_reg = - Register::GetXRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(base_reg.GetCode()); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip0, ip1); - vixl::aarch64::Label slow_path; - int32_t data_offset = - mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value(); - MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset); - DCHECK_LT(lock_word.GetOffset(), 0); - EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path); - __ Bind(&slow_path); - MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET); - __ Ldr(ip0.W(), ldr_address); // Load the LDR (register) unsigned offset. - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); - __ Ubfx(ip0, ip0, 16, 6); // Extract the index register, plus 32 (bit 21 is set). - __ Bfi(ip1, ip0, 3, 6); // Insert ip0 to the entrypoint address to create - // a switch case target based on the index register. - __ Mov(ip0, base_reg); // Move the base register to ip0. - __ Br(ip1); // Jump to the entrypoint's array switch case. - break; - } - case BakerReadBarrierKind::kGcRoot: { - // Check if the reference needs to be marked and if so (i.e. not null, not marked yet - // and it does not have a forwarding address), call the correct introspection entrypoint; - // otherwise return the reference (or the extracted forwarding address). - // There is no gray bit check for GC roots. - auto root_reg = - Register::GetWRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(root_reg.GetCode()); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip0, ip1); - vixl::aarch64::Label return_label, not_marked, forwarding_address; - __ Cbz(root_reg, &return_label); - MemOperand lock_word(root_reg.X(), mirror::Object::MonitorOffset().Int32Value()); - __ Ldr(ip0.W(), lock_word); - __ Tbz(ip0.W(), LockWord::kMarkBitStateShift, ¬_marked); - __ Bind(&return_label); - __ Br(lr); - __ Bind(¬_marked); - __ Tst(ip0.W(), Operand(ip0.W(), LSL, 1)); - __ B(&forwarding_address, mi); - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); - // Adjust the art_quick_read_barrier_mark_introspection address in IP1 to - // art_quick_read_barrier_mark_introspection_gc_roots. - __ Add(ip1, ip1, Operand(BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRYPOINT_OFFSET)); - __ Mov(ip0.W(), root_reg); - __ Br(ip1); - __ Bind(&forwarding_address); - __ Lsl(root_reg, ip0.W(), LockWord::kForwardingAddressShift); - __ Br(lr); - break; - } - default: - LOG(FATAL) << "Unexpected kind: " << static_cast(kind); - UNREACHABLE(); - } -} - -std::vector Arm64RelativePatcher::CompileThunk(const ThunkKey& key) { - ArenaPool pool; - ArenaAllocator allocator(&pool); - arm64::Arm64Assembler assembler(&allocator); - - switch (key.GetType()) { - case ThunkType::kMethodCall: { - // The thunk just uses the entry point in the ArtMethod. This works even for calls - // to the generic JNI and interpreter trampolines. - Offset offset(ArtMethod::EntryPointFromQuickCompiledCodeOffset( - kArm64PointerSize).Int32Value()); - assembler.JumpTo(ManagedRegister(arm64::X0), offset, ManagedRegister(arm64::IP0)); - break; - } - case ThunkType::kBakerReadBarrier: { - CompileBakerReadBarrierThunk(assembler, key.GetCustomValue1()); - break; - } - } - - // Ensure we emit the literal pool. - assembler.FinalizeCode(); - std::vector thunk_code(assembler.CodeSize()); - MemoryRegion code(thunk_code.data(), thunk_code.size()); - assembler.FinalizeInstructions(code); - return thunk_code; -} - -std::string Arm64RelativePatcher::GetThunkDebugName(const ThunkKey& key) { - switch (key.GetType()) { - case ThunkType::kMethodCall: - return "MethodCallThunk"; - - case ThunkType::kBakerReadBarrier: { - uint32_t encoded_data = key.GetCustomValue1(); - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - std::ostringstream oss; - oss << "BakerReadBarrierThunk"; - switch (kind) { - case BakerReadBarrierKind::kField: - oss << "Field_r" << BakerReadBarrierFirstRegField::Decode(encoded_data) - << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data); - break; - case BakerReadBarrierKind::kArray: - oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - break; - case BakerReadBarrierKind::kGcRoot: - oss << "GcRoot_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - break; - } - return oss.str(); - } - } -} - -#undef __ - uint32_t Arm64RelativePatcher::MaxPositiveDisplacement(const ThunkKey& key) { switch (key.GetType()) { case ThunkType::kMethodCall: diff --git a/compiler/linker/arm64/relative_patcher_arm64.h b/dex2oat/linker/arm64/relative_patcher_arm64.h similarity index 53% rename from compiler/linker/arm64/relative_patcher_arm64.h rename to dex2oat/linker/arm64/relative_patcher_arm64.h index 8ba59976e7d3447990feb1e1353a681997c80e4a..f7f673c1ba388ed40ed8634cb6dd5274ec703bc8 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.h +++ b/dex2oat/linker/arm64/relative_patcher_arm64.h @@ -14,12 +14,10 @@ * limitations under the License. */ -#ifndef ART_COMPILER_LINKER_ARM64_RELATIVE_PATCHER_ARM64_H_ -#define ART_COMPILER_LINKER_ARM64_RELATIVE_PATCHER_ARM64_H_ +#ifndef ART_DEX2OAT_LINKER_ARM64_RELATIVE_PATCHER_ARM64_H_ +#define ART_DEX2OAT_LINKER_ARM64_RELATIVE_PATCHER_ARM64_H_ #include "base/array_ref.h" -#include "base/bit_field.h" -#include "base/bit_utils.h" #include "linker/arm/relative_patcher_arm_base.h" namespace art { @@ -32,29 +30,8 @@ namespace linker { class Arm64RelativePatcher FINAL : public ArmBaseRelativePatcher { public: - static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, uint32_t holder_reg) { - CheckValidReg(base_reg); - CheckValidReg(holder_reg); - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kField) | - BakerReadBarrierFirstRegField::Encode(base_reg) | - BakerReadBarrierSecondRegField::Encode(holder_reg); - } - - static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { - CheckValidReg(base_reg); - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kArray) | - BakerReadBarrierFirstRegField::Encode(base_reg) | - BakerReadBarrierSecondRegField::Encode(kInvalidEncodedReg); - } - - static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg) { - CheckValidReg(root_reg); - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kGcRoot) | - BakerReadBarrierFirstRegField::Encode(root_reg) | - BakerReadBarrierSecondRegField::Encode(kInvalidEncodedReg); - } - - Arm64RelativePatcher(RelativePatcherTargetProvider* provider, + Arm64RelativePatcher(RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider, const Arm64InstructionSetFeatures* features); uint32_t ReserveSpace(uint32_t offset, @@ -75,37 +52,10 @@ class Arm64RelativePatcher FINAL : public ArmBaseRelativePatcher { uint32_t patch_offset) OVERRIDE; protected: - std::vector CompileThunk(const ThunkKey& key) OVERRIDE; - std::string GetThunkDebugName(const ThunkKey& key) OVERRIDE; uint32_t MaxPositiveDisplacement(const ThunkKey& key) OVERRIDE; uint32_t MaxNegativeDisplacement(const ThunkKey& key) OVERRIDE; private: - static constexpr uint32_t kInvalidEncodedReg = /* sp/zr is invalid */ 31u; - - enum class BakerReadBarrierKind : uint8_t { - kField, // Field get or array get with constant offset (i.e. constant index). - kArray, // Array get with index in register. - kGcRoot, // GC root load. - kLast = kGcRoot - }; - - static constexpr size_t kBitsForBakerReadBarrierKind = - MinimumBitsToStore(static_cast(BakerReadBarrierKind::kLast)); - static constexpr size_t kBitsForRegister = 5u; - using BakerReadBarrierKindField = - BitField; - using BakerReadBarrierFirstRegField = - BitField; - using BakerReadBarrierSecondRegField = - BitField; - - static void CheckValidReg(uint32_t reg) { - DCHECK(reg < 30u && reg != 16u && reg != 17u) << reg; - } - - void CompileBakerReadBarrierThunk(arm64::Arm64Assembler& assembler, uint32_t encoded_data); - static uint32_t PatchAdrp(uint32_t adrp, uint32_t disp); static bool NeedsErratum843419Thunk(ArrayRef code, uint32_t literal_offset, @@ -131,4 +81,4 @@ class Arm64RelativePatcher FINAL : public ArmBaseRelativePatcher { } // namespace linker } // namespace art -#endif // ART_COMPILER_LINKER_ARM64_RELATIVE_PATCHER_ARM64_H_ +#endif // ART_DEX2OAT_LINKER_ARM64_RELATIVE_PATCHER_ARM64_H_ diff --git a/compiler/linker/arm64/relative_patcher_arm64_test.cc b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc similarity index 95% rename from compiler/linker/arm64/relative_patcher_arm64_test.cc rename to dex2oat/linker/arm64/relative_patcher_arm64_test.cc index 05459a2a8237f4807437f0b4599a3b12d6a5b667..07e6860f9c8c4b976c177585611d4a32f67cd3a4 100644 --- a/compiler/linker/arm64/relative_patcher_arm64_test.cc +++ b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc @@ -16,12 +16,15 @@ #include "linker/arm64/relative_patcher_arm64.h" +#include "arch/arm64/instruction_set_features_arm64.h" #include "base/casts.h" #include "linker/relative_patcher_test.h" #include "lock_word.h" #include "mirror/array-inl.h" #include "mirror/object.h" #include "oat_quick_method_header.h" +#include "optimizing/code_generator_arm64.h" +#include "optimizing/optimizing_unit_test.h" namespace art { namespace linker { @@ -168,9 +171,39 @@ class Arm64RelativePatcherTest : public RelativePatcherTest { return result.second; } + std::vector CompileThunk(const LinkerPatch& patch, + /*out*/ std::string* debug_name = nullptr) { + OptimizingUnitTestHelper helper; + HGraph* graph = helper.CreateGraph(); + std::string error_msg; + arm64::CodeGeneratorARM64 codegen(graph, *compiler_options_); + ArenaVector code(helper.GetAllocator()->Adapter()); + codegen.EmitThunkCode(patch, &code, debug_name); + return std::vector(code.begin(), code.end()); + } + + void AddCompiledMethod( + MethodReference method_ref, + const ArrayRef& code, + const ArrayRef& patches = ArrayRef()) { + RelativePatcherTest::AddCompiledMethod(method_ref, code, patches); + + // Make sure the ThunkProvider has all the necessary thunks. + for (const LinkerPatch& patch : patches) { + if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch || + patch.GetType() == LinkerPatch::Type::kCallRelative) { + std::string debug_name; + std::vector thunk_code = CompileThunk(patch, &debug_name); + thunk_provider_.SetThunkCode(patch, ArrayRef(thunk_code), debug_name); + } + } + } + std::vector CompileMethodCallThunk() { - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetMethodCallKey(); - return down_cast(patcher_.get())->CompileThunk(key); + LinkerPatch patch = LinkerPatch::RelativeCodePatch(/* literal_offset */ 0u, + /* target_dex_file*/ nullptr, + /* target_method_idx */ 0u); + return CompileThunk(patch); } uint32_t MethodCallThunkSize() { @@ -475,25 +508,34 @@ class Arm64RelativePatcherTest : public RelativePatcherTest { TestAdrpInsn2Add(insn2, adrp_offset, has_thunk, string_offset); } + static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, uint32_t holder_reg) { + return arm64::CodeGeneratorARM64::EncodeBakerReadBarrierFieldData(base_reg, holder_reg); + } + + static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { + return arm64::CodeGeneratorARM64::EncodeBakerReadBarrierArrayData(base_reg); + } + + static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg) { + return arm64::CodeGeneratorARM64::EncodeBakerReadBarrierGcRootData(root_reg); + } + std::vector CompileBakerOffsetThunk(uint32_t base_reg, uint32_t holder_reg) { const LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - 0u, Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(base_reg, holder_reg)); - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); - return down_cast(patcher_.get())->CompileThunk(key); + /* literal_offset */ 0u, EncodeBakerReadBarrierFieldData(base_reg, holder_reg)); + return CompileThunk(patch); } std::vector CompileBakerArrayThunk(uint32_t base_reg) { LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - 0u, Arm64RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg)); - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); - return down_cast(patcher_.get())->CompileThunk(key); + /* literal_offset */ 0u, EncodeBakerReadBarrierArrayData(base_reg)); + return CompileThunk(patch); } std::vector CompileBakerGcRootThunk(uint32_t root_reg) { LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - 0u, Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg)); - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); - return down_cast(patcher_.get())->CompileThunk(key); + /* literal_offset */ 0u, EncodeBakerReadBarrierGcRootData(root_reg)); + return CompileThunk(patch); } uint32_t GetOutputInsn(uint32_t offset) { @@ -919,8 +961,7 @@ void Arm64RelativePatcherTest::TestBakerField(uint32_t offset, uint32_t ref_reg) const std::vector raw_code = RawCode({kCbnzIP1Plus0Insn, ldr}); ASSERT_EQ(kMethodCodeSize, raw_code.size()); ArrayRef code(raw_code); - uint32_t encoded_data = - Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(base_reg, holder_reg); + uint32_t encoded_data = EncodeBakerReadBarrierFieldData(base_reg, holder_reg); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset, encoded_data), }; @@ -1005,8 +1046,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddle) { constexpr uint32_t kLiteralOffset1 = 4; const std::vector raw_code1 = RawCode({kNopInsn, kCbnzIP1Plus0Insn, kLdrWInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = - Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); + uint32_t encoded_data = EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), }; @@ -1066,8 +1106,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkBeforeFiller) { constexpr uint32_t kLiteralOffset1 = 0; const std::vector raw_code1 = RawCode({kCbnzIP1Plus0Insn, kLdrWInsn, kNopInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = - Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); + uint32_t encoded_data = EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), }; @@ -1096,8 +1135,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddleUnreachableFr constexpr uint32_t kLiteralOffset1 = 4; const std::vector raw_code1 = RawCode({kNopInsn, kCbnzIP1Plus0Insn, kLdrWInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = - Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); + uint32_t encoded_data = EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), }; @@ -1170,7 +1208,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerArray) { ArrayRef code(raw_code); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch( - kLiteralOffset, Arm64RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg)), + kLiteralOffset, EncodeBakerReadBarrierArrayData(base_reg)), }; AddCompiledMethod(MethodRef(method_idx), code, ArrayRef(patches)); } @@ -1247,7 +1285,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerGcRoot) { ArrayRef code(raw_code); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch( - kLiteralOffset, Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg)), + kLiteralOffset, EncodeBakerReadBarrierGcRootData(root_reg)), }; AddCompiledMethod(MethodRef(method_idx), code, ArrayRef(patches)); } @@ -1343,8 +1381,8 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerAndMethodCallInteraction) { kNopInsn, kNopInsn, // Padding before second GC root read barrier. ldr2, kCbnzIP1Plus0Insn, // Second GC root LDR with read barrier. }); - uint32_t encoded_data1 = Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 1); - uint32_t encoded_data2 = Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 2); + uint32_t encoded_data1 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 1); + uint32_t encoded_data2 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 2); const LinkerPatch last_method_patches[] = { LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset1, encoded_data1), LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset2, encoded_data2), diff --git a/dex2oat/linker/elf_writer.h b/dex2oat/linker/elf_writer.h index bcf2cd7d4b97109e5fd93a2ae3cf60e41572b8ba..cd8cf4c54ea3f8006e9ce8b75cd10b4f99faa7f1 100644 --- a/dex2oat/linker/elf_writer.h +++ b/dex2oat/linker/elf_writer.h @@ -63,6 +63,7 @@ class ElfWriter { // This method must be called before calling GetLoadedSize(). virtual void PrepareDynamicSection(size_t rodata_size, size_t text_size, + size_t data_bimg_rel_ro_size, size_t bss_size, size_t bss_methods_offset, size_t bss_roots_offset, @@ -72,6 +73,8 @@ class ElfWriter { virtual void EndRoData(OutputStream* rodata) = 0; virtual OutputStream* StartText() = 0; virtual void EndText(OutputStream* text) = 0; + virtual OutputStream* StartDataBimgRelRo() = 0; + virtual void EndDataBimgRelRo(OutputStream* data_bimg_rel_ro) = 0; virtual void WriteDynamicSection() = 0; virtual void WriteDebugInfo(const debug::DebugInfo& debug_info) = 0; virtual bool End() = 0; diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc index 07b02f10335a904351c2e76c2ef7fcd5fbf76c1a..8f6ff702cc849bcdc51209750f3a638d40113b85 100644 --- a/dex2oat/linker/elf_writer_quick.cc +++ b/dex2oat/linker/elf_writer_quick.cc @@ -23,6 +23,7 @@ #include #include "base/casts.h" +#include "base/globals.h" #include "base/leb128.h" #include "base/utils.h" #include "compiled_method.h" @@ -31,7 +32,6 @@ #include "driver/compiler_options.h" #include "elf.h" #include "elf_utils.h" -#include "globals.h" #include "linker/buffered_output_stream.h" #include "linker/elf_builder.h" #include "linker/file_output_stream.h" @@ -96,15 +96,14 @@ class DebugInfoTask : public Task { template class ElfWriterQuick FINAL : public ElfWriter { public: - ElfWriterQuick(InstructionSet instruction_set, - const InstructionSetFeatures* features, - const CompilerOptions* compiler_options, + ElfWriterQuick(const CompilerOptions& compiler_options, File* elf_file); ~ElfWriterQuick(); void Start() OVERRIDE; void PrepareDynamicSection(size_t rodata_size, size_t text_size, + size_t data_bimg_rel_ro_size, size_t bss_size, size_t bss_methods_offset, size_t bss_roots_offset, @@ -114,6 +113,8 @@ class ElfWriterQuick FINAL : public ElfWriter { void EndRoData(OutputStream* rodata) OVERRIDE; OutputStream* StartText() OVERRIDE; void EndText(OutputStream* text) OVERRIDE; + OutputStream* StartDataBimgRelRo() OVERRIDE; + void EndDataBimgRelRo(OutputStream* data_bimg_rel_ro) OVERRIDE; void WriteDynamicSection() OVERRIDE; void WriteDebugInfo(const debug::DebugInfo& debug_info) OVERRIDE; bool End() OVERRIDE; @@ -126,11 +127,11 @@ class ElfWriterQuick FINAL : public ElfWriter { std::vector* buffer); private: - const InstructionSetFeatures* instruction_set_features_; - const CompilerOptions* const compiler_options_; + const CompilerOptions& compiler_options_; File* const elf_file_; size_t rodata_size_; size_t text_size_; + size_t data_bimg_rel_ro_size_; size_t bss_size_; size_t dex_section_size_; std::unique_ptr output_stream_; @@ -143,39 +144,30 @@ class ElfWriterQuick FINAL : public ElfWriter { DISALLOW_IMPLICIT_CONSTRUCTORS(ElfWriterQuick); }; -std::unique_ptr CreateElfWriterQuick(InstructionSet instruction_set, - const InstructionSetFeatures* features, - const CompilerOptions* compiler_options, +std::unique_ptr CreateElfWriterQuick(const CompilerOptions& compiler_options, File* elf_file) { - if (Is64BitInstructionSet(instruction_set)) { - return std::make_unique>(instruction_set, - features, - compiler_options, - elf_file); + if (Is64BitInstructionSet(compiler_options.GetInstructionSet())) { + return std::make_unique>(compiler_options, elf_file); } else { - return std::make_unique>(instruction_set, - features, - compiler_options, - elf_file); + return std::make_unique>(compiler_options, elf_file); } } template -ElfWriterQuick::ElfWriterQuick(InstructionSet instruction_set, - const InstructionSetFeatures* features, - const CompilerOptions* compiler_options, - File* elf_file) +ElfWriterQuick::ElfWriterQuick(const CompilerOptions& compiler_options, File* elf_file) : ElfWriter(), - instruction_set_features_(features), compiler_options_(compiler_options), elf_file_(elf_file), rodata_size_(0u), text_size_(0u), + data_bimg_rel_ro_size_(0u), bss_size_(0u), dex_section_size_(0u), output_stream_( std::make_unique(std::make_unique(elf_file))), - builder_(new ElfBuilder(instruction_set, features, output_stream_.get())) {} + builder_(new ElfBuilder(compiler_options_.GetInstructionSet(), + compiler_options_.GetInstructionSetFeatures(), + output_stream_.get())) {} template ElfWriterQuick::~ElfWriterQuick() {} @@ -183,7 +175,7 @@ ElfWriterQuick::~ElfWriterQuick() {} template void ElfWriterQuick::Start() { builder_->Start(); - if (compiler_options_->GetGenerateBuildId()) { + if (compiler_options_.GetGenerateBuildId()) { builder_->GetBuildId()->AllocateVirtualMemory(builder_->GetBuildId()->GetSize()); builder_->WriteBuildIdSection(); } @@ -192,6 +184,7 @@ void ElfWriterQuick::Start() { template void ElfWriterQuick::PrepareDynamicSection(size_t rodata_size, size_t text_size, + size_t data_bimg_rel_ro_size, size_t bss_size, size_t bss_methods_offset, size_t bss_roots_offset, @@ -200,6 +193,8 @@ void ElfWriterQuick::PrepareDynamicSection(size_t rodata_size, rodata_size_ = rodata_size; DCHECK_EQ(text_size_, 0u); text_size_ = text_size; + DCHECK_EQ(data_bimg_rel_ro_size_, 0u); + data_bimg_rel_ro_size_ = data_bimg_rel_ro_size; DCHECK_EQ(bss_size_, 0u); bss_size_ = bss_size; DCHECK_EQ(dex_section_size_, 0u); @@ -207,6 +202,7 @@ void ElfWriterQuick::PrepareDynamicSection(size_t rodata_size, builder_->PrepareDynamicSection(elf_file_->GetPath(), rodata_size_, text_size_, + data_bimg_rel_ro_size_, bss_size_, bss_methods_offset, bss_roots_offset, @@ -239,6 +235,19 @@ void ElfWriterQuick::EndText(OutputStream* text) { builder_->GetText()->End(); } +template +OutputStream* ElfWriterQuick::StartDataBimgRelRo() { + auto* data_bimg_rel_ro = builder_->GetDataBimgRelRo(); + data_bimg_rel_ro->Start(); + return data_bimg_rel_ro; +} + +template +void ElfWriterQuick::EndDataBimgRelRo(OutputStream* data_bimg_rel_ro) { + CHECK_EQ(builder_->GetDataBimgRelRo(), data_bimg_rel_ro); + builder_->GetDataBimgRelRo()->End(); +} + template void ElfWriterQuick::WriteDynamicSection() { if (builder_->GetIsa() == InstructionSet::kMips || @@ -250,12 +259,12 @@ void ElfWriterQuick::WriteDynamicSection() { template void ElfWriterQuick::PrepareDebugInfo(const debug::DebugInfo& debug_info) { - if (!debug_info.Empty() && compiler_options_->GetGenerateMiniDebugInfo()) { + if (!debug_info.Empty() && compiler_options_.GetGenerateMiniDebugInfo()) { // Prepare the mini-debug-info in background while we do other I/O. Thread* self = Thread::Current(); debug_info_task_ = std::unique_ptr( new DebugInfoTask(builder_->GetIsa(), - instruction_set_features_, + compiler_options_.GetInstructionSetFeatures(), builder_->GetText()->GetAddress(), text_size_, builder_->GetDex()->Exists() ? builder_->GetDex()->GetAddress() : 0, @@ -271,11 +280,11 @@ void ElfWriterQuick::PrepareDebugInfo(const debug::DebugInfo& debug_in template void ElfWriterQuick::WriteDebugInfo(const debug::DebugInfo& debug_info) { if (!debug_info.Empty()) { - if (compiler_options_->GetGenerateDebugInfo()) { + if (compiler_options_.GetGenerateDebugInfo()) { // Generate all the debug information we can. debug::WriteDebugInfo(builder_.get(), debug_info, kCFIFormat, true /* write_oat_patches */); } - if (compiler_options_->GetGenerateMiniDebugInfo()) { + if (compiler_options_.GetGenerateMiniDebugInfo()) { // Wait for the mini-debug-info generation to finish and write it to disk. Thread* self = Thread::Current(); DCHECK(debug_info_thread_pool_ != nullptr); @@ -288,7 +297,7 @@ void ElfWriterQuick::WriteDebugInfo(const debug::DebugInfo& debug_info template bool ElfWriterQuick::End() { builder_->End(); - if (compiler_options_->GetGenerateBuildId()) { + if (compiler_options_.GetGenerateBuildId()) { uint8_t build_id[ElfBuilder::kBuildIdLen]; ComputeFileBuildId(&build_id); builder_->WriteBuildId(build_id); diff --git a/dex2oat/linker/elf_writer_quick.h b/dex2oat/linker/elf_writer_quick.h index 274d18b8580ce953321eed6b1ea22b526a2398b5..333c6e3b06e5aee882755d285267f1dc628f3d55 100644 --- a/dex2oat/linker/elf_writer_quick.h +++ b/dex2oat/linker/elf_writer_quick.h @@ -30,9 +30,7 @@ class InstructionSetFeatures; namespace linker { -std::unique_ptr CreateElfWriterQuick(InstructionSet instruction_set, - const InstructionSetFeatures* features, - const CompilerOptions* compiler_options, +std::unique_ptr CreateElfWriterQuick(const CompilerOptions& compiler_options, File* elf_file); } // namespace linker diff --git a/dex2oat/linker/image_test.cc b/dex2oat/linker/image_test.cc index ab6e7a875a3dc73f3ffe0259ef0649d7d0b663b5..96c48b8798e902a87d1b02d220ea23933b5ceb61 100644 --- a/dex2oat/linker/image_test.cc +++ b/dex2oat/linker/image_test.cc @@ -111,18 +111,18 @@ TEST_F(ImageTest, TestDefaultMethods) { // Test the pointer to quick code is the same in origin method // and in the copied method form the same oat file. - mirror::Class* iface_klass = class_linker_->LookupClass( - self, "LIface;", ObjPtr()); + ObjPtr iface_klass = + class_linker_->LookupClass(self, "LIface;", /* class_loader */ nullptr); ASSERT_NE(nullptr, iface_klass); ArtMethod* origin = iface_klass->FindInterfaceMethod("defaultMethod", "()V", pointer_size); ASSERT_NE(nullptr, origin); - ASSERT_TRUE(origin->GetDeclaringClass() == iface_klass); + ASSERT_OBJ_PTR_EQ(origin->GetDeclaringClass(), iface_klass); const void* code = origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); // The origin method should have a pointer to quick code ASSERT_NE(nullptr, code); ASSERT_FALSE(class_linker_->IsQuickToInterpreterBridge(code)); - mirror::Class* impl_klass = class_linker_->LookupClass( - self, "LImpl;", ObjPtr()); + ObjPtr impl_klass = + class_linker_->LookupClass(self, "LImpl;", /* class_loader */ nullptr); ASSERT_NE(nullptr, impl_klass); ArtMethod* copied = FindCopiedMethod(origin, impl_klass); ASSERT_NE(nullptr, copied); @@ -132,20 +132,20 @@ TEST_F(ImageTest, TestDefaultMethods) { // Test the origin method has pointer to quick code // but the copied method has pointer to interpreter // because these methods are in different oat files. - mirror::Class* iterable_klass = class_linker_->LookupClass( - self, "Ljava/lang/Iterable;", ObjPtr()); + ObjPtr iterable_klass = + class_linker_->LookupClass(self, "Ljava/lang/Iterable;", /* class_loader */ nullptr); ASSERT_NE(nullptr, iterable_klass); origin = iterable_klass->FindClassMethod( "forEach", "(Ljava/util/function/Consumer;)V", pointer_size); ASSERT_NE(nullptr, origin); ASSERT_FALSE(origin->IsDirect()); - ASSERT_TRUE(origin->GetDeclaringClass() == iterable_klass); + ASSERT_OBJ_PTR_EQ(origin->GetDeclaringClass(), iterable_klass); code = origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); // the origin method should have a pointer to quick code ASSERT_NE(nullptr, code); ASSERT_FALSE(class_linker_->IsQuickToInterpreterBridge(code)); - mirror::Class* iterablebase_klass = class_linker_->LookupClass( - self, "LIterableBase;", ObjPtr()); + ObjPtr iterablebase_klass = + class_linker_->LookupClass(self, "LIterableBase;", /* class_loader */ nullptr); ASSERT_NE(nullptr, iterablebase_klass); copied = FindCopiedMethod(origin, iterablebase_klass); ASSERT_NE(nullptr, copied); diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h index 319c5fb67589925b9e3358aeb757d98c96a8a502..fa8c7784f556ec9c4e53624bd1b89256f722d0b2 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -27,6 +27,7 @@ #include "art_method-inl.h" #include "base/file_utils.h" +#include "base/hash_set.h" #include "base/unix_file/fd_file.h" #include "base/utils.h" #include "class_linker-inl.h" @@ -34,6 +35,7 @@ #include "compiler_callbacks.h" #include "debug/method_debug_info.h" #include "dex/quick_compiler_callbacks.h" +#include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "gc/space/image_space.h" #include "image_writer.h" @@ -62,9 +64,6 @@ struct CompilationHelper { std::vector vdex_files; std::string image_dir; - void Compile(CompilerDriver* driver, - ImageHeader::StorageMode storage_mode); - std::vector GetImageObjectSectionSizes(); ~CompilationHelper(); @@ -80,7 +79,7 @@ class ImageTest : public CommonCompilerTest { void TestWriteRead(ImageHeader::StorageMode storage_mode); void Compile(ImageHeader::StorageMode storage_mode, - CompilationHelper& out_helper, + /*out*/ CompilationHelper& out_helper, const std::string& extra_dex = "", const std::initializer_list& image_classes = {}); @@ -93,11 +92,11 @@ class ImageTest : public CommonCompilerTest { options->push_back(std::make_pair("compilercallbacks", callbacks_.get())); } - std::unordered_set* GetImageClasses() OVERRIDE { - return new std::unordered_set(image_classes_); + std::unique_ptr> GetImageClasses() OVERRIDE { + return std::make_unique>(image_classes_); } - ArtMethod* FindCopiedMethod(ArtMethod* origin, mirror::Class* klass) + ArtMethod* FindCopiedMethod(ArtMethod* origin, ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_) { PointerSize pointer_size = class_linker_->GetImagePointerSize(); for (ArtMethod& m : klass->GetCopiedMethods(pointer_size)) { @@ -110,7 +109,9 @@ class ImageTest : public CommonCompilerTest { } private: - std::unordered_set image_classes_; + void DoCompile(ImageHeader::StorageMode storage_mode, /*out*/ CompilationHelper& out_helper); + + HashSet image_classes_; }; inline CompilationHelper::~CompilationHelper() { @@ -140,12 +141,13 @@ inline std::vector CompilationHelper::GetImageObjectSectionSizes() { return ret; } -inline void CompilationHelper::Compile(CompilerDriver* driver, - ImageHeader::StorageMode storage_mode) { +inline void ImageTest::DoCompile(ImageHeader::StorageMode storage_mode, + /*out*/ CompilationHelper& out_helper) { + CompilerDriver* driver = compiler_driver_.get(); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); std::vector class_path = class_linker->GetBootClassPath(); - for (const std::unique_ptr& dex_file : extra_dex_files) { + for (const std::unique_ptr& dex_file : out_helper.extra_dex_files) { { ScopedObjectAccess soa(Thread::Current()); // Inject in boot class path so that the compiler driver can see it. @@ -156,7 +158,7 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, // Enable write for dex2dex. for (const DexFile* dex_file : class_path) { - dex_file_locations.push_back(dex_file->GetLocation()); + out_helper.dex_file_locations.push_back(dex_file->GetLocation()); if (dex_file->IsReadOnly()) { dex_file->EnableWrite(); } @@ -167,31 +169,31 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, for (int i = 0; i < static_cast(class_path.size()); ++i) { std::string cur_location = android::base::StringPrintf("%s-%d.art", location.GetFilename().c_str(), i); - image_locations.push_back(ScratchFile(cur_location)); + out_helper.image_locations.push_back(ScratchFile(cur_location)); } } std::vector image_filenames; - for (ScratchFile& file : image_locations) { + for (ScratchFile& file : out_helper.image_locations) { std::string image_filename(GetSystemImageFilename(file.GetFilename().c_str(), kRuntimeISA)); image_filenames.push_back(image_filename); size_t pos = image_filename.rfind('/'); CHECK_NE(pos, std::string::npos) << image_filename; - if (image_dir.empty()) { - image_dir = image_filename.substr(0, pos); - int mkdir_result = mkdir(image_dir.c_str(), 0700); - CHECK_EQ(0, mkdir_result) << image_dir; + if (out_helper.image_dir.empty()) { + out_helper.image_dir = image_filename.substr(0, pos); + int mkdir_result = mkdir(out_helper.image_dir.c_str(), 0700); + CHECK_EQ(0, mkdir_result) << out_helper.image_dir; } - image_files.push_back(ScratchFile(OS::CreateEmptyFile(image_filename.c_str()))); + out_helper.image_files.push_back(ScratchFile(OS::CreateEmptyFile(image_filename.c_str()))); } std::vector oat_filenames; std::vector vdex_filenames; for (const std::string& image_filename : image_filenames) { std::string oat_filename = ReplaceFileExtension(image_filename, "oat"); - oat_files.push_back(ScratchFile(OS::CreateEmptyFile(oat_filename.c_str()))); + out_helper.oat_files.push_back(ScratchFile(OS::CreateEmptyFile(oat_filename.c_str()))); oat_filenames.push_back(oat_filename); std::string vdex_filename = ReplaceFileExtension(image_filename, "vdex"); - vdex_files.push_back(ScratchFile(OS::CreateEmptyFile(vdex_filename.c_str()))); + out_helper.vdex_files.push_back(ScratchFile(OS::CreateEmptyFile(vdex_filename.c_str()))); vdex_filenames.push_back(vdex_filename); } @@ -210,7 +212,7 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, ++image_idx; } // TODO: compile_pic should be a test argument. - std::unique_ptr writer(new ImageWriter(*driver, + std::unique_ptr writer(new ImageWriter(*compiler_options_, kRequestedImageBase, /*compile_pic*/false, /*compile_app_image*/false, @@ -223,7 +225,7 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, jobject class_loader = nullptr; TimingLogger timings("ImageTest::WriteRead", false, false); TimingLogger::ScopedTiming t("CompileAll", &timings); - driver->SetDexFilesForOatFile(class_path); + SetDexFilesForOatFile(class_path); driver->CompileAll(class_loader, class_path, &timings); t.NewTiming("WriteElf"); @@ -240,13 +242,10 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, std::vector> elf_writers; std::vector> oat_writers; - for (ScratchFile& oat_file : oat_files) { - elf_writers.emplace_back(CreateElfWriterQuick(driver->GetInstructionSet(), - driver->GetInstructionSetFeatures(), - &driver->GetCompilerOptions(), - oat_file.GetFile())); + for (ScratchFile& oat_file : out_helper.oat_files) { + elf_writers.emplace_back(CreateElfWriterQuick(*compiler_options_, oat_file.GetFile())); elf_writers.back()->Start(); - oat_writers.emplace_back(new OatWriter(/*compiling_boot_image*/true, + oat_writers.emplace_back(new OatWriter(*compiler_options_, &timings, /*profile_compilation_info*/nullptr, CompactDexLevel::kCompactDexLevelNone)); @@ -269,10 +268,8 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, std::vector> cur_opened_dex_files_maps; std::vector> cur_opened_dex_files; bool dex_files_ok = oat_writers[i]->WriteAndOpenDexFiles( - vdex_files[i].GetFile(), + out_helper.vdex_files[i].GetFile(), rodata.back(), - driver->GetInstructionSet(), - driver->GetInstructionSetFeatures(), &key_value_store, /* verify */ false, // Dex files may be dex-to-dex-ed, don't verify. /* update_input_vdex */ false, @@ -293,13 +290,14 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, ASSERT_TRUE(cur_opened_dex_files.empty()); } } - bool image_space_ok = writer->PrepareImageAddressSpace(); + bool image_space_ok = writer->PrepareImageAddressSpace(&timings); ASSERT_TRUE(image_space_ok); - DCHECK_EQ(vdex_files.size(), oat_files.size()); - for (size_t i = 0, size = oat_files.size(); i != size; ++i) { - MultiOatRelativePatcher patcher(driver->GetInstructionSet(), - driver->GetInstructionSetFeatures()); + DCHECK_EQ(out_helper.vdex_files.size(), out_helper.oat_files.size()); + for (size_t i = 0, size = out_helper.oat_files.size(); i != size; ++i) { + MultiOatRelativePatcher patcher(compiler_options_->GetInstructionSet(), + compiler_options_->GetInstructionSetFeatures(), + driver->GetCompiledMethodStorage()); OatWriter* const oat_writer = oat_writers[i].get(); ElfWriter* const elf_writer = elf_writers[i].get(); std::vector cur_dex_files(1u, class_path[i]); @@ -307,16 +305,15 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, std::unique_ptr vdex_out = std::make_unique( - std::make_unique(vdex_files[i].GetFile())); + std::make_unique(out_helper.vdex_files[i].GetFile())); oat_writer->WriteVerifierDeps(vdex_out.get(), nullptr); oat_writer->WriteQuickeningInfo(vdex_out.get()); oat_writer->WriteChecksumsAndVdexHeader(vdex_out.get()); oat_writer->PrepareLayout(&patcher); - size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset(); - size_t text_size = oat_writer->GetOatSize() - rodata_size; - elf_writer->PrepareDynamicSection(rodata_size, - text_size, + elf_writer->PrepareDynamicSection(oat_writer->GetOatHeader().GetExecutableOffset(), + oat_writer->GetCodeSize(), + oat_writer->GetDataBimgRelRoSize(), oat_writer->GetBssSize(), oat_writer->GetBssMethodsOffset(), oat_writer->GetBssRootsOffset(), @@ -336,6 +333,13 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, ASSERT_TRUE(text_ok); elf_writer->EndText(text); + if (oat_writer->GetDataBimgRelRoSize() != 0u) { + OutputStream* data_bimg_rel_ro = elf_writer->StartDataBimgRelRo(); + bool data_bimg_rel_ro_ok = oat_writer->WriteDataBimgRelRo(data_bimg_rel_ro); + ASSERT_TRUE(data_bimg_rel_ro_ok); + elf_writer->EndDataBimgRelRo(data_bimg_rel_ro); + } + bool header_ok = oat_writer->WriteHeader(elf_writer->GetStream(), 0u, 0u, 0u); ASSERT_TRUE(header_ok); @@ -358,8 +362,7 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, const char* oat_filename = oat_filenames[i].c_str(); std::unique_ptr oat_file(OS::OpenFileReadWrite(oat_filename)); ASSERT_TRUE(oat_file != nullptr); - bool success_fixup = ElfWriter::Fixup(oat_file.get(), - writer->GetOatDataBegin(i)); + bool success_fixup = ElfWriter::Fixup(oat_file.get(), writer->GetOatDataBegin(i)); ASSERT_TRUE(success_fixup); ASSERT_EQ(oat_file->FlushCloseOrErase(), 0) << "Could not flush and close oat file " << oat_filename; @@ -374,20 +377,22 @@ inline void ImageTest::Compile(ImageHeader::StorageMode storage_mode, for (const std::string& image_class : image_classes) { image_classes_.insert(image_class); } - CreateCompilerDriver(Compiler::kOptimizing, kRuntimeISA, kIsTargetBuild ? 2U : 16U); + number_of_threads_ = kIsTargetBuild ? 2U : 16U; + CreateCompilerDriver(); // Set inline filter values. compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits); image_classes_.clear(); if (!extra_dex.empty()) { helper.extra_dex_files = OpenTestDexFiles(extra_dex.c_str()); } - helper.Compile(compiler_driver_.get(), storage_mode); + DoCompile(storage_mode, helper); if (image_classes.begin() != image_classes.end()) { // Make sure the class got initialized. ScopedObjectAccess soa(Thread::Current()); ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); for (const std::string& image_class : image_classes) { - mirror::Class* klass = class_linker->FindSystemClass(Thread::Current(), image_class.c_str()); + ObjPtr klass = + class_linker->FindSystemClass(Thread::Current(), image_class.c_str()); EXPECT_TRUE(klass != nullptr); EXPECT_TRUE(klass->IsInitialized()); } @@ -417,9 +422,6 @@ inline void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { image_file_sizes.push_back(file->GetLength()); } - ASSERT_TRUE(compiler_driver_->GetImageClasses() != nullptr); - std::unordered_set image_classes(*compiler_driver_->GetImageClasses()); - // Need to delete the compiler since it has worker threads which are attached to runtime. compiler_driver_.reset(); @@ -460,6 +462,7 @@ inline void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { // We loaded the runtime with an explicit image, so it must exist. ASSERT_EQ(heap->GetBootImageSpaces().size(), image_file_sizes.size()); + const HashSet& image_classes = compiler_options_->GetImageClasses(); for (size_t i = 0; i < helper.dex_file_locations.size(); ++i) { std::unique_ptr dex( LoadExpectSingleDexFile(helper.dex_file_locations[i].c_str())); @@ -485,15 +488,15 @@ inline void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { for (size_t j = 0; j < dex->NumClassDefs(); ++j) { const DexFile::ClassDef& class_def = dex->GetClassDef(j); const char* descriptor = dex->GetClassDescriptor(class_def); - mirror::Class* klass = class_linker_->FindSystemClass(soa.Self(), descriptor); + ObjPtr klass = class_linker_->FindSystemClass(soa.Self(), descriptor); EXPECT_TRUE(klass != nullptr) << descriptor; - if (image_classes.find(descriptor) == image_classes.end()) { - EXPECT_TRUE(reinterpret_cast(klass) >= image_end || - reinterpret_cast(klass) < image_begin) << descriptor; + uint8_t* raw_klass = reinterpret_cast(klass.Ptr()); + if (image_classes.find(StringPiece(descriptor)) == image_classes.end()) { + EXPECT_TRUE(raw_klass >= image_end || raw_klass < image_begin) << descriptor; } else { // Image classes should be located inside the image. - EXPECT_LT(image_begin, reinterpret_cast(klass)) << descriptor; - EXPECT_LT(reinterpret_cast(klass), image_end) << descriptor; + EXPECT_LT(image_begin, raw_klass) << descriptor; + EXPECT_LT(raw_klass, image_end) << descriptor; } EXPECT_TRUE(Monitor::IsValidLockWord(klass->GetLockWord(false))); } diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index 5d99aef1c9f23ca2f70c0b229b639a09863915e2..de9c3d831d6a82be83673f7c54df8ab706f9c7ad 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -29,13 +29,15 @@ #include "art_method-inl.h" #include "base/callee_save_type.h" #include "base/enums.h" +#include "base/globals.h" #include "base/logging.h" // For VLOG. #include "base/unix_file/fd_file.h" #include "class_linker-inl.h" +#include "class_root.h" #include "compiled_method.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_types.h" -#include "driver/compiler_driver.h" +#include "driver/compiler_options.h" #include "elf_file.h" #include "elf_utils.h" #include "gc/accounting/card_table-inl.h" @@ -47,12 +49,10 @@ #include "gc/space/large_object_space.h" #include "gc/space/space-inl.h" #include "gc/verification.h" -#include "globals.h" #include "handle_scope-inl.h" #include "image.h" #include "imt_conflict_table.h" -#include "subtype_check.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "linear_alloc.h" #include "lock_word.h" #include "mirror/array-inl.h" @@ -70,8 +70,10 @@ #include "oat.h" #include "oat_file.h" #include "oat_file_manager.h" +#include "optimizing/intrinsic_objects.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" +#include "subtype_check.h" #include "utils/dex_cache_arrays_layout-inl.h" #include "well_known_classes.h" @@ -133,23 +135,31 @@ static void ClearDexFileCookies() REQUIRES_SHARED(Locks::mutator_lock_) { Runtime::Current()->GetHeap()->VisitObjects(visitor); } -bool ImageWriter::PrepareImageAddressSpace() { - target_ptr_size_ = InstructionSetPointerSize(compiler_driver_.GetInstructionSet()); +bool ImageWriter::PrepareImageAddressSpace(TimingLogger* timings) { + target_ptr_size_ = InstructionSetPointerSize(compiler_options_.GetInstructionSet()); gc::Heap* const heap = Runtime::Current()->GetHeap(); { ScopedObjectAccess soa(Thread::Current()); - PruneNonImageClasses(); // Remove junk + { + TimingLogger::ScopedTiming t("PruneNonImageClasses", timings); + PruneNonImageClasses(); // Remove junk + } if (compile_app_image_) { + TimingLogger::ScopedTiming t("ClearDexFileCookies", timings); // Clear dex file cookies for app images to enable app image determinism. This is required // since the cookie field contains long pointers to DexFiles which are not deterministic. // b/34090128 ClearDexFileCookies(); } else { + TimingLogger::ScopedTiming t("ComputeLazyFieldsForImageClasses", timings); // Avoid for app image since this may increase RAM and image size. ComputeLazyFieldsForImageClasses(); // Add useful information } } - heap->CollectGarbage(/* clear_soft_references */ false); // Remove garbage. + { + TimingLogger::ScopedTiming t("CollectGarbage", timings); + heap->CollectGarbage(/* clear_soft_references */ false); // Remove garbage. + } if (kIsDebugBuild) { ScopedObjectAccess soa(Thread::Current()); @@ -157,12 +167,14 @@ bool ImageWriter::PrepareImageAddressSpace() { } { + TimingLogger::ScopedTiming t("CalculateNewObjectOffsets", timings); ScopedObjectAccess soa(Thread::Current()); CalculateNewObjectOffsets(); } // This needs to happen after CalculateNewObjectOffsets since it relies on intern_table_bytes_ and // bin size sums being calculated. + TimingLogger::ScopedTiming t("AllocMemory", timings); if (!AllocMemory()) { return false; } @@ -238,7 +250,6 @@ bool ImageWriter::Write(int image_fd, CHECK_EQ(image_header->storage_mode_, image_storage_mode_); switch (image_storage_mode_) { - case ImageHeader::kStorageModeLZ4HC: // Fall-through. case ImageHeader::kStorageModeLZ4: { const size_t compressed_max_size = LZ4_compressBound(image_data_size); compressed_data.reset(new char[compressed_max_size]); @@ -247,22 +258,20 @@ bool ImageWriter::Write(int image_fd, &compressed_data[0], image_data_size, compressed_max_size); - break; } - /* - * Disabled due to image_test64 flakyness. Both use same decompression. b/27560444 case ImageHeader::kStorageModeLZ4HC: { // Bound is same as non HC. const size_t compressed_max_size = LZ4_compressBound(image_data_size); compressed_data.reset(new char[compressed_max_size]); - data_size = LZ4_compressHC( + data_size = LZ4_compress_HC( reinterpret_cast(image_info.image_->Begin()) + sizeof(ImageHeader), &compressed_data[0], - image_data_size); + image_data_size, + compressed_max_size, + LZ4HC_CLEVEL_MAX); break; } - */ case ImageHeader::kStorageModeUncompressed: { data_size = image_data_size; image_data_to_write = image_data; @@ -429,10 +438,10 @@ void ImageWriter::SetImageBinSlot(mirror::Object* object, BinSlot bin_slot) { } void ImageWriter::PrepareDexCacheArraySlots() { - // Prepare dex cache array starts based on the ordering specified in the CompilerDriver. + // Prepare dex cache array starts based on the ordering specified in the CompilerOptions. // Set the slot size early to avoid DCHECK() failures in IsImageBinSlotAssigned() // when AssignImageBinSlot() assigns their indexes out or order. - for (const DexFile* dex_file : compiler_driver_.GetDexFilesForOatFile()) { + for (const DexFile* dex_file : compiler_options_.GetDexFilesForOatFile()) { auto it = dex_file_oat_index_map_.find(dex_file); DCHECK(it != dex_file_oat_index_map_.end()) << dex_file->GetLocation(); ImageInfo& image_info = GetImageInfo(it->second); @@ -611,8 +620,7 @@ void ImageWriter::AssignImageBinSlot(mirror::Object* object, size_t oat_index) { } } else if (object->GetClass()->IsStringClass()) { bin = Bin::kString; // Strings are almost always immutable (except for object header). - } else if (object->GetClass() == - Runtime::Current()->GetClassLinker()->GetClassRoot(ClassLinker::kJavaLangObject)) { + } else if (object->GetClass() == GetClassRoot()) { // Instance of java lang object, probably a lock object. This means it will be dirty when we // synchronize on it. bin = Bin::kMiscDirty; @@ -692,7 +700,7 @@ bool ImageWriter::AllocMemory() { for (ImageInfo& image_info : image_infos_) { ImageSection unused_sections[ImageHeader::kSectionCount]; const size_t length = RoundUp( - image_info.CreateImageSections(unused_sections, compile_app_image_), kPageSize); + image_info.CreateImageSections(unused_sections), kPageSize); std::string error_msg; image_info.image_.reset(MemMap::MapAnonymous("image writer image", @@ -771,8 +779,11 @@ class ImageWriter::PruneObjectReferenceVisitor { return; } + ObjPtr> class_roots = + Runtime::Current()->GetClassLinker()->GetClassRoots(); ObjPtr klass = ref->IsClass() ? ref->AsClass() : ref->GetClass(); - if (klass == mirror::Method::StaticClass() || klass == mirror::Constructor::StaticClass()) { + if (klass == GetClassRoot(class_roots) || + klass == GetClassRoot(class_roots)) { // Prune all classes using reflection because the content they held will not be fixup. *result_ = true; } @@ -840,7 +851,7 @@ bool ImageWriter::PruneAppImageClassInternal( std::string temp; // Prune if not an image class, this handles any broken sets of image classes such as having a // class in the set but not it's superclass. - result = result || !compiler_driver_.IsImageClass(klass->GetDescriptor(&temp)); + result = result || !compiler_options_.IsImageClass(klass->GetDescriptor(&temp)); bool my_early_exit = false; // Only for ourselves, ignore caller. // Remove classes that failed to verify since we don't want to have java.lang.VerifyError in the // app image. @@ -930,7 +941,7 @@ bool ImageWriter::KeepClass(ObjPtr klass) { return true; } std::string temp; - if (!compiler_driver_.IsImageClass(klass->GetDescriptor(&temp))) { + if (!compiler_options_.IsImageClass(klass->GetDescriptor(&temp))) { return false; } if (compile_app_image_) { @@ -1201,27 +1212,22 @@ void ImageWriter::PruneNonImageClasses() { } void ImageWriter::CheckNonImageClassesRemoved() { - if (compiler_driver_.GetImageClasses() != nullptr) { - auto visitor = [&](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { - if (obj->IsClass() && !IsInBootImage(obj)) { - Class* klass = obj->AsClass(); - if (!KeepClass(klass)) { - DumpImageClasses(); - std::string temp; - CHECK(KeepClass(klass)) - << Runtime::Current()->GetHeap()->GetVerification()->FirstPathFromRootSet(klass); - } + auto visitor = [&](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { + if (obj->IsClass() && !IsInBootImage(obj)) { + Class* klass = obj->AsClass(); + if (!KeepClass(klass)) { + DumpImageClasses(); + CHECK(KeepClass(klass)) + << Runtime::Current()->GetHeap()->GetVerification()->FirstPathFromRootSet(klass); } - }; - gc::Heap* heap = Runtime::Current()->GetHeap(); - heap->VisitObjects(visitor); - } + } + }; + gc::Heap* heap = Runtime::Current()->GetHeap(); + heap->VisitObjects(visitor); } void ImageWriter::DumpImageClasses() { - auto image_classes = compiler_driver_.GetImageClasses(); - CHECK(image_classes != nullptr); - for (const std::string& image_class : *image_classes) { + for (const std::string& image_class : compiler_options_.GetImageClasses()) { LOG(INFO) << " " << image_class; } } @@ -1251,15 +1257,8 @@ mirror::String* ImageWriter::FindInternedString(mirror::String* string) { return nullptr; } - -ObjectArray* 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 object_array_class(hs.NewHandle( - class_linker->FindSystemClass(self, "[Ljava/lang/Object;"))); - +ObjPtr> ImageWriter::CollectDexCaches(Thread* self, + size_t oat_index) const { std::unordered_set image_dex_files; for (auto& pair : dex_file_oat_index_map_) { const DexFile* image_dex_file = pair.first; @@ -1274,6 +1273,7 @@ ObjectArray* ImageWriter::CreateImageRoots(size_t oat_index) const { // 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_); @@ -1290,8 +1290,8 @@ ObjectArray* ImageWriter::CreateImageRoots(size_t oat_index) const { } } } - Handle> dex_caches( - hs.NewHandle(ObjectArray::Alloc(self, object_array_class.Get(), dex_cache_count))); + ObjPtr> dex_caches = ObjectArray::Alloc( + self, GetClassRoot>(class_linker), dex_cache_count); CHECK(dex_caches != nullptr) << "Failed to allocate a dex cache array."; { ReaderMutexLock mu(self, *Locks::dex_lock_); @@ -1325,17 +1325,44 @@ ObjectArray* ImageWriter::CreateImageRoots(size_t oat_index) const { } } } + return dex_caches; +} + +ObjPtr> ImageWriter::CreateImageRoots( + size_t oat_index, + Handle> boot_image_live_objects) const { + Runtime* runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + Thread* self = Thread::Current(); + StackHandleScope<2> hs(self); + + Handle> 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::Alloc(self, object_array_class.Get(), image_roots_size))); + Handle> image_roots(hs.NewHandle(ObjectArray::Alloc( + self, GetClassRoot>(class_linker), image_roots_size))); image_roots->Set(ImageHeader::kDexCaches, dex_caches.Get()); image_roots->Set(ImageHeader::kClassRoots, class_linker->GetClassRoots()); - // 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) { + image_roots->Set(ImageHeader::kOomeWhenThrowingException, + runtime->GetPreAllocatedOutOfMemoryErrorWhenThrowingException()); + image_roots->Set(ImageHeader::kOomeWhenThrowingOome, + runtime->GetPreAllocatedOutOfMemoryErrorWhenThrowingOOME()); + image_roots->Set(ImageHeader::kOomeWhenHandlingStackOverflow, + runtime->GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow()); + image_roots->Set(ImageHeader::kNoClassDefFoundError, + runtime->GetPreAllocatedNoClassDefFoundError()); + if (!compile_app_image_) { + DCHECK(boot_image_live_objects != nullptr); + image_roots->Set(ImageHeader::kBootImageLiveObjects, boot_image_live_objects.Get()); + } else { + DCHECK(boot_image_live_objects == nullptr); + } + 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(); @@ -1658,13 +1685,17 @@ void ImageWriter::ProcessWorkStack(WorkStack* work_stack) { void ImageWriter::CalculateNewObjectOffsets() { Thread* const self = Thread::Current(); + Runtime* const runtime = Runtime::Current(); VariableSizedHandleScope handles(self); + MutableHandle> boot_image_live_objects = handles.NewHandle( + compile_app_image_ + ? nullptr + : IntrinsicObjects::AllocateBootImageLiveObjects(self, runtime->GetClassLinker())); std::vector>> image_roots; for (size_t i = 0, size = oat_filenames_.size(); i != size; ++i) { - image_roots.push_back(handles.NewHandle(CreateImageRoots(i))); + image_roots.push_back(handles.NewHandle(CreateImageRoots(i, boot_image_live_objects))); } - Runtime* const runtime = Runtime::Current(); gc::Heap* const heap = runtime->GetHeap(); // Leave space for the header, but do not write it yet, we need to @@ -1707,12 +1738,15 @@ void ImageWriter::CalculateNewObjectOffsets() { heap->VisitObjects(deflate_monitor); } + // From this point on, there shall be no GC anymore and no objects shall be allocated. + // We can now assign a BitSlot to each object and store it in its lockword. + // Work list of for objects. Everything on the stack must already be // assigned a bin slot. WorkStack work_stack; // Special case interned strings to put them in the image they are likely to be resolved from. - for (const DexFile* dex_file : compiler_driver_.GetDexFilesForOatFile()) { + for (const DexFile* dex_file : compiler_options_.GetDexFilesForOatFile()) { auto it = dex_file_oat_index_map_.find(dex_file); DCHECK(it != dex_file_oat_index_map_.end()) << dex_file->GetLocation(); const size_t oat_index = it->second; @@ -1763,7 +1797,7 @@ void ImageWriter::CalculateNewObjectOffsets() { CHECK_EQ(class_loaders_.size(), 1u); CHECK_EQ(image_roots.size(), 1u); CHECK(*class_loaders_.begin() != nullptr); - image_roots[0]->Set(ImageHeader::kClassLoader, *class_loaders_.begin()); + image_roots[0]->Set(ImageHeader::kAppImageClassLoader, *class_loaders_.begin()); } // Verify that all objects have assigned image bin slots. @@ -1834,7 +1868,7 @@ void ImageWriter::CalculateNewObjectOffsets() { image_info.image_offset_ = image_offset; ImageSection unused_sections[ImageHeader::kSectionCount]; image_info.image_size_ = - RoundUp(image_info.CreateImageSections(unused_sections, compile_app_image_), kPageSize); + RoundUp(image_info.CreateImageSections(unused_sections), kPageSize); // There should be no gaps until the next image. image_offset += image_info.image_size_; } @@ -1863,10 +1897,12 @@ void ImageWriter::CalculateNewObjectOffsets() { ImageInfo& image_info = GetImageInfo(relocation.oat_index); relocation.offset += image_info.GetBinSlotOffset(bin_type); } + + // Remember the boot image live objects as raw pointer. No GC can happen anymore. + boot_image_live_objects_ = boot_image_live_objects.Get(); } -size_t ImageWriter::ImageInfo::CreateImageSections(ImageSection* out_sections, - bool app_image) const { +size_t ImageWriter::ImageInfo::CreateImageSections(ImageSection* out_sections) const { DCHECK(out_sections != nullptr); // Do not round up any sections here that are represented by the bins since it will break @@ -1904,13 +1940,8 @@ size_t ImageWriter::ImageInfo::CreateImageSections(ImageSection* out_sections, ImageSection* dex_cache_arrays_section = &out_sections[ImageHeader::kSectionDexCacheArrays]; *dex_cache_arrays_section = ImageSection(GetBinSlotOffset(Bin::kDexCacheArray), GetBinSlotSize(Bin::kDexCacheArray)); - // For boot image, round up to the page boundary to separate the interned strings and - // class table from the modifiable data. We shall mprotect() these pages read-only when - // we load the boot image. This is more than sufficient for the string table alignment, - // namely sizeof(uint64_t). See HashSet::WriteToMemory. - static_assert(IsAligned(kPageSize), "String table alignment check."); - size_t cur_pos = - RoundUp(dex_cache_arrays_section->End(), app_image ? sizeof(uint64_t) : kPageSize); + // Round up to the alignment the string table expects. See HashSet::WriteToMemory. + size_t cur_pos = RoundUp(dex_cache_arrays_section->End(), sizeof(uint64_t)); // Calculate the size of the interned strings. ImageSection* interned_strings_section = &out_sections[ImageHeader::kSectionInternedStrings]; *interned_strings_section = ImageSection(cur_pos, intern_table_bytes_); @@ -1933,7 +1964,7 @@ void ImageWriter::CreateHeader(size_t oat_index) { // Create the image sections. ImageSection sections[ImageHeader::kSectionCount]; - const size_t image_end = image_info.CreateImageSections(sections, compile_app_image_); + const size_t image_end = image_info.CreateImageSections(sections); // Finally bitmap section. const size_t bitmap_bytes = image_info.image_bitmap_->Size(); @@ -1995,6 +2026,28 @@ ArtMethod* ImageWriter::GetImageMethodAddress(ArtMethod* method) { return reinterpret_cast(image_info.image_begin_ + it->second.offset); } +const void* ImageWriter::GetIntrinsicReferenceAddress(uint32_t intrinsic_data) { + DCHECK(!compile_app_image_); + switch (IntrinsicObjects::DecodePatchType(intrinsic_data)) { + case IntrinsicObjects::PatchType::kIntegerValueOfArray: { + const uint8_t* base_address = + reinterpret_cast(GetImageAddress(boot_image_live_objects_)); + MemberOffset data_offset = + IntrinsicObjects::GetIntegerValueOfArrayDataOffset(boot_image_live_objects_); + return base_address + data_offset.Uint32Value(); + } + case IntrinsicObjects::PatchType::kIntegerValueOfObject: { + uint32_t index = IntrinsicObjects::DecodePatchIndex(intrinsic_data); + ObjPtr value = + IntrinsicObjects::GetIntegerValueOfObject(boot_image_live_objects_, index); + return GetImageAddress(value.Ptr()); + } + } + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + + class ImageWriter::FixupRootVisitor : public RootVisitor { public: explicit FixupRootVisitor(ImageWriter* image_writer) : image_writer_(image_writer) { @@ -2082,7 +2135,8 @@ void ImageWriter::CopyAndFixupNativeData(size_t oat_index) { size_t size = ArtMethod::Size(target_ptr_size_); size_t alignment = ArtMethod::Alignment(target_ptr_size_); memcpy(dest, pair.first, LengthPrefixedArray::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*>(dest)->ClearPadding(size, alignment); break; } @@ -2163,12 +2217,7 @@ void ImageWriter::CopyAndFixupObjects() { CopyAndFixupObject(obj); }; Runtime::Current()->GetHeap()->VisitObjects(visitor); - // Fix up the object previously had hash codes. - for (const auto& hash_pair : saved_hashcode_map_) { - Object* obj = hash_pair.first; - DCHECK_EQ(obj->GetLockWord(false).ReadBarrierState(), 0U); - obj->SetLockWord(LockWord::FromHashCode(hash_pair.second, 0U), false); - } + // We no longer need the hashcode map, values have already been copied to target objects. saved_hashcode_map_.clear(); } @@ -2401,15 +2450,17 @@ void ImageWriter::FixupObject(Object* orig, Object* copy) { if (orig->IsClass()) { FixupClass(orig->AsClass(), down_cast(copy)); } else { - if (klass == mirror::Method::StaticClass() || klass == mirror::Constructor::StaticClass()) { + ObjPtr> class_roots = + Runtime::Current()->GetClassLinker()->GetClassRoots(); + if (klass == GetClassRoot(class_roots) || + klass == GetClassRoot(class_roots)) { // Need to go update the ArtMethod. auto* dest = down_cast(copy); auto* src = down_cast(orig); ArtMethod* src_method = src->GetArtMethod(); dest->SetArtMethod(GetImageMethodAddress(src_method)); } else if (!klass->IsArrayClass()) { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - if (klass == class_linker->GetClassRoot(ClassLinker::kJavaLangDexCache)) { + if (klass == GetClassRoot()) { FixupDexCache(down_cast(orig), down_cast(copy)); } else if (klass->IsClassLoaderClass()) { mirror::ClassLoader* copy_loader = down_cast(copy); @@ -2796,23 +2847,24 @@ void ImageWriter::UpdateOatFileHeader(size_t oat_index, const OatHeader& oat_hea } ImageWriter::ImageWriter( - const CompilerDriver& compiler_driver, + const CompilerOptions& compiler_options, uintptr_t image_begin, bool compile_pic, bool compile_app_image, ImageHeader::StorageMode image_storage_mode, const std::vector& oat_filenames, const std::unordered_map& dex_file_oat_index_map, - const std::unordered_set* dirty_image_objects) - : compiler_driver_(compiler_driver), + const HashSet* dirty_image_objects) + : compiler_options_(compiler_options), global_image_begin_(reinterpret_cast(image_begin)), image_objects_offset_begin_(0), compile_pic_(compile_pic), compile_app_image_(compile_app_image), - target_ptr_size_(InstructionSetPointerSize(compiler_driver_.GetInstructionSet())), + target_ptr_size_(InstructionSetPointerSize(compiler_options.GetInstructionSet())), image_infos_(oat_filenames.size()), dirty_methods_(0u), clean_methods_(0u), + boot_image_live_objects_(nullptr), image_storage_mode_(image_storage_mode), oat_filenames_(oat_filenames), dex_file_oat_index_map_(dex_file_oat_index_map), diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h index 36bbb47786751ca7e94ca701ccb5eaf13e8076ae..9097cc90c6dd3f0f4540a56d7e90be4ead913568 100644 --- a/dex2oat/linker/image_writer.h +++ b/dex2oat/linker/image_writer.h @@ -31,17 +31,17 @@ #include "base/bit_utils.h" #include "base/dchecked_vector.h" #include "base/enums.h" +#include "base/hash_set.h" #include "base/length_prefixed_array.h" #include "base/macros.h" +#include "base/mem_map.h" #include "base/os.h" #include "base/safe_map.h" #include "base/utils.h" #include "class_table.h" -#include "driver/compiler_driver.h" #include "image.h" #include "intern_table.h" #include "lock_word.h" -#include "mem_map.h" #include "mirror/dex_cache.h" #include "oat_file.h" #include "obj_ptr.h" @@ -62,8 +62,11 @@ class ClassLoader; } // namespace mirror class ClassLoaderVisitor; +class CompilerOptions; +template class Handle; class ImTable; class ImtConflictTable; +class TimingLogger; static constexpr int kInvalidFd = -1; @@ -72,16 +75,16 @@ namespace linker { // Write a Space built during compilation for use during execution. class ImageWriter FINAL { public: - ImageWriter(const CompilerDriver& compiler_driver, + ImageWriter(const CompilerOptions& compiler_options, uintptr_t image_begin, bool compile_pic, bool compile_app_image, ImageHeader::StorageMode image_storage_mode, const std::vector& oat_filenames, const std::unordered_map& dex_file_oat_index_map, - const std::unordered_set* dirty_image_objects); + const HashSet* dirty_image_objects); - bool PrepareImageAddressSpace(); + bool PrepareImageAddressSpace(TimingLogger* timings); bool IsImageAddressSpaceReady() const { DCHECK(!image_infos_.empty()); @@ -110,6 +113,8 @@ class ImageWriter FINAL { } ArtMethod* GetImageMethodAddress(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); + const void* GetIntrinsicReferenceAddress(uint32_t intrinsic_data) + REQUIRES_SHARED(Locks::mutator_lock_); size_t GetOatFileOffset(size_t oat_index) const { return GetImageInfo(oat_index).oat_offset_; @@ -267,7 +272,7 @@ class ImageWriter FINAL { // Create the image sections into the out sections variable, returns the size of the image // excluding the bitmap. - size_t CreateImageSections(ImageSection* out_sections, bool app_image) const; + size_t CreateImageSections(ImageSection* out_sections) const; size_t GetStubOffset(StubType stub_type) const { DCHECK_LT(static_cast(stub_type), kNumberOfStubTypes); @@ -449,7 +454,11 @@ class ImageWriter FINAL { REQUIRES_SHARED(Locks::mutator_lock_); void CreateHeader(size_t oat_index) REQUIRES_SHARED(Locks::mutator_lock_); - mirror::ObjectArray* CreateImageRoots(size_t oat_index) const + ObjPtr> CollectDexCaches(Thread* self, size_t oat_index) const + REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr> CreateImageRoots( + size_t oat_index, + Handle> boot_image_live_objects) const REQUIRES_SHARED(Locks::mutator_lock_); void CalculateObjectBinSlots(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_); @@ -507,9 +516,8 @@ class ImageWriter FINAL { // classes since we do not want any boot class loader classes in the image. This means that // we also cannot have any classes which refer to these boot class loader non image classes. // PruneAppImageClass also prunes if klass depends on a non-image class according to the compiler - // driver. - bool PruneAppImageClass(ObjPtr klass) - REQUIRES_SHARED(Locks::mutator_lock_); + // options. + bool PruneAppImageClass(ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_); // early_exit is true if we had a cyclic dependency anywhere down the chain. bool PruneAppImageClassInternal(ObjPtr klass, @@ -571,7 +579,7 @@ class ImageWriter FINAL { void CopyAndFixupPointer(void** target, void* value); - const CompilerDriver& compiler_driver_; + const CompilerOptions& compiler_options_; // Beginning target image address for the first image. uint8_t* global_image_begin_; @@ -631,6 +639,9 @@ class ImageWriter FINAL { // null is a valid entry. std::unordered_set class_loaders_; + // Boot image live objects, null for app image. + mirror::ObjectArray* boot_image_live_objects_; + // Which mode the image is stored as, see image.h const ImageHeader::StorageMode image_storage_mode_; @@ -641,7 +652,7 @@ class ImageWriter FINAL { const std::unordered_map& dex_file_oat_index_map_; // Set of objects known to be dirty in the image. Can be nullptr if there are none. - const std::unordered_set* dirty_image_objects_; + const HashSet* dirty_image_objects_; class ComputeLazyFieldsForClassesVisitor; class FixupClassVisitor; diff --git a/compiler/linker/mips/relative_patcher_mips.cc b/dex2oat/linker/mips/relative_patcher_mips.cc similarity index 100% rename from compiler/linker/mips/relative_patcher_mips.cc rename to dex2oat/linker/mips/relative_patcher_mips.cc diff --git a/compiler/linker/mips/relative_patcher_mips.h b/dex2oat/linker/mips/relative_patcher_mips.h similarity index 92% rename from compiler/linker/mips/relative_patcher_mips.h rename to dex2oat/linker/mips/relative_patcher_mips.h index 5714a7d1b0c20a8586e967253a81595ede86a97e..d3a4c5a14f2797d420e02afa0b5396f74c1b1b87 100644 --- a/compiler/linker/mips/relative_patcher_mips.h +++ b/dex2oat/linker/mips/relative_patcher_mips.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_LINKER_MIPS_RELATIVE_PATCHER_MIPS_H_ -#define ART_COMPILER_LINKER_MIPS_RELATIVE_PATCHER_MIPS_H_ +#ifndef ART_DEX2OAT_LINKER_MIPS_RELATIVE_PATCHER_MIPS_H_ +#define ART_DEX2OAT_LINKER_MIPS_RELATIVE_PATCHER_MIPS_H_ #include "arch/mips/instruction_set_features_mips.h" #include "linker/relative_patcher.h" @@ -55,4 +55,4 @@ class MipsRelativePatcher FINAL : public RelativePatcher { } // namespace linker } // namespace art -#endif // ART_COMPILER_LINKER_MIPS_RELATIVE_PATCHER_MIPS_H_ +#endif // ART_DEX2OAT_LINKER_MIPS_RELATIVE_PATCHER_MIPS_H_ diff --git a/compiler/linker/mips/relative_patcher_mips32r6_test.cc b/dex2oat/linker/mips/relative_patcher_mips32r6_test.cc similarity index 100% rename from compiler/linker/mips/relative_patcher_mips32r6_test.cc rename to dex2oat/linker/mips/relative_patcher_mips32r6_test.cc diff --git a/compiler/linker/mips/relative_patcher_mips_test.cc b/dex2oat/linker/mips/relative_patcher_mips_test.cc similarity index 100% rename from compiler/linker/mips/relative_patcher_mips_test.cc rename to dex2oat/linker/mips/relative_patcher_mips_test.cc diff --git a/compiler/linker/mips64/relative_patcher_mips64.cc b/dex2oat/linker/mips64/relative_patcher_mips64.cc similarity index 100% rename from compiler/linker/mips64/relative_patcher_mips64.cc rename to dex2oat/linker/mips64/relative_patcher_mips64.cc diff --git a/compiler/linker/mips64/relative_patcher_mips64.h b/dex2oat/linker/mips64/relative_patcher_mips64.h similarity index 90% rename from compiler/linker/mips64/relative_patcher_mips64.h rename to dex2oat/linker/mips64/relative_patcher_mips64.h index 183bbedb396bebc4aec55b9b9c6a67c399589fdd..9f5a1254080459ba42df6fc46fe86c5d6bd190f5 100644 --- a/compiler/linker/mips64/relative_patcher_mips64.h +++ b/dex2oat/linker/mips64/relative_patcher_mips64.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_ -#define ART_COMPILER_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_ +#ifndef ART_DEX2OAT_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_ +#define ART_DEX2OAT_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_ #include "linker/relative_patcher.h" @@ -51,4 +51,4 @@ class Mips64RelativePatcher FINAL : public RelativePatcher { } // namespace linker } // namespace art -#endif // ART_COMPILER_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_ +#endif // ART_DEX2OAT_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_ diff --git a/compiler/linker/mips64/relative_patcher_mips64_test.cc b/dex2oat/linker/mips64/relative_patcher_mips64_test.cc similarity index 100% rename from compiler/linker/mips64/relative_patcher_mips64_test.cc rename to dex2oat/linker/mips64/relative_patcher_mips64_test.cc diff --git a/dex2oat/linker/multi_oat_relative_patcher.cc b/dex2oat/linker/multi_oat_relative_patcher.cc index 1abaf7dfd1831dbf0d5bbe9e478561248a04bf77..a6797ffb8a5b68b195c7c6844a5044fa05f2fb27 100644 --- a/dex2oat/linker/multi_oat_relative_patcher.cc +++ b/dex2oat/linker/multi_oat_relative_patcher.cc @@ -19,15 +19,29 @@ #include #include "base/bit_utils.h" -#include "globals.h" +#include "base/globals.h" +#include "driver/compiled_method_storage.h" namespace art { namespace linker { +void MultiOatRelativePatcher::ThunkProvider::GetThunkCode(const LinkerPatch& patch, + /*out*/ ArrayRef* code, + /*out*/ std::string* debug_name) { + *code = storage_->GetThunkCode(patch, debug_name); + DCHECK(!code->empty()); +} + + MultiOatRelativePatcher::MultiOatRelativePatcher(InstructionSet instruction_set, - const InstructionSetFeatures* features) - : method_offset_map_(), - relative_patcher_(RelativePatcher::Create(instruction_set, features, &method_offset_map_)), + const InstructionSetFeatures* features, + CompiledMethodStorage* storage) + : thunk_provider_(storage), + method_offset_map_(), + relative_patcher_(RelativePatcher::Create(instruction_set, + features, + &thunk_provider_, + &method_offset_map_)), adjustment_(0u), instruction_set_(instruction_set), start_size_code_alignment_(0u), diff --git a/dex2oat/linker/multi_oat_relative_patcher.h b/dex2oat/linker/multi_oat_relative_patcher.h index bd33b9531879423e34d6fbb3c7f601d52162fb93..60fcfe8b58134ba364ed5c454b46672bfc7a5427 100644 --- a/dex2oat/linker/multi_oat_relative_patcher.h +++ b/dex2oat/linker/multi_oat_relative_patcher.h @@ -26,6 +26,7 @@ namespace art { class CompiledMethod; +class CompiledMethodStorage; class InstructionSetFeatures; namespace linker { @@ -38,7 +39,9 @@ class MultiOatRelativePatcher FINAL { public: using const_iterator = SafeMap::const_iterator; - MultiOatRelativePatcher(InstructionSet instruction_set, const InstructionSetFeatures* features); + MultiOatRelativePatcher(InstructionSet instruction_set, + const InstructionSetFeatures* features, + CompiledMethodStorage* storage); // Mark the start of a new oat file (for statistics retrieval) and set the // adjustment for a new oat file to apply to all relative offsets that are @@ -129,6 +132,19 @@ class MultiOatRelativePatcher FINAL { uint32_t MiscThunksSize() const; private: + class ThunkProvider : public RelativePatcherThunkProvider { + public: + explicit ThunkProvider(CompiledMethodStorage* storage) + : storage_(storage) {} + + void GetThunkCode(const LinkerPatch& patch, + /*out*/ ArrayRef* code, + /*out*/ std::string* debug_name) OVERRIDE; + + private: + CompiledMethodStorage* storage_; + }; + // Map method reference to assigned offset. // Wrap the map in a class implementing RelativePatcherTargetProvider. class MethodOffsetMap : public RelativePatcherTargetProvider { @@ -137,6 +153,7 @@ class MultiOatRelativePatcher FINAL { SafeMap map; }; + ThunkProvider thunk_provider_; MethodOffsetMap method_offset_map_; std::unique_ptr relative_patcher_; uint32_t adjustment_; diff --git a/dex2oat/linker/multi_oat_relative_patcher_test.cc b/dex2oat/linker/multi_oat_relative_patcher_test.cc index ca9c5f1e8472a267acb90374f408ca14d89402ae..05fe36a5904feec2e93037250bc5250ea1375fa1 100644 --- a/dex2oat/linker/multi_oat_relative_patcher_test.cc +++ b/dex2oat/linker/multi_oat_relative_patcher_test.cc @@ -122,7 +122,7 @@ class MultiOatRelativePatcherTest : public testing::Test { MultiOatRelativePatcherTest() : instruction_set_features_(InstructionSetFeatures::FromCppDefines()), - patcher_(kRuntimeISA, instruction_set_features_.get()) { + patcher_(kRuntimeISA, instruction_set_features_.get(), /* storage */ nullptr) { std::unique_ptr mock(new MockPatcher()); mock_ = mock.get(); patcher_.relative_patcher_ = std::move(mock); diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index c635d59cd7087487802ace40e6ac412b341498ef..09a0d376e0eab69336f9c3331ace74e2b0d3e8b2 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -31,6 +31,7 @@ #include "base/safe_map.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" +#include "base/zip_archive.h" #include "class_linker.h" #include "class_table-inl.h" #include "compiled_method-inl.h" @@ -40,6 +41,7 @@ #include "dex/dex_file_loader.h" #include "dex/dex_file_types.h" #include "dex/standard_dex_file.h" +#include "dex/type_lookup_table.h" #include "dex/verification_results.h" #include "dex_container.h" #include "dexlayout.h" @@ -49,7 +51,6 @@ #include "gc/space/space.h" #include "handle_scope-inl.h" #include "image_writer.h" -#include "jit/profile_compilation_info.h" #include "linker/buffered_output_stream.h" #include "linker/file_output_stream.h" #include "linker/index_bss_mapping_encoder.h" @@ -61,13 +62,12 @@ #include "mirror/dex_cache-inl.h" #include "mirror/object-inl.h" #include "oat_quick_method_header.h" +#include "profile/profile_compilation_info.h" #include "quicken_info.h" #include "scoped_thread_state_change-inl.h" -#include "type_lookup_table.h" #include "utils/dex_cache_arrays_layout-inl.h" #include "vdex_file.h" #include "verifier/verifier_deps.h" -#include "zip_archive.h" namespace art { namespace linker { @@ -355,7 +355,7 @@ class OatWriter::OatDexFile { DCHECK_EQ(static_cast(file_offset + offset_), out->Seek(0, kSeekCurrent)) \ << "file_offset=" << file_offset << " offset_=" << offset_ -OatWriter::OatWriter(bool compiling_boot_image, +OatWriter::OatWriter(const CompilerOptions& compiler_options, TimingLogger* timings, ProfileCompilationInfo* info, CompactDexLevel compact_dex_level) @@ -366,8 +366,8 @@ OatWriter::OatWriter(bool compiling_boot_image, zipped_dex_files_(), zipped_dex_file_locations_(), compiler_driver_(nullptr), + compiler_options_(compiler_options), image_writer_(nullptr), - compiling_boot_image_(compiling_boot_image), extract_dex_files_into_vdex_(true), dex_files_(nullptr), vdex_size_(0u), @@ -375,16 +375,19 @@ OatWriter::OatWriter(bool compiling_boot_image, vdex_dex_shared_data_offset_(0u), vdex_verifier_deps_offset_(0u), vdex_quickening_info_offset_(0u), + code_size_(0u), oat_size_(0u), + data_bimg_rel_ro_start_(0u), + data_bimg_rel_ro_size_(0u), bss_start_(0u), bss_size_(0u), bss_methods_offset_(0u), bss_roots_offset_(0u), + data_bimg_rel_ro_entries_(), bss_method_entry_references_(), bss_method_entries_(), bss_type_entries_(), bss_string_entries_(), - map_boot_image_tables_to_bss_(false), oat_data_offset_(0u), oat_header_(nullptr), size_vdex_header_(0), @@ -409,6 +412,8 @@ OatWriter::OatWriter(bool compiling_boot_image, size_method_header_(0), size_code_(0), size_code_alignment_(0), + size_data_bimg_rel_ro_(0), + size_data_bimg_rel_ro_alignment_(0), size_relative_call_thunks_(0), size_misc_thunks_(0), size_vmap_table_(0), @@ -637,15 +642,12 @@ dchecked_vector OatWriter::GetSourceLocations() const { } bool OatWriter::MayHaveCompiledMethods() const { - return CompilerFilter::IsAnyCompilationEnabled( - GetCompilerDriver()->GetCompilerOptions().GetCompilerFilter()); + return GetCompilerOptions().IsAnyCompilationEnabled(); } bool OatWriter::WriteAndOpenDexFiles( File* vdex_file, OutputStream* oat_rodata, - InstructionSet instruction_set, - const InstructionSetFeatures* instruction_set_features, SafeMap* key_value_store, bool verify, bool update_input_vdex, @@ -667,9 +669,7 @@ bool OatWriter::WriteAndOpenDexFiles( // Reserve space for Vdex header and checksums. vdex_size_ = sizeof(VdexFile::VerifierDepsHeader) + oat_dex_files_.size() * sizeof(VdexFile::VdexChecksum); - oat_size_ = InitOatHeader(instruction_set, - instruction_set_features, - dchecked_integral_cast(oat_dex_files_.size()), + oat_size_ = InitOatHeader(dchecked_integral_cast(oat_dex_files_.size()), key_value_store); ChecksumUpdatingOutputStream checksum_updating_rodata(oat_rodata, oat_header_.get()); @@ -698,16 +698,25 @@ bool OatWriter::WriteAndOpenDexFiles( return true; } +// Initialize the writer with the given parameters. +void OatWriter::Initialize(const CompilerDriver* compiler_driver, + ImageWriter* image_writer, + const std::vector& dex_files) { + compiler_driver_ = compiler_driver; + image_writer_ = image_writer; + dex_files_ = &dex_files; +} + void OatWriter::PrepareLayout(MultiOatRelativePatcher* relative_patcher) { CHECK(write_state_ == WriteState::kPrepareLayout); relative_patcher_ = relative_patcher; SetMultiOatRelativePatcherAdjustment(); - if (compiling_boot_image_) { + if (GetCompilerOptions().IsBootImage()) { CHECK(image_writer_ != nullptr); } - InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); + InstructionSet instruction_set = compiler_options_.GetInstructionSet(); CHECK_EQ(instruction_set, oat_header_->GetInstructionSet()); { @@ -744,12 +753,17 @@ void OatWriter::PrepareLayout(MultiOatRelativePatcher* relative_patcher) { { TimingLogger::ScopedTiming split("InitOatCodeDexFiles", timings_); offset = InitOatCodeDexFiles(offset); + code_size_ = offset - GetOatHeader().GetExecutableOffset(); + } + { + TimingLogger::ScopedTiming split("InitDataBimgRelRoLayout", timings_); + offset = InitDataBimgRelRoLayout(offset); } - oat_size_ = offset; + oat_size_ = offset; // .bss does not count towards oat_size_. bss_start_ = (bss_size_ != 0u) ? RoundUp(oat_size_, kPageSize) : 0u; CHECK_EQ(dex_files_->size(), oat_dex_files_.size()); - if (compiling_boot_image_) { + if (GetCompilerOptions().IsBootImage()) { CHECK_EQ(image_writer_ != nullptr, oat_header_->GetStoreValueByKey(OatHeader::kImageLocationKey) == nullptr); } @@ -852,7 +866,10 @@ class OatWriter::InitBssLayoutMethodVisitor : public DexMethodVisitor { MethodReference(dex_file_, it.GetMemberIndex())); if (HasCompiledCode(compiled_method)) { for (const LinkerPatch& patch : compiled_method->GetPatches()) { - if (patch.GetType() == LinkerPatch::Type::kMethodBssEntry) { + if (patch.GetType() == LinkerPatch::Type::kDataBimgRelRo) { + writer_->data_bimg_rel_ro_entries_.Overwrite(patch.BootImageOffset(), + /* placeholder */ 0u); + } else if (patch.GetType() == LinkerPatch::Type::kMethodBssEntry) { MethodReference target_method = patch.TargetMethod(); AddBssReference(target_method, target_method.dex_file->NumMethodIds(), @@ -870,9 +887,6 @@ class OatWriter::InitBssLayoutMethodVisitor : public DexMethodVisitor { target_string.dex_file->NumStringIds(), &writer_->bss_string_entry_references_); writer_->bss_string_entries_.Overwrite(target_string, /* placeholder */ 0u); - } else if (patch.GetType() == LinkerPatch::Type::kStringInternTable || - patch.GetType() == LinkerPatch::Type::kTypeClassTable) { - writer_->map_boot_image_tables_to_bss_ = true; } } } else { @@ -1147,7 +1161,7 @@ class OatWriter::LayoutCodeMethodVisitor : public OatDexMethodVisitor { size_t debug_info_idx = OrderedMethodData::kDebugInfoIdxInvalid; { - const CompilerOptions& compiler_options = writer_->compiler_driver_->GetCompilerOptions(); + const CompilerOptions& compiler_options = writer_->GetCompilerOptions(); ArrayRef quick_code = compiled_method->GetQuickCode(); uint32_t code_size = quick_code.size() * sizeof(uint8_t); @@ -1228,7 +1242,7 @@ class OatWriter::LayoutReserveOffsetCodeMethodVisitor : public OrderedMethodVisi OrderedMethodList ordered_methods) : LayoutReserveOffsetCodeMethodVisitor(writer, offset, - writer->GetCompilerDriver()->GetCompilerOptions(), + writer->GetCompilerOptions(), std::move(ordered_methods)) { } @@ -1531,7 +1545,7 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { size_t offset, const std::vector* dex_files) : OatDexMethodVisitor(writer, offset), - pointer_size_(GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet())), + pointer_size_(GetInstructionSetPointerSize(writer_->compiler_options_.GetInstructionSet())), class_loader_(writer->HasImage() ? writer->image_writer_->GetClassLoader() : nullptr), dex_files_(dex_files), class_linker_(Runtime::Current()->GetClassLinker()) {} @@ -1599,7 +1613,7 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { Thread* self = Thread::Current(); ObjPtr dex_cache = class_linker_->FindDexCache(self, *dex_file_); ArtMethod* method; - if (writer_->HasBootImage()) { + if (writer_->GetCompilerOptions().IsBootImage()) { const InvokeType invoke_type = it.GetMethodInvokeType( dex_file_->GetClassDef(class_def_index_)); // Unchecked as we hold mutator_lock_ on entry. @@ -1641,7 +1655,7 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { const DexFile::TypeId& type_id = dex_file_->GetTypeId(dex_file_->GetClassDef(class_def_index_).class_idx_); const char* class_descriptor = dex_file_->GetTypeDescriptor(type_id); - return writer_->GetCompilerDriver()->IsImageClass(class_descriptor); + return writer_->GetCompilerOptions().IsImageClass(class_descriptor); } // Check whether specified dex file is in the compiled oat file. @@ -1682,7 +1696,7 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { writer_(writer), offset_(relative_offset), dex_file_(nullptr), - pointer_size_(GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet())), + pointer_size_(GetInstructionSetPointerSize(writer_->compiler_options_.GetInstructionSet())), class_loader_(writer->HasImage() ? writer->image_writer_->GetClassLoader() : nullptr), out_(out), file_offset_(file_offset), @@ -1690,7 +1704,7 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { dex_cache_(nullptr), no_thread_suspension_("OatWriter patching") { patched_code_.reserve(16 * KB); - if (writer_->HasBootImage()) { + if (writer_->GetCompilerOptions().IsBootImage()) { // If we're creating the image, the address space must be ready so that we can apply patches. CHECK(writer_->image_writer_->IsImageAddressSpaceReady()); } @@ -1707,7 +1721,7 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { // Ordered method visiting is only for compiled methods. DCHECK(writer_->MayHaveCompiledMethods()); - if (writer_->GetCompilerDriver()->GetCompilerOptions().IsAotCompilationEnabled()) { + if (writer_->GetCompilerOptions().IsAotCompilationEnabled()) { // Only need to set the dex cache if we have compilation. Other modes might have unloaded it. if (dex_cache_ == nullptr || dex_cache_->GetDexFile() != dex_file) { dex_cache_ = class_linker_->FindDexCache(Thread::Current(), *dex_file); @@ -1783,6 +1797,24 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { for (const LinkerPatch& patch : compiled_method->GetPatches()) { uint32_t literal_offset = patch.LiteralOffset(); switch (patch.GetType()) { + case LinkerPatch::Type::kIntrinsicReference: { + uint32_t target_offset = GetTargetIntrinsicReferenceOffset(patch); + writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_, + patch, + offset_ + literal_offset, + target_offset); + break; + } + case LinkerPatch::Type::kDataBimgRelRo: { + uint32_t target_offset = + writer_->data_bimg_rel_ro_start_ + + writer_->data_bimg_rel_ro_entries_.Get(patch.BootImageOffset()); + writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_, + patch, + offset_ + literal_offset, + target_offset); + break; + } case LinkerPatch::Type::kMethodBssEntry: { uint32_t target_offset = writer_->bss_start_ + writer_->bss_method_entries_.Get(patch.TargetMethod()); @@ -1809,14 +1841,6 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { target_offset); break; } - case LinkerPatch::Type::kStringInternTable: { - uint32_t target_offset = GetInternTableEntryOffset(patch); - writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_, - patch, - offset_ + literal_offset, - target_offset); - break; - } case LinkerPatch::Type::kStringBssEntry: { StringReference ref(patch.TargetStringDexFile(), patch.TargetStringIndex()); uint32_t target_offset = @@ -1835,14 +1859,6 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { target_offset); break; } - case LinkerPatch::Type::kTypeClassTable: { - uint32_t target_offset = GetClassTableEntryOffset(patch); - writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_, - patch, - offset_ + literal_offset, - target_offset); - break; - } case LinkerPatch::Type::kTypeBssEntry: { TypeReference ref(patch.TargetTypeDexFile(), patch.TargetTypeIndex()); uint32_t target_offset = writer_->bss_start_ + writer_->bss_type_entries_.Get(ref); @@ -1944,7 +1960,7 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { const void* oat_code_offset = target->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_); if (oat_code_offset != 0) { - DCHECK(!writer_->HasBootImage()); + DCHECK(!writer_->GetCompilerOptions().IsBootImage()); DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickResolutionStub(oat_code_offset)); DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(oat_code_offset)); DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickGenericJniStub(oat_code_offset)); @@ -1981,13 +1997,24 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { ObjPtr string = linker->LookupString(patch.TargetStringIndex(), GetDexCache(patch.TargetStringDexFile())); DCHECK(string != nullptr); - DCHECK(writer_->HasBootImage() || + DCHECK(writer_->GetCompilerOptions().IsBootImage() || Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(string)); return string; } + uint32_t GetTargetIntrinsicReferenceOffset(const LinkerPatch& patch) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(writer_->GetCompilerOptions().IsBootImage()); + const void* address = + writer_->image_writer_->GetIntrinsicReferenceAddress(patch.IntrinsicData()); + size_t oat_index = writer_->image_writer_->GetOatIndexForDexFile(dex_file_); + uintptr_t oat_data_begin = writer_->image_writer_->GetOatDataBegin(oat_index); + // TODO: Clean up offset types. The target offset must be treated as signed. + return static_cast(reinterpret_cast(address) - oat_data_begin); + } + uint32_t GetTargetMethodOffset(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(writer_->HasBootImage()); + DCHECK(writer_->GetCompilerOptions().IsBootImage()); method = writer_->image_writer_->GetImageMethodAddress(method); size_t oat_index = writer_->image_writer_->GetOatIndexForDexFile(dex_file_); uintptr_t oat_data_begin = writer_->image_writer_->GetOatDataBegin(oat_index); @@ -1997,7 +2024,7 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { uint32_t GetTargetObjectOffset(ObjPtr object) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(writer_->HasBootImage()); + DCHECK(writer_->GetCompilerOptions().IsBootImage()); object = writer_->image_writer_->GetImageAddress(object.Ptr()); size_t oat_index = writer_->image_writer_->GetOatIndexForDexFile(dex_file_); uintptr_t oat_data_begin = writer_->image_writer_->GetOatDataBegin(oat_index); @@ -2007,7 +2034,7 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { void PatchObjectAddress(std::vector* code, uint32_t offset, mirror::Object* object) REQUIRES_SHARED(Locks::mutator_lock_) { - if (writer_->HasBootImage()) { + if (writer_->GetCompilerOptions().IsBootImage()) { object = writer_->image_writer_->GetImageAddress(object); } else { // NOTE: We're using linker patches for app->boot references when the image can @@ -2028,7 +2055,7 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { void PatchCodeAddress(std::vector* code, uint32_t offset, uint32_t target_offset) REQUIRES_SHARED(Locks::mutator_lock_) { uint32_t address = target_offset; - if (writer_->HasBootImage()) { + if (writer_->GetCompilerOptions().IsBootImage()) { size_t oat_index = writer_->image_writer_->GetOatIndexForDexCache(dex_cache_); // TODO: Clean up offset types. // The target_offset must be treated as signed for cross-oat patching. @@ -2044,42 +2071,6 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { data[2] = (address >> 16) & 0xffu; data[3] = (address >> 24) & 0xffu; } - - // Calculate the offset of the InternTable slot (GcRoot) when mmapped to the .bss. - uint32_t GetInternTableEntryOffset(const LinkerPatch& patch) - REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(!writer_->HasBootImage()); - const uint8_t* string_root = writer_->LookupBootImageInternTableSlot( - *patch.TargetStringDexFile(), patch.TargetStringIndex()); - DCHECK(string_root != nullptr); - return GetBootImageTableEntryOffset(string_root); - } - - // Calculate the offset of the ClassTable::TableSlot when mmapped to the .bss. - uint32_t GetClassTableEntryOffset(const LinkerPatch& patch) - REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(!writer_->HasBootImage()); - const uint8_t* table_slot = - writer_->LookupBootImageClassTableSlot(*patch.TargetTypeDexFile(), patch.TargetTypeIndex()); - DCHECK(table_slot != nullptr); - return GetBootImageTableEntryOffset(table_slot); - } - - uint32_t GetBootImageTableEntryOffset(const uint8_t* raw_root) { - uint32_t base_offset = writer_->bss_start_; - for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) { - const uint8_t* const_tables_begin = - space->Begin() + space->GetImageHeader().GetBootImageConstantTablesOffset(); - size_t offset = static_cast(raw_root - const_tables_begin); - if (offset < space->GetImageHeader().GetBootImageConstantTablesSize()) { - DCHECK_LE(base_offset + offset, writer_->bss_start_ + writer_->bss_methods_offset_); - return base_offset + offset; - } - base_offset += space->GetImageHeader().GetBootImageConstantTablesSize(); - } - LOG(FATAL) << "Didn't find boot image string in boot image intern tables!"; - UNREACHABLE(); - } }; class OatWriter::WriteMapMethodVisitor : public OatDexMethodVisitor { @@ -2234,13 +2225,11 @@ bool OatWriter::VisitDexMethods(DexMethodVisitor* visitor) { return true; } -size_t OatWriter::InitOatHeader(InstructionSet instruction_set, - const InstructionSetFeatures* instruction_set_features, - uint32_t num_dex_files, +size_t OatWriter::InitOatHeader(uint32_t num_dex_files, SafeMap* key_value_store) { TimingLogger::ScopedTiming split("InitOatHeader", timings_); - oat_header_.reset(OatHeader::Create(instruction_set, - instruction_set_features, + oat_header_.reset(OatHeader::Create(GetCompilerOptions().GetInstructionSet(), + GetCompilerOptions().GetInstructionSetFeatures(), num_dex_files, key_value_store)); size_oat_header_ += sizeof(OatHeader); @@ -2420,9 +2409,9 @@ size_t OatWriter::InitOatCode(size_t offset) { // TODO: Remove unused trampoline offsets from the OatHeader (requires oat version change). oat_header_->SetInterpreterToInterpreterBridgeOffset(0); oat_header_->SetInterpreterToCompiledCodeBridgeOffset(0); - if (compiler_driver_->GetCompilerOptions().IsBootImage()) { - InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); - const bool generate_debug_info = compiler_driver_->GetCompilerOptions().GenerateAnyDebugInfo(); + if (GetCompilerOptions().IsBootImage()) { + InstructionSet instruction_set = compiler_options_.GetInstructionSet(); + const bool generate_debug_info = GetCompilerOptions().GenerateAnyDebugInfo(); size_t adjusted_offset = offset; #define DO_TRAMPOLINE(field, fn_name) \ @@ -2460,7 +2449,7 @@ size_t OatWriter::InitOatCode(size_t offset) { } size_t OatWriter::InitOatCodeDexFiles(size_t offset) { - if (!compiler_driver_->GetCompilerOptions().IsAnyCompilationEnabled()) { + if (!GetCompilerOptions().IsAnyCompilationEnabled()) { if (kOatWriterDebugOatCodeLayout) { LOG(INFO) << "InitOatCodeDexFiles: OatWriter(" << this << "), " @@ -2517,6 +2506,25 @@ size_t OatWriter::InitOatCodeDexFiles(size_t offset) { return offset; } +size_t OatWriter::InitDataBimgRelRoLayout(size_t offset) { + DCHECK_EQ(data_bimg_rel_ro_size_, 0u); + if (data_bimg_rel_ro_entries_.empty()) { + // Nothing to put to the .data.bimg.rel.ro section. + return offset; + } + + data_bimg_rel_ro_start_ = RoundUp(offset, kPageSize); + + for (auto& entry : data_bimg_rel_ro_entries_) { + size_t& entry_offset = entry.second; + entry_offset = data_bimg_rel_ro_size_; + data_bimg_rel_ro_size_ += sizeof(uint32_t); + } + + offset = data_bimg_rel_ro_start_ + data_bimg_rel_ro_size_; + return offset; +} + void OatWriter::InitBssLayout(InstructionSet instruction_set) { { InitBssLayoutMethodVisitor visitor(this); @@ -2525,26 +2533,17 @@ void OatWriter::InitBssLayout(InstructionSet instruction_set) { } DCHECK_EQ(bss_size_, 0u); - if (HasBootImage()) { - DCHECK(!map_boot_image_tables_to_bss_); + if (GetCompilerOptions().IsBootImage()) { DCHECK(bss_string_entries_.empty()); } - if (!map_boot_image_tables_to_bss_ && - bss_method_entries_.empty() && + if (bss_method_entries_.empty() && bss_type_entries_.empty() && bss_string_entries_.empty()) { // Nothing to put to the .bss section. return; } - // Allocate space for boot image tables in the .bss section. PointerSize pointer_size = GetInstructionSetPointerSize(instruction_set); - if (map_boot_image_tables_to_bss_) { - for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) { - bss_size_ += space->GetImageHeader().GetBootImageConstantTablesSize(); - } - } - bss_methods_offset_ = bss_size_; // Prepare offsets for .bss ArtMethod entries. @@ -2763,7 +2762,7 @@ bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) { } size_t current_offset = start_offset; - if (compiler_driver_->GetCompilerOptions().IsQuickeningCompilationEnabled()) { + if (GetCompilerOptions().IsQuickeningCompilationEnabled()) { std::vector dex_files_indices; WriteQuickeningInfoMethodVisitor write_quicken_info_visitor(this, vdex_out); if (!write_quicken_info_visitor.VisitDexMethods(*dex_files_)) { @@ -2912,6 +2911,49 @@ bool OatWriter::WriteCode(OutputStream* out) { return false; } + if (data_bimg_rel_ro_size_ != 0u) { + write_state_ = WriteState::kWriteDataBimgRelRo; + } else { + if (!CheckOatSize(out, file_offset, relative_offset)) { + return false; + } + write_state_ = WriteState::kWriteHeader; + } + return true; +} + +bool OatWriter::WriteDataBimgRelRo(OutputStream* out) { + CHECK(write_state_ == WriteState::kWriteDataBimgRelRo); + + // Wrap out to update checksum with each write. + ChecksumUpdatingOutputStream checksum_updating_out(out, oat_header_.get()); + out = &checksum_updating_out; + + const size_t file_offset = oat_data_offset_; + size_t relative_offset = data_bimg_rel_ro_start_; + + // Record the padding before the .data.bimg.rel.ro section. + // Do not write anything, this zero-filled part was skipped (Seek()) when starting the section. + size_t code_end = GetOatHeader().GetExecutableOffset() + code_size_; + DCHECK_EQ(RoundUp(code_end, kPageSize), relative_offset); + size_t padding_size = relative_offset - code_end; + DCHECK_EQ(size_data_bimg_rel_ro_alignment_, 0u); + size_data_bimg_rel_ro_alignment_ = padding_size; + + relative_offset = WriteDataBimgRelRo(out, file_offset, relative_offset); + if (relative_offset == 0) { + LOG(ERROR) << "Failed to write boot image relocations to " << out->GetLocation(); + return false; + } + + if (!CheckOatSize(out, file_offset, relative_offset)) { + return false; + } + write_state_ = WriteState::kWriteHeader; + return true; +} + +bool OatWriter::CheckOatSize(OutputStream* out, size_t file_offset, size_t relative_offset) { const off_t oat_end_file_offset = out->Seek(0, kSeekCurrent); if (oat_end_file_offset == static_cast(-1)) { LOG(ERROR) << "Failed to get oat end file offset in " << out->GetLocation(); @@ -2946,6 +2988,8 @@ bool OatWriter::WriteCode(OutputStream* out) { DO_STAT(size_method_header_); DO_STAT(size_code_); DO_STAT(size_code_alignment_); + DO_STAT(size_data_bimg_rel_ro_); + DO_STAT(size_data_bimg_rel_ro_alignment_); DO_STAT(size_relative_call_thunks_); DO_STAT(size_misc_thunks_); DO_STAT(size_vmap_table_); @@ -2996,7 +3040,7 @@ bool OatWriter::WriteHeader(OutputStream* out, oat_header_->SetImageFileLocationOatChecksum(image_file_location_oat_checksum); oat_header_->SetImageFileLocationOatDataBegin(image_file_location_oat_begin); - if (compiler_driver_->GetCompilerOptions().IsBootImage()) { + if (GetCompilerOptions().IsBootImage()) { CHECK_EQ(image_patch_delta, 0); CHECK_EQ(oat_header_->GetImagePatchDelta(), 0); } else { @@ -3260,8 +3304,8 @@ size_t OatWriter::WriteOatDexFiles(OutputStream* out, size_t file_offset, size_t } size_t OatWriter::WriteCode(OutputStream* out, size_t file_offset, size_t relative_offset) { - if (compiler_driver_->GetCompilerOptions().IsBootImage()) { - InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); + if (GetCompilerOptions().IsBootImage()) { + InstructionSet instruction_set = compiler_options_.GetInstructionSet(); #define DO_TRAMPOLINE(field) \ do { \ @@ -3291,7 +3335,7 @@ size_t OatWriter::WriteCode(OutputStream* out, size_t file_offset, size_t relati size_t OatWriter::WriteCodeDexFiles(OutputStream* out, size_t file_offset, size_t relative_offset) { - if (!compiler_driver_->GetCompilerOptions().IsAnyCompilationEnabled()) { + if (!GetCompilerOptions().IsAnyCompilationEnabled()) { // As with InitOatCodeDexFiles, also skip the writer if // compilation was disabled. if (kOatWriterDebugOatCodeLayout) { @@ -3323,6 +3367,32 @@ size_t OatWriter::WriteCodeDexFiles(OutputStream* out, return relative_offset; } +size_t OatWriter::WriteDataBimgRelRo(OutputStream* out, + size_t file_offset, + size_t relative_offset) { + if (data_bimg_rel_ro_entries_.empty()) { + return relative_offset; + } + + // Write the entire .data.bimg.rel.ro with a single WriteFully(). + std::vector data; + data.reserve(data_bimg_rel_ro_entries_.size()); + for (const auto& entry : data_bimg_rel_ro_entries_) { + uint32_t boot_image_offset = entry.first; + data.push_back(boot_image_offset); + } + DCHECK_EQ(data.size(), data_bimg_rel_ro_entries_.size()); + DCHECK_OFFSET(); + if (!out->WriteFully(data.data(), data.size() * sizeof(data[0]))) { + PLOG(ERROR) << "Failed to write .data.bimg.rel.ro in " << out->GetLocation(); + return 0u; + } + DCHECK_EQ(size_data_bimg_rel_ro_, 0u); + size_data_bimg_rel_ro_ = data.size() * sizeof(data[0]); + relative_offset += size_data_bimg_rel_ro_; + return relative_offset; +} + bool OatWriter::RecordOatDataOffset(OutputStream* out) { // Get the elf file offset of the oat file. const off_t raw_file_offset = out->Seek(0, kSeekCurrent); @@ -3349,7 +3419,7 @@ bool OatWriter::WriteDexFiles(OutputStream* out, break; } ZipEntry* entry = oat_dex_file.source_.GetZipEntry(); - if (!entry->IsUncompressed() || !entry->IsAlignedToDexHeader()) { + if (!entry->IsUncompressed() || !entry->IsAlignedTo(alignof(DexFile::Header))) { extract_dex_files_into_vdex_ = true; break; } @@ -3580,18 +3650,22 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil const ArtDexFileLoader dex_file_loader; if (oat_dex_file->source_.IsZipEntry()) { ZipEntry* zip_entry = oat_dex_file->source_.GetZipEntry(); - std::unique_ptr mem_map( - zip_entry->ExtractToMemMap(location.c_str(), "classes.dex", &error_msg)); + std::unique_ptr mem_map; + { + TimingLogger::ScopedTiming extract("Unzip", timings_); + mem_map.reset(zip_entry->ExtractToMemMap(location.c_str(), "classes.dex", &error_msg)); + } if (mem_map == nullptr) { LOG(ERROR) << "Failed to extract dex file to mem map for layout: " << error_msg; return false; } + TimingLogger::ScopedTiming extract("Open", timings_); dex_file = dex_file_loader.Open(location, - zip_entry->GetCrc32(), - std::move(mem_map), - /* verify */ !compiling_boot_image_, - /* verify_checksum */ true, - &error_msg); + zip_entry->GetCrc32(), + std::move(mem_map), + /* verify */ !GetCompilerOptions().IsBootImage(), + /* verify_checksum */ true, + &error_msg); } else if (oat_dex_file->source_.IsRawFile()) { File* raw_file = oat_dex_file->source_.GetRawFile(); int dup_fd = dup(raw_file->Fd()); @@ -3599,8 +3673,9 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil PLOG(ERROR) << "Failed to dup dex file descriptor (" << raw_file->Fd() << ") at " << location; return false; } + TimingLogger::ScopedTiming extract("Open", timings_); dex_file = dex_file_loader.OpenDex(dup_fd, location, - /* verify */ !compiling_boot_image_, + /* verify */ !GetCompilerOptions().IsBootImage(), /* verify_checksum */ true, /* mmap_shared */ false, &error_msg); @@ -3633,21 +3708,31 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil options.update_checksum_ = true; DexLayout dex_layout(options, profile_compilation_info_, /*file*/ nullptr, /*header*/ nullptr); const uint8_t* dex_src = nullptr; - if (dex_layout.ProcessDexFile(location.c_str(), dex_file.get(), 0, &dex_container_, &error_msg)) { - oat_dex_file->dex_sections_layout_ = dex_layout.GetSections(); - // Dex layout can affect the size of the dex file, so we update here what we have set - // when adding the dex file as a source. - const UnalignedDexFileHeader* header = - AsUnalignedDexFileHeader(dex_container_->GetMainSection()->Begin()); - oat_dex_file->dex_file_size_ = header->file_size_; - dex_src = dex_container_->GetMainSection()->Begin(); - } else { - LOG(WARNING) << "Failed to run dex layout, reason:" << error_msg; - // Since we failed to convert the dex, just copy the input dex. - dex_src = dex_file->Begin(); + { + TimingLogger::ScopedTiming extract("ProcessDexFile", timings_); + if (dex_layout.ProcessDexFile(location.c_str(), + dex_file.get(), + 0, + &dex_container_, + &error_msg)) { + oat_dex_file->dex_sections_layout_ = dex_layout.GetSections(); + // Dex layout can affect the size of the dex file, so we update here what we have set + // when adding the dex file as a source. + const UnalignedDexFileHeader* header = + AsUnalignedDexFileHeader(dex_container_->GetMainSection()->Begin()); + oat_dex_file->dex_file_size_ = header->file_size_; + dex_src = dex_container_->GetMainSection()->Begin(); + } else { + LOG(WARNING) << "Failed to run dex layout, reason:" << error_msg; + // Since we failed to convert the dex, just copy the input dex. + dex_src = dex_file->Begin(); + } } - if (!WriteDexFile(out, oat_dex_file, dex_src, /* update_input_vdex */ false)) { - return false; + { + TimingLogger::ScopedTiming extract("WriteDexFile", timings_); + if (!WriteDexFile(out, oat_dex_file, dex_src, /* update_input_vdex */ false)) { + return false; + } } if (dex_container_ != nullptr) { // Clear the main section in case we write more data into the container. @@ -3968,13 +4053,13 @@ bool OatWriter::WriteTypeLookupTables( // TypeLookupTable allocates its own and OatDexFile takes ownership. const DexFile& dex_file = *opened_dex_files[i]; { - std::unique_ptr type_lookup_table = - TypeLookupTable::Create(dex_file, /* storage */ nullptr); + TypeLookupTable type_lookup_table = TypeLookupTable::Create(dex_file); type_lookup_table_oat_dex_files_.push_back( std::make_unique(std::move(type_lookup_table))); dex_file.SetOatDexFile(type_lookup_table_oat_dex_files_.back().get()); } - TypeLookupTable* const table = type_lookup_table_oat_dex_files_.back()->GetTypeLookupTable(); + const TypeLookupTable& table = type_lookup_table_oat_dex_files_.back()->GetTypeLookupTable(); + DCHECK(table.Valid()); // Type tables are required to be 4 byte aligned. size_t initial_offset = oat_size_; @@ -3993,9 +4078,9 @@ bool OatWriter::WriteTypeLookupTables( DCHECK_EQ(oat_data_offset_ + rodata_offset, static_cast(oat_rodata->Seek(0u, kSeekCurrent))); - DCHECK_EQ(table_size, table->RawDataLength()); + DCHECK_EQ(table_size, table.RawDataLength()); - if (!oat_rodata->WriteFully(table->RawData(), table_size)) { + if (!oat_rodata->WriteFully(table.RawData(), table_size)) { PLOG(ERROR) << "Failed to write lookup table." << " File: " << oat_dex_file->GetLocation() << " Output: " << oat_rodata->GetLocation(); @@ -4376,42 +4461,6 @@ bool OatWriter::OatClass::Write(OatWriter* oat_writer, OutputStream* out) const return true; } -const uint8_t* OatWriter::LookupBootImageInternTableSlot(const DexFile& dex_file, - dex::StringIndex string_idx) - NO_THREAD_SAFETY_ANALYSIS { // Single-threaded OatWriter can avoid locking. - uint32_t utf16_length; - const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length); - DCHECK_EQ(utf16_length, CountModifiedUtf8Chars(utf8_data)); - InternTable::Utf8String string(utf16_length, - utf8_data, - ComputeUtf16HashFromModifiedUtf8(utf8_data, utf16_length)); - const InternTable* intern_table = Runtime::Current()->GetClassLinker()->intern_table_; - for (const InternTable::Table::UnorderedSet& table : intern_table->strong_interns_.tables_) { - auto it = table.Find(string); - if (it != table.end()) { - return reinterpret_cast(std::addressof(*it)); - } - } - LOG(FATAL) << "Did not find boot image string " << utf8_data; - UNREACHABLE(); -} - -const uint8_t* OatWriter::LookupBootImageClassTableSlot(const DexFile& dex_file, - dex::TypeIndex type_idx) - NO_THREAD_SAFETY_ANALYSIS { // Single-threaded OatWriter can avoid locking. - const char* descriptor = dex_file.StringByTypeIdx(type_idx); - ClassTable::DescriptorHashPair pair(descriptor, ComputeModifiedUtf8Hash(descriptor)); - ClassTable* table = Runtime::Current()->GetClassLinker()->boot_class_table_.get(); - for (const ClassTable::ClassSet& class_set : table->classes_) { - auto it = class_set.Find(pair); - if (it != class_set.end()) { - return reinterpret_cast(std::addressof(*it)); - } - } - LOG(FATAL) << "Did not find boot image class " << descriptor; - UNREACHABLE(); -} - debug::DebugInfo OatWriter::GetDebugInfo() const { debug::DebugInfo debug_info{}; debug_info.compiled_methods = ArrayRef(method_info_); diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h index 2dae811e04c8c590e7e7888a3666a33c046293e5..298859bb38e0a7c0ffc4bd0e1b0fb794acfa6131 100644 --- a/dex2oat/linker/oat_writer.h +++ b/dex2oat/linker/oat_writer.h @@ -25,15 +25,14 @@ #include "base/array_ref.h" #include "base/dchecked_vector.h" #include "base/os.h" +#include "base/mem_map.h" #include "base/safe_map.h" -#include "compiler.h" #include "debug/debug_info.h" #include "dex/compact_dex_level.h" #include "dex/method_reference.h" #include "dex/string_reference.h" #include "dex/type_reference.h" #include "linker/relative_patcher.h" // For RelativePatcherTargetProvider. -#include "mem_map.h" #include "mirror/class.h" #include "oat.h" @@ -42,6 +41,7 @@ namespace art { class BitVector; class CompiledMethod; class CompilerDriver; +class CompilerOptions; class DexContainer; class ProfileCompilationInfo; class TimingLogger; @@ -63,6 +63,12 @@ class ImageWriter; class MultiOatRelativePatcher; class OutputStream; +enum class CopyOption { + kNever, + kAlways, + kOnlyIfCompressed +}; + // OatHeader variable length with count of D OatDexFiles // // TypeLookupTable[0] one descriptor to class def index hash table for each OatDexFile. @@ -118,7 +124,7 @@ class OatWriter { kDefault = kCreate }; - OatWriter(bool compiling_boot_image, + OatWriter(const CompilerOptions& compiler_options, TimingLogger* timings, ProfileCompilationInfo* info, CompactDexLevel compact_dex_level); @@ -137,6 +143,7 @@ class OatWriter { // - PrepareLayout(), // - WriteRodata(), // - WriteCode(), + // - WriteDataBimgRelRo() iff GetDataBimgRelRoSize() != 0, // - WriteHeader(). // Add dex file source(s) from a file, either a plain dex file or @@ -171,25 +178,19 @@ class OatWriter { // and the compiler will just re-use the existing vdex file. bool WriteAndOpenDexFiles(File* vdex_file, OutputStream* oat_rodata, - InstructionSet instruction_set, - const InstructionSetFeatures* instruction_set_features, SafeMap* key_value_store, bool verify, bool update_input_vdex, CopyOption copy_dex_files, /*out*/ std::vector>* opened_dex_files_map, /*out*/ std::vector>* opened_dex_files); + // Initialize the writer with the given parameters. + void Initialize(const CompilerDriver* compiler_driver, + ImageWriter* image_writer, + const std::vector& dex_files); bool WriteQuickeningInfo(OutputStream* vdex_out); bool WriteVerifierDeps(OutputStream* vdex_out, verifier::VerifierDeps* verifier_deps); bool WriteChecksumsAndVdexHeader(OutputStream* vdex_out); - // Initialize the writer with the given parameters. - void Initialize(const CompilerDriver* compiler, - ImageWriter* image_writer, - const std::vector& dex_files) { - compiler_driver_ = compiler; - image_writer_ = image_writer; - dex_files_ = &dex_files; - } // Prepare layout of remaining data. void PrepareLayout(MultiOatRelativePatcher* relative_patcher); @@ -197,6 +198,10 @@ class OatWriter { bool WriteRodata(OutputStream* out); // Write the code to the .text section. bool WriteCode(OutputStream* out); + // Write the boot image relocation data to the .data.bimg.rel.ro section. + bool WriteDataBimgRelRo(OutputStream* out); + // Check the size of the written oat file. + bool CheckOatSize(OutputStream* out, size_t file_offset, size_t relative_offset); // Write the oat header. This finalizes the oat file. bool WriteHeader(OutputStream* out, uint32_t image_file_location_oat_checksum, @@ -210,18 +215,22 @@ class OatWriter { return image_writer_ != nullptr; } - bool HasBootImage() const { - return compiling_boot_image_; - } - const OatHeader& GetOatHeader() const { return *oat_header_; } + size_t GetCodeSize() const { + return code_size_; + } + size_t GetOatSize() const { return oat_size_; } + size_t GetDataBimgRelRoSize() const { + return data_bimg_rel_ro_size_; + } + size_t GetBssSize() const { return bss_size_; } @@ -250,6 +259,10 @@ class OatWriter { return compiler_driver_; } + const CompilerOptions& GetCompilerOptions() const { + return compiler_options_; + } + private: class DexFileSource; class OatClassHeader; @@ -312,10 +325,7 @@ class OatWriter { /*out*/ std::vector>* opened_dex_files_map, /*out*/ std::vector>* opened_dex_files); - size_t InitOatHeader(InstructionSet instruction_set, - const InstructionSetFeatures* instruction_set_features, - uint32_t num_dex_files, - SafeMap* key_value_store); + size_t InitOatHeader(uint32_t num_dex_files, SafeMap* key_value_store); size_t InitClassOffsets(size_t offset); size_t InitOatClasses(size_t offset); size_t InitOatMaps(size_t offset); @@ -323,6 +333,7 @@ class OatWriter { size_t InitOatDexFiles(size_t offset); size_t InitOatCode(size_t offset); size_t InitOatCodeDexFiles(size_t offset); + size_t InitDataBimgRelRoLayout(size_t offset); void InitBssLayout(InstructionSet instruction_set); size_t WriteClassOffsets(OutputStream* out, size_t file_offset, size_t relative_offset); @@ -332,6 +343,7 @@ class OatWriter { size_t WriteOatDexFiles(OutputStream* out, size_t file_offset, size_t relative_offset); size_t WriteCode(OutputStream* out, size_t file_offset, size_t relative_offset); size_t WriteCodeDexFiles(OutputStream* out, size_t file_offset, size_t relative_offset); + size_t WriteDataBimgRelRo(OutputStream* out, size_t file_offset, size_t relative_offset); bool RecordOatDataOffset(OutputStream* out); bool WriteTypeLookupTables(OutputStream* oat_rodata, @@ -349,17 +361,12 @@ class OatWriter { return dex_files_ != nullptr && extract_dex_files_into_vdex_; } - // Find the address of the GcRoot in the InternTable for a boot image string. - const uint8_t* LookupBootImageInternTableSlot(const DexFile& dex_file, - dex::StringIndex string_idx); - // Find the address of the ClassTable::TableSlot for a boot image class. - const uint8_t* LookupBootImageClassTableSlot(const DexFile& dex_file, dex::TypeIndex type_idx); - enum class WriteState { kAddingDexFileSources, kPrepareLayout, kWriteRoData, kWriteText, + kWriteDataBimgRelRo, kWriteHeader, kDone }; @@ -378,8 +385,8 @@ class OatWriter { dchecked_vector method_info_; const CompilerDriver* compiler_driver_; + const CompilerOptions& compiler_options_; ImageWriter* image_writer_; - const bool compiling_boot_image_; // Whether the dex files being compiled are going to be extracted to the vdex. bool extract_dex_files_into_vdex_; @@ -401,9 +408,18 @@ class OatWriter { // Offset of section holding quickening info inside Vdex. size_t vdex_quickening_info_offset_; + // Size of the .text segment. + size_t code_size_; + // Size required for Oat data structures. size_t oat_size_; + // The start of the required .data.bimg.rel.ro section. + size_t data_bimg_rel_ro_start_; + + // The size of the required .data.bimg.rel.ro section holding the boot image relocations. + size_t data_bimg_rel_ro_size_; + // The start of the required .bss section. size_t bss_start_; @@ -416,6 +432,10 @@ class OatWriter { // The offset of the GC roots in .bss section. size_t bss_roots_offset_; + // Map for allocating .data.bimg.rel.ro entries. Indexed by the boot image offset of the + // relocation. The value is the assigned offset within the .data.bimg.rel.ro section. + SafeMap data_bimg_rel_ro_entries_; + // Map for recording references to ArtMethod entries in .bss. SafeMap bss_method_entry_references_; @@ -440,10 +460,6 @@ class OatWriter { // is the target offset for patching, starting at `bss_start_ + bss_roots_offset_`. SafeMap bss_string_entries_; - // Whether boot image tables should be mapped to the .bss. This is needed for compiled - // code that reads from these tables with PC-relative instructions. - bool map_boot_image_tables_to_bss_; - // Offset of the oat data from the start of the mmapped region of the elf file. size_t oat_data_offset_; @@ -484,6 +500,8 @@ class OatWriter { uint32_t size_method_header_; uint32_t size_code_; uint32_t size_code_alignment_; + uint32_t size_data_bimg_rel_ro_; + uint32_t size_data_bimg_rel_ro_alignment_; uint32_t size_relative_call_thunks_; uint32_t size_misc_thunks_; uint32_t size_vmap_table_; diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index e2dfc78fc70bee706e11823bcd7b3c3457de5781..f8370515b5b771bcd1295a989876f87e33f15c92 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -33,7 +33,6 @@ #include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "entrypoints/quick/quick_entrypoints.h" -#include "jit/profile_compilation_info.h" #include "linker/buffered_output_stream.h" #include "linker/elf_writer.h" #include "linker/elf_writer_quick.h" @@ -45,6 +44,7 @@ #include "mirror/object_array-inl.h" #include "oat_file-inl.h" #include "oat_writer.h" +#include "profile/profile_compilation_info.h" #include "scoped_thread_state_change-inl.h" #include "vdex_file.h" @@ -86,35 +86,17 @@ class OatTest : public CommonCompilerTest { } } - void SetupCompiler(Compiler::Kind compiler_kind, - InstructionSet insn_set, - const std::vector& compiler_options, - /*out*/std::string* error_msg) { - ASSERT_TRUE(error_msg != nullptr); - insn_features_ = InstructionSetFeatures::FromVariant(insn_set, "default", error_msg); - ASSERT_TRUE(insn_features_ != nullptr) << *error_msg; - compiler_options_.reset(new CompilerOptions); + void SetupCompiler(const std::vector& compiler_options) { + std::string error_msg; if (!compiler_options_->ParseCompilerOptions(compiler_options, false /* ignore_unrecognized */, - error_msg)) { - LOG(FATAL) << *error_msg; + &error_msg)) { + LOG(FATAL) << error_msg; UNREACHABLE(); } - verification_results_.reset(new VerificationResults(compiler_options_.get())); callbacks_.reset(new QuickCompilerCallbacks(CompilerCallbacks::CallbackMode::kCompileApp)); callbacks_->SetVerificationResults(verification_results_.get()); Runtime::Current()->SetCompilerCallbacks(callbacks_.get()); - compiler_driver_.reset(new CompilerDriver(compiler_options_.get(), - verification_results_.get(), - compiler_kind, - insn_set, - insn_features_.get(), - /* image_classes */ nullptr, - /* compiled_classes */ nullptr, - /* compiled_methods */ nullptr, - /* thread_count */ 2, - /* swap_fd */ -1, - /* profile_compilation_info */ nullptr)); } bool WriteElf(File* vdex_file, @@ -123,7 +105,8 @@ class OatTest : public CommonCompilerTest { SafeMap& key_value_store, bool verify) { TimingLogger timings("WriteElf", false, false); - OatWriter oat_writer(/*compiling_boot_image*/false, + ClearBootImageOption(); + OatWriter oat_writer(*compiler_options_, &timings, /*profile_compilation_info*/nullptr, CompactDexLevel::kCompactDexLevelNone); @@ -147,7 +130,8 @@ class OatTest : public CommonCompilerTest { bool verify, ProfileCompilationInfo* profile_compilation_info) { TimingLogger timings("WriteElf", false, false); - OatWriter oat_writer(/*compiling_boot_image*/false, + ClearBootImageOption(); + OatWriter oat_writer(*compiler_options_, &timings, profile_compilation_info, CompactDexLevel::kCompactDexLevelNone); @@ -166,7 +150,8 @@ class OatTest : public CommonCompilerTest { SafeMap& key_value_store, bool verify) { TimingLogger timings("WriteElf", false, false); - OatWriter oat_writer(/*compiling_boot_image*/false, + ClearBootImageOption(); + OatWriter oat_writer(*compiler_options_, &timings, /*profile_compilation_info*/nullptr, CompactDexLevel::kCompactDexLevelNone); @@ -182,9 +167,7 @@ class OatTest : public CommonCompilerTest { SafeMap& key_value_store, bool verify) { std::unique_ptr elf_writer = CreateElfWriterQuick( - compiler_driver_->GetInstructionSet(), - compiler_driver_->GetInstructionSetFeatures(), - &compiler_driver_->GetCompilerOptions(), + compiler_driver_->GetCompilerOptions(), oat_file); elf_writer->Start(); OutputStream* oat_rodata = elf_writer->StartRoData(); @@ -193,8 +176,6 @@ class OatTest : public CommonCompilerTest { if (!oat_writer.WriteAndOpenDexFiles( vdex_file, oat_rodata, - compiler_driver_->GetInstructionSet(), - compiler_driver_->GetInstructionSetFeatures(), &key_value_store, verify, /* update_input_vdex */ false, @@ -212,14 +193,14 @@ class OatTest : public CommonCompilerTest { ScopedObjectAccess soa(Thread::Current()); class_linker->RegisterDexFile(*dex_file, nullptr); } - MultiOatRelativePatcher patcher(compiler_driver_->GetInstructionSet(), - instruction_set_features_.get()); + MultiOatRelativePatcher patcher(compiler_options_->GetInstructionSet(), + compiler_options_->GetInstructionSetFeatures(), + compiler_driver_->GetCompiledMethodStorage()); oat_writer.Initialize(compiler_driver_.get(), nullptr, dex_files); oat_writer.PrepareLayout(&patcher); - size_t rodata_size = oat_writer.GetOatHeader().GetExecutableOffset(); - size_t text_size = oat_writer.GetOatSize() - rodata_size; - elf_writer->PrepareDynamicSection(rodata_size, - text_size, + elf_writer->PrepareDynamicSection(oat_writer.GetOatHeader().GetExecutableOffset(), + oat_writer.GetCodeSize(), + oat_writer.GetDataBimgRelRoSize(), oat_writer.GetBssSize(), oat_writer.GetBssMethodsOffset(), oat_writer.GetBssRootsOffset(), @@ -248,6 +229,14 @@ class OatTest : public CommonCompilerTest { } elf_writer->EndText(text); + if (oat_writer.GetDataBimgRelRoSize() != 0u) { + OutputStream* data_bimg_rel_ro = elf_writer->StartDataBimgRelRo(); + if (!oat_writer.WriteDataBimgRelRo(data_bimg_rel_ro)) { + return false; + } + elf_writer->EndDataBimgRelRo(data_bimg_rel_ro); + } + if (!oat_writer.WriteHeader(elf_writer->GetStream(), 42U, 4096U, 0)) { return false; } @@ -272,7 +261,6 @@ class OatTest : public CommonCompilerTest { void TestZipFileInput(bool verify); void TestZipFileInputWithEmptyDex(); - std::unique_ptr insn_features_; std::unique_ptr callbacks_; std::vector> opened_dex_files_maps_; @@ -394,16 +382,13 @@ TEST_F(OatTest, WriteRead) { TimingLogger timings("OatTest::WriteRead", false, false); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - // TODO: make selectable. - Compiler::Kind compiler_kind = Compiler::kQuick; - InstructionSet insn_set = kIsTargetBuild ? InstructionSet::kThumb2 : InstructionSet::kX86; std::string error_msg; - SetupCompiler(compiler_kind, insn_set, std::vector(), /*out*/ &error_msg); + SetupCompiler(std::vector()); jobject class_loader = nullptr; if (kCompile) { TimingLogger timings2("OatTest::WriteRead", false, false); - compiler_driver_->SetDexFilesForOatFile(class_linker->GetBootClassPath()); + SetDexFilesForOatFile(class_linker->GetBootClassPath()); compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings2); } @@ -440,8 +425,8 @@ TEST_F(OatTest, WriteRead) { ASSERT_TRUE(java_lang_dex_file_ != nullptr); const DexFile& dex_file = *java_lang_dex_file_; uint32_t dex_file_checksum = dex_file.GetLocationChecksum(); - const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation().c_str(), - &dex_file_checksum); + const OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation().c_str(), + &dex_file_checksum); ASSERT_TRUE(oat_dex_file != nullptr); CHECK_EQ(dex_file.GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum()); ScopedObjectAccess soa(Thread::Current()); @@ -457,9 +442,9 @@ TEST_F(OatTest, WriteRead) { } const char* descriptor = dex_file.GetClassDescriptor(class_def); - mirror::Class* klass = class_linker->FindClass(soa.Self(), - descriptor, - ScopedNullHandle()); + ObjPtr klass = class_linker->FindClass(soa.Self(), + descriptor, + ScopedNullHandle()); const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(i); CHECK_EQ(ClassStatus::kNotReady, oat_class.GetStatus()) << descriptor; @@ -491,7 +476,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) { EXPECT_EQ(76U, sizeof(OatHeader)); EXPECT_EQ(4U, sizeof(OatMethodOffsets)); EXPECT_EQ(24U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(162 * static_cast(GetInstructionSetPointerSize(kRuntimeISA)), + EXPECT_EQ(166 * static_cast(GetInstructionSetPointerSize(kRuntimeISA)), sizeof(QuickEntryPoints)); } @@ -518,14 +503,9 @@ TEST_F(OatTest, OatHeaderIsValid) { TEST_F(OatTest, EmptyTextSection) { TimingLogger timings("OatTest::EmptyTextSection", false, false); - // TODO: make selectable. - Compiler::Kind compiler_kind = Compiler::kQuick; - InstructionSet insn_set = kRuntimeISA; - if (insn_set == InstructionSet::kArm) insn_set = InstructionSet::kThumb2; - std::string error_msg; std::vector compiler_options; compiler_options.push_back("--compiler-filter=extract"); - SetupCompiler(compiler_kind, insn_set, compiler_options, /*out*/ &error_msg); + SetupCompiler(compiler_options); jobject class_loader; { @@ -539,10 +519,9 @@ TEST_F(OatTest, EmptyTextSection) { ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); for (const DexFile* dex_file : dex_files) { ScopedObjectAccess soa(Thread::Current()); - class_linker->RegisterDexFile(*dex_file, - soa.Decode(class_loader).Ptr()); + class_linker->RegisterDexFile(*dex_file, soa.Decode(class_loader)); } - compiler_driver_->SetDexFilesForOatFile(dex_files); + SetDexFilesForOatFile(dex_files); compiler_driver_->CompileAll(class_loader, dex_files, &timings); ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex"); @@ -555,6 +534,7 @@ TEST_F(OatTest, EmptyTextSection) { /* verify */ false); ASSERT_TRUE(success); + std::string error_msg; std::unique_ptr oat_file(OatFile::Open(/* zip_fd */ -1, tmp_oat.GetFilename(), tmp_oat.GetFilename(), diff --git a/compiler/linker/relative_patcher.cc b/dex2oat/linker/relative_patcher.cc similarity index 91% rename from compiler/linker/relative_patcher.cc rename to dex2oat/linker/relative_patcher.cc index 13877f8f128d65988d7b113dca0bea81bb7bb4e6..b6135c9b5fc88a85b82aa24284d03adc226626d9 100644 --- a/compiler/linker/relative_patcher.cc +++ b/dex2oat/linker/relative_patcher.cc @@ -35,7 +35,7 @@ #ifdef ART_ENABLE_CODEGEN_x86_64 #include "linker/x86_64/relative_patcher_x86_64.h" #endif -#include "output_stream.h" +#include "linker/output_stream.h" namespace art { namespace linker { @@ -43,7 +43,8 @@ namespace linker { std::unique_ptr RelativePatcher::Create( InstructionSet instruction_set, const InstructionSetFeatures* features, - RelativePatcherTargetProvider* provider) { + RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider) { class RelativePatcherNone FINAL : public RelativePatcher { public: RelativePatcherNone() { } @@ -92,7 +93,8 @@ std::unique_ptr RelativePatcher::Create( }; UNUSED(features); - UNUSED(provider); + UNUSED(thunk_provider); + UNUSED(target_provider); switch (instruction_set) { #ifdef ART_ENABLE_CODEGEN_x86 case InstructionSet::kX86: @@ -106,12 +108,15 @@ std::unique_ptr RelativePatcher::Create( case InstructionSet::kArm: // Fall through: we generate Thumb2 code for "arm". case InstructionSet::kThumb2: - return std::unique_ptr(new Thumb2RelativePatcher(provider)); + return std::unique_ptr( + new Thumb2RelativePatcher(thunk_provider, target_provider)); #endif #ifdef ART_ENABLE_CODEGEN_arm64 case InstructionSet::kArm64: return std::unique_ptr( - new Arm64RelativePatcher(provider, features->AsArm64InstructionSetFeatures())); + new Arm64RelativePatcher(thunk_provider, + target_provider, + features->AsArm64InstructionSetFeatures())); #endif #ifdef ART_ENABLE_CODEGEN_mips case InstructionSet::kMips: diff --git a/compiler/linker/relative_patcher.h b/dex2oat/linker/relative_patcher.h similarity index 82% rename from compiler/linker/relative_patcher.h rename to dex2oat/linker/relative_patcher.h index b58e3dffbd665e2363e67d06a85264483ec22e9f..d80eaf94f7d78dccd75711fda12c7f27eec3e90f 100644 --- a/compiler/linker/relative_patcher.h +++ b/dex2oat/linker/relative_patcher.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_LINKER_RELATIVE_PATCHER_H_ -#define ART_COMPILER_LINKER_RELATIVE_PATCHER_H_ +#ifndef ART_DEX2OAT_LINKER_RELATIVE_PATCHER_H_ +#define ART_DEX2OAT_LINKER_RELATIVE_PATCHER_H_ #include @@ -38,6 +38,27 @@ namespace linker { class LinkerPatch; class OutputStream; +/** + * @class RelativePatcherThunkProvider + * @brief Interface for providing method offsets for relative call targets. + */ +class RelativePatcherThunkProvider { + public: + /** + * Get the code and debug name of a thunk needed by the given linker patch. + * + * @param patch The patch for which we need to retrieve the thunk code. + * @param code A variable to receive the code of the thunk. This code must not be empty. + * @param debug_name A variable to receive the debug name of the thunk. + */ + virtual void GetThunkCode(const LinkerPatch& patch, + /*out*/ ArrayRef* code, + /*out*/ std::string* debug_name) = 0; + + protected: + virtual ~RelativePatcherThunkProvider() { } +}; + /** * @class RelativePatcherTargetProvider * @brief Interface for providing method offsets for relative call targets. @@ -70,8 +91,10 @@ class RelativePatcherTargetProvider { class RelativePatcher { public: static std::unique_ptr Create( - InstructionSet instruction_set, const InstructionSetFeatures* features, - RelativePatcherTargetProvider* provider); + InstructionSet instruction_set, + const InstructionSetFeatures* features, + RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider); virtual ~RelativePatcher() { } @@ -144,4 +167,4 @@ class RelativePatcher { } // namespace linker } // namespace art -#endif // ART_COMPILER_LINKER_RELATIVE_PATCHER_H_ +#endif // ART_DEX2OAT_LINKER_RELATIVE_PATCHER_H_ diff --git a/compiler/linker/relative_patcher_test.h b/dex2oat/linker/relative_patcher_test.h similarity index 74% rename from compiler/linker/relative_patcher_test.h rename to dex2oat/linker/relative_patcher_test.h index d21f2795b983e8883349f90629d5209b5e5a8810..ae58b54863ab403b377230ebd225f553d5eb90ac 100644 --- a/compiler/linker/relative_patcher_test.h +++ b/dex2oat/linker/relative_patcher_test.h @@ -14,61 +14,65 @@ * limitations under the License. */ -#ifndef ART_COMPILER_LINKER_RELATIVE_PATCHER_TEST_H_ -#define ART_COMPILER_LINKER_RELATIVE_PATCHER_TEST_H_ +#ifndef ART_DEX2OAT_LINKER_RELATIVE_PATCHER_TEST_H_ +#define ART_DEX2OAT_LINKER_RELATIVE_PATCHER_TEST_H_ #include "arch/instruction_set.h" #include "arch/instruction_set_features.h" #include "base/array_ref.h" +#include "base/globals.h" #include "base/macros.h" +#include "common_compiler_test.h" #include "compiled_method-inl.h" #include "dex/verification_results.h" #include "dex/method_reference.h" #include "dex/string_reference.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" -#include "globals.h" #include "gtest/gtest.h" #include "linker/relative_patcher.h" +#include "linker/vector_output_stream.h" #include "oat.h" #include "oat_quick_method_header.h" -#include "vector_output_stream.h" namespace art { namespace linker { // Base class providing infrastructure for architecture-specific tests. -class RelativePatcherTest : public testing::Test { +class RelativePatcherTest : public CommonCompilerTest { protected: RelativePatcherTest(InstructionSet instruction_set, const std::string& variant) - : compiler_options_(), - verification_results_(&compiler_options_), - driver_(&compiler_options_, - &verification_results_, - Compiler::kQuick, - instruction_set, - /* instruction_set_features*/ nullptr, - /* image_classes */ nullptr, - /* compiled_classes */ nullptr, - /* compiled_methods */ nullptr, - /* thread_count */ 1u, - /* swap_fd */ -1, - /* profile_compilation_info */ nullptr), - error_msg_(), - instruction_set_(instruction_set), - features_(InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg_)), + : variant_(variant), method_offset_map_(), - patcher_(RelativePatcher::Create(instruction_set, features_.get(), &method_offset_map_)), + patcher_(nullptr), bss_begin_(0u), compiled_method_refs_(), compiled_methods_(), patched_code_(), output_(), out_("test output stream", &output_) { - CHECK(error_msg_.empty()) << instruction_set << "/" << variant; + // Override CommonCompilerTest's defaults. + instruction_set_ = instruction_set; + number_of_threads_ = 1u; patched_code_.reserve(16 * KB); } + void SetUp() OVERRIDE { + OverrideInstructionSetFeatures(instruction_set_, variant_); + CommonCompilerTest::SetUp(); + + patcher_ = RelativePatcher::Create(compiler_options_->GetInstructionSet(), + compiler_options_->GetInstructionSetFeatures(), + &thunk_provider_, + &method_offset_map_); + } + + void TearDown() OVERRIDE { + compiled_methods_.clear(); + patcher_.reset(); + CommonCompilerTest::TearDown(); + } + MethodReference MethodRef(uint32_t method_idx) { CHECK_NE(method_idx, 0u); return MethodReference(nullptr, method_idx); @@ -80,7 +84,7 @@ class RelativePatcherTest : public testing::Test { const ArrayRef& patches = ArrayRef()) { compiled_method_refs_.push_back(method_ref); compiled_methods_.emplace_back(new CompiledMethod( - &driver_, + compiler_driver_.get(), instruction_set_, code, /* frame_size_in_bytes */ 0u, @@ -248,6 +252,72 @@ class RelativePatcherTest : public testing::Test { LOG(ERROR) << " " << diff_indicator_str; } + class ThunkProvider : public RelativePatcherThunkProvider { + public: + ThunkProvider() {} + + void SetThunkCode(const LinkerPatch& patch, + ArrayRef code, + const std::string& debug_name) { + thunk_map_.emplace(ThunkKey(patch), ThunkValue(code, debug_name)); + } + + void GetThunkCode(const LinkerPatch& patch, + /*out*/ ArrayRef* code, + /*out*/ std::string* debug_name) OVERRIDE { + auto it = thunk_map_.find(ThunkKey(patch)); + CHECK(it != thunk_map_.end()); + const ThunkValue& value = it->second; + CHECK(code != nullptr); + *code = value.GetCode(); + CHECK(debug_name != nullptr); + *debug_name = value.GetDebugName(); + } + + private: + class ThunkKey { + public: + explicit ThunkKey(const LinkerPatch& patch) + : type_(patch.GetType()), + custom_value1_(patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch + ? patch.GetBakerCustomValue1() : 0u), + custom_value2_(patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch + ? patch.GetBakerCustomValue2() : 0u) { + CHECK(patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch || + patch.GetType() == LinkerPatch::Type::kCallRelative); + } + + bool operator<(const ThunkKey& other) const { + if (custom_value1_ != other.custom_value1_) { + return custom_value1_ < other.custom_value1_; + } + if (custom_value2_ != other.custom_value2_) { + return custom_value2_ < other.custom_value2_; + } + return type_ < other.type_; + } + + private: + const LinkerPatch::Type type_; + const uint32_t custom_value1_; + const uint32_t custom_value2_; + }; + + class ThunkValue { + public: + ThunkValue(ArrayRef code, const std::string& debug_name) + : code_(code.begin(), code.end()), debug_name_(debug_name) {} + ArrayRef GetCode() const { return ArrayRef(code_); } + const std::string& GetDebugName() const { return debug_name_; } + + private: + const std::vector code_; + const std::string debug_name_; + }; + + std::map thunk_map_; + }; + // Map method reference to assinged offset. // Wrap the map in a class implementing RelativePatcherTargetProvider. class MethodOffsetMap FINAL : public RelativePatcherTargetProvider { @@ -266,12 +336,8 @@ class RelativePatcherTest : public testing::Test { static const uint32_t kTrampolineSize = 4u; static const uint32_t kTrampolineOffset = 0u; - CompilerOptions compiler_options_; - VerificationResults verification_results_; - CompilerDriver driver_; // Needed for constructing CompiledMethod. - std::string error_msg_; - InstructionSet instruction_set_; - std::unique_ptr features_; + std::string variant_; + ThunkProvider thunk_provider_; MethodOffsetMap method_offset_map_; std::unique_ptr patcher_; uint32_t bss_begin_; @@ -286,4 +352,4 @@ class RelativePatcherTest : public testing::Test { } // namespace linker } // namespace art -#endif // ART_COMPILER_LINKER_RELATIVE_PATCHER_TEST_H_ +#endif // ART_DEX2OAT_LINKER_RELATIVE_PATCHER_TEST_H_ diff --git a/compiler/linker/x86/relative_patcher_x86.cc b/dex2oat/linker/x86/relative_patcher_x86.cc similarity index 100% rename from compiler/linker/x86/relative_patcher_x86.cc rename to dex2oat/linker/x86/relative_patcher_x86.cc diff --git a/compiler/linker/x86/relative_patcher_x86.h b/dex2oat/linker/x86/relative_patcher_x86.h similarity index 88% rename from compiler/linker/x86/relative_patcher_x86.h rename to dex2oat/linker/x86/relative_patcher_x86.h index 63a8338722304dce214afda3571ce62a4c994cb4..e723580dae9f6051d9d81f753f91c22230845731 100644 --- a/compiler/linker/x86/relative_patcher_x86.h +++ b/dex2oat/linker/x86/relative_patcher_x86.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_LINKER_X86_RELATIVE_PATCHER_X86_H_ -#define ART_COMPILER_LINKER_X86_RELATIVE_PATCHER_X86_H_ +#ifndef ART_DEX2OAT_LINKER_X86_RELATIVE_PATCHER_X86_H_ +#define ART_DEX2OAT_LINKER_X86_RELATIVE_PATCHER_X86_H_ #include "linker/x86/relative_patcher_x86_base.h" @@ -38,4 +38,4 @@ class X86RelativePatcher FINAL : public X86BaseRelativePatcher { } // namespace linker } // namespace art -#endif // ART_COMPILER_LINKER_X86_RELATIVE_PATCHER_X86_H_ +#endif // ART_DEX2OAT_LINKER_X86_RELATIVE_PATCHER_X86_H_ diff --git a/compiler/linker/x86/relative_patcher_x86_base.cc b/dex2oat/linker/x86/relative_patcher_x86_base.cc similarity index 100% rename from compiler/linker/x86/relative_patcher_x86_base.cc rename to dex2oat/linker/x86/relative_patcher_x86_base.cc diff --git a/compiler/linker/x86/relative_patcher_x86_base.h b/dex2oat/linker/x86/relative_patcher_x86_base.h similarity index 90% rename from compiler/linker/x86/relative_patcher_x86_base.h rename to dex2oat/linker/x86/relative_patcher_x86_base.h index 6097345657d0fd66ed4c6adcbc0e258a6785b8d9..4cc7b07d2daeaf95f680627ce4292b63d03dd2e2 100644 --- a/compiler/linker/x86/relative_patcher_x86_base.h +++ b/dex2oat/linker/x86/relative_patcher_x86_base.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_LINKER_X86_RELATIVE_PATCHER_X86_BASE_H_ -#define ART_COMPILER_LINKER_X86_RELATIVE_PATCHER_X86_BASE_H_ +#ifndef ART_DEX2OAT_LINKER_X86_RELATIVE_PATCHER_X86_BASE_H_ +#define ART_DEX2OAT_LINKER_X86_RELATIVE_PATCHER_X86_BASE_H_ #include "linker/relative_patcher.h" @@ -50,4 +50,4 @@ class X86BaseRelativePatcher : public RelativePatcher { } // namespace linker } // namespace art -#endif // ART_COMPILER_LINKER_X86_RELATIVE_PATCHER_X86_BASE_H_ +#endif // ART_DEX2OAT_LINKER_X86_RELATIVE_PATCHER_X86_BASE_H_ diff --git a/compiler/linker/x86/relative_patcher_x86_test.cc b/dex2oat/linker/x86/relative_patcher_x86_test.cc similarity index 100% rename from compiler/linker/x86/relative_patcher_x86_test.cc rename to dex2oat/linker/x86/relative_patcher_x86_test.cc diff --git a/compiler/linker/x86_64/relative_patcher_x86_64.cc b/dex2oat/linker/x86_64/relative_patcher_x86_64.cc similarity index 100% rename from compiler/linker/x86_64/relative_patcher_x86_64.cc rename to dex2oat/linker/x86_64/relative_patcher_x86_64.cc diff --git a/compiler/linker/x86_64/relative_patcher_x86_64.h b/dex2oat/linker/x86_64/relative_patcher_x86_64.h similarity index 87% rename from compiler/linker/x86_64/relative_patcher_x86_64.h rename to dex2oat/linker/x86_64/relative_patcher_x86_64.h index 4f3ec498cb8c041071e42e99c9ce379fd6b58406..a31e1ebfbb4f53b3b92876cc790e18f79a9200e3 100644 --- a/compiler/linker/x86_64/relative_patcher_x86_64.h +++ b/dex2oat/linker/x86_64/relative_patcher_x86_64.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_LINKER_X86_64_RELATIVE_PATCHER_X86_64_H_ -#define ART_COMPILER_LINKER_X86_64_RELATIVE_PATCHER_X86_64_H_ +#ifndef ART_DEX2OAT_LINKER_X86_64_RELATIVE_PATCHER_X86_64_H_ +#define ART_DEX2OAT_LINKER_X86_64_RELATIVE_PATCHER_X86_64_H_ #include "linker/x86/relative_patcher_x86_base.h" @@ -38,4 +38,4 @@ class X86_64RelativePatcher FINAL : public X86BaseRelativePatcher { } // namespace linker } // namespace art -#endif // ART_COMPILER_LINKER_X86_64_RELATIVE_PATCHER_X86_64_H_ +#endif // ART_DEX2OAT_LINKER_X86_64_RELATIVE_PATCHER_X86_64_H_ diff --git a/compiler/linker/x86_64/relative_patcher_x86_64_test.cc b/dex2oat/linker/x86_64/relative_patcher_x86_64_test.cc similarity index 100% rename from compiler/linker/x86_64/relative_patcher_x86_64_test.cc rename to dex2oat/linker/x86_64/relative_patcher_x86_64_test.cc diff --git a/dexdump/Android.bp b/dexdump/Android.bp index c63d6c319e3527bf03175a5e1208675c4ab70e68..ac9a9a2932038cbc0f20669f43c2c853dcb7af80 100644 --- a/dexdump/Android.bp +++ b/dexdump/Android.bp @@ -23,10 +23,6 @@ cc_defaults { "dexdump.cc", ], cflags: ["-Wall", "-Werror"], - // TODO: fix b/72216369 and remove the need for this. - include_dirs: [ - "art/runtime" // dex utils. - ], } art_cc_binary { @@ -35,6 +31,7 @@ art_cc_binary { host_supported: true, shared_libs: [ "libdexfile", + "libartbase", "libbase", ], } @@ -46,6 +43,7 @@ art_cc_binary { device_supported: false, static_libs: [ "libdexfile", + "libartbase", ] + art_static_dependencies, target: { darwin: { diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index 9536381ed0404cdb23e5120de5c83109c8be0826..82610353b4719905e1e1aef384b5506d90022fec 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -45,6 +45,7 @@ #include "android-base/logging.h" #include "android-base/stringprintf.h" +#include "dex/class_accessor-inl.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_exception_helpers.h" @@ -611,19 +612,11 @@ static void dumpClassDef(const DexFile* pDexFile, int idx) { pClassDef.class_data_off_, pClassDef.class_data_off_); // Fields and methods. - const u1* pEncodedData = pDexFile->GetClassData(pClassDef); - if (pEncodedData != nullptr) { - ClassDataItemIterator pClassData(*pDexFile, pEncodedData); - fprintf(gOutFile, "static_fields_size : %d\n", pClassData.NumStaticFields()); - fprintf(gOutFile, "instance_fields_size: %d\n", pClassData.NumInstanceFields()); - fprintf(gOutFile, "direct_methods_size : %d\n", pClassData.NumDirectMethods()); - fprintf(gOutFile, "virtual_methods_size: %d\n", pClassData.NumVirtualMethods()); - } else { - fprintf(gOutFile, "static_fields_size : 0\n"); - fprintf(gOutFile, "instance_fields_size: 0\n"); - fprintf(gOutFile, "direct_methods_size : 0\n"); - fprintf(gOutFile, "virtual_methods_size: 0\n"); - } + ClassAccessor accessor(*pDexFile, 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()); + fprintf(gOutFile, "virtual_methods_size: %d\n", accessor.NumVirtualMethods()); fprintf(gOutFile, "\n"); } @@ -786,11 +779,10 @@ static void dumpLocalsCb(void* /*context*/, const DexFile::LocalInfo& entry) { static std::unique_ptr indexString(const DexFile* pDexFile, const Instruction* pDecInsn, size_t bufSize) { - static const u4 kInvalidIndex = std::numeric_limits::max(); std::unique_ptr buf(new char[bufSize]); // Determine index and width of the string. u4 index = 0; - u4 secondary_index = kInvalidIndex; + u2 secondary_index = 0; u4 width = 4; switch (Instruction::FormatOf(pDecInsn->Opcode())) { // SOME NOT SUPPORTED: @@ -898,7 +890,7 @@ static std::unique_ptr indexString(const DexFile* pDexFile, signature.ToString().c_str()); } if (secondary_index < pDexFile->GetHeader().proto_ids_size_) { - const DexFile::ProtoId& protoId = pDexFile->GetProtoId(secondary_index); + const DexFile::ProtoId& protoId = pDexFile->GetProtoId(dex::ProtoIndex(secondary_index)); const Signature signature = pDexFile->GetProtoSignature(protoId); proto = signature.ToString(); } @@ -916,7 +908,7 @@ static std::unique_ptr indexString(const DexFile* pDexFile, break; case Instruction::kIndexProtoRef: if (index < pDexFile->GetHeader().proto_ids_size_) { - const DexFile::ProtoId& protoId = pDexFile->GetProtoId(index); + const DexFile::ProtoId& protoId = pDexFile->GetProtoId(dex::ProtoIndex(index)); const Signature signature = pDexFile->GetProtoSignature(protoId); const std::string& proto = signature.ToString(); outSize = snprintf(buf.get(), bufSize, "%s // proto@%0*x", proto.c_str(), width, index); @@ -1705,7 +1697,7 @@ static void dumpCallSite(const DexFile* pDexFile, u4 idx) { dex::StringIndex method_name_idx = static_cast(it.GetJavaValue().i); const char* method_name = pDexFile->StringDataByIdx(method_name_idx); it.Next(); - uint32_t method_type_idx = static_cast(it.GetJavaValue().i); + dex::ProtoIndex method_type_idx = static_cast(it.GetJavaValue().i); const DexFile::ProtoId& method_type_id = pDexFile->GetProtoId(method_type_idx); std::string method_type = pDexFile->GetProtoSignature(method_type_id).ToString(); it.Next(); @@ -1763,7 +1755,7 @@ static void dumpCallSite(const DexFile* pDexFile, u4 idx) { break; case EncodedArrayValueIterator::ValueType::kMethodType: { type = "MethodType"; - uint32_t proto_idx = static_cast(it.GetJavaValue().i); + dex::ProtoIndex proto_idx = static_cast(it.GetJavaValue().i); const DexFile::ProtoId& proto_id = pDexFile->GetProtoId(proto_idx); value = pDexFile->GetProtoSignature(proto_id).ToString(); break; @@ -1781,9 +1773,8 @@ static void dumpCallSite(const DexFile* pDexFile, u4 idx) { case EncodedArrayValueIterator::ValueType::kType: { type = "Class"; dex::TypeIndex type_idx = static_cast(it.GetJavaValue().i); - const DexFile::ClassDef* class_def = pDexFile->FindClassDef(type_idx); - value = pDexFile->GetClassDescriptor(*class_def); - value = descriptorClassToDot(value.c_str()).get(); + const DexFile::TypeId& type_id = pDexFile->GetTypeId(type_idx); + value = pDexFile->GetTypeDescriptor(type_id); break; } case EncodedArrayValueIterator::ValueType::kField: diff --git a/dexdump/dexdump_cfg.cc b/dexdump/dexdump_cfg.cc index 69ee0682a303a293724076d7e1348528df02cf8a..7e534ed359c9b302188c6e4325f1d9e28fa4ca9b 100644 --- a/dexdump/dexdump_cfg.cc +++ b/dexdump/dexdump_cfg.cc @@ -120,7 +120,7 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, os << inst_str.substr(cur_start, next_escape - cur_start); // Escape all necessary characters. while (next_escape < inst_str.size()) { - char c = inst_str.at(next_escape); + char c = inst_str[next_escape]; if (c == '"' || c == '{' || c == '}' || c == '<' || c == '>') { os << '\\' << c; } else { diff --git a/dexdump/dexdump_test.cc b/dexdump/dexdump_test.cc index 63e0e3f20de6e2385353fc9f0d553c13f87406c9..3a2d38dec63857b5df5166cad2a5ceeb471f479b 100644 --- a/dexdump/dexdump_test.cc +++ b/dexdump/dexdump_test.cc @@ -58,7 +58,7 @@ TEST_F(DexDumpTest, NoInputFileGiven) { TEST_F(DexDumpTest, CantOpenOutput) { std::string error_msg; - ASSERT_FALSE(Exec({"-o", "/joho", dex_file_}, &error_msg)) << error_msg; + ASSERT_FALSE(Exec({"-o", "/non/existent/path", dex_file_}, &error_msg)) << error_msg; } TEST_F(DexDumpTest, BadFlagCombination) { diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp index 33ba58f5f76db599effa2d3937dc6a192d2125e5..147af0c6a963cc09d8dfbfaee359c7790ea005bc 100644 --- a/dexlayout/Android.bp +++ b/dexlayout/Android.bp @@ -39,8 +39,9 @@ art_cc_library { "dex2oat-pgo-defaults", ], shared_libs: [ - "libart", "libdexfile", + "libartbase", + "libprofile", ], target: { @@ -59,8 +60,9 @@ art_cc_library { "art_debug_defaults", ], shared_libs: [ - "libartd", "libdexfiled", + "libartbased", + "libprofiled", ], } @@ -68,7 +70,6 @@ cc_defaults { name: "dexlayout-defaults", defaults: ["art_defaults"], host_supported: true, - srcs: ["dexlayout_main.cc"], shared_libs: [ "libbase", ], @@ -77,8 +78,11 @@ cc_defaults { art_cc_binary { name: "dexlayout", defaults: ["dexlayout-defaults"], + srcs: ["dexlayout_main.cc"], shared_libs: [ - "libart", + "libdexfile", + "libprofile", + "libartbase", "libart-dexlayout", ], } @@ -89,8 +93,11 @@ art_cc_binary { "art_debug_defaults", "dexlayout-defaults", ], + srcs: ["dexlayout_main.cc"], shared_libs: [ - "libartd", + "libdexfiled", + "libprofiled", + "libartbased", "libartd-dexlayout", ], } @@ -98,7 +105,10 @@ art_cc_binary { art_cc_test { name: "art_dexlayout_tests", defaults: ["art_gtest_defaults"], - shared_libs: ["libart-dexlayout"], + shared_libs: [ + "libprofiled", + "libartd-dexlayout", + ], srcs: ["dexlayout_test.cc"], } @@ -110,6 +120,8 @@ art_cc_binary { cflags: ["-Wall"], shared_libs: [ "libart", + "libdexfile", + "libartbase", "libart-dexlayout", "libbase", ], diff --git a/dexlayout/compact_dex_writer.cc b/dexlayout/compact_dex_writer.cc index 2b4144c6117ce8f804f90dc788a55bdcfb07706a..00fb0af710d8b65e7150da4378091f4998eec1cd 100644 --- a/dexlayout/compact_dex_writer.cc +++ b/dexlayout/compact_dex_writer.cc @@ -40,9 +40,8 @@ CompactDexWriter::Container::Container(bool dedupe_code_items) uint32_t CompactDexWriter::WriteDebugInfoOffsetTable(Stream* stream) { const uint32_t start_offset = stream->Tell(); - const dex_ir::Collections& collections = header_->GetCollections(); // Debug offsets for method indexes. 0 means no debug info. - std::vector debug_info_offsets(collections.MethodIdsSize(), 0u); + std::vector debug_info_offsets(header_->MethodIds().Size(), 0u); static constexpr InvokeType invoke_types[] = { kDirect, @@ -50,7 +49,7 @@ uint32_t CompactDexWriter::WriteDebugInfoOffsetTable(Stream* stream) { }; for (InvokeType invoke_type : invoke_types) { - for (const std::unique_ptr& class_def : collections.ClassDefs()) { + for (auto& class_def : header_->ClassDefs()) { // Skip classes that are not defined in this dex file. dex_ir::ClassData* class_data = class_def->GetClassData(); if (class_data == nullptr) { @@ -59,8 +58,8 @@ uint32_t CompactDexWriter::WriteDebugInfoOffsetTable(Stream* stream) { for (auto& method : *(invoke_type == InvokeType::kDirect ? class_data->DirectMethods() : class_data->VirtualMethods())) { - const dex_ir::MethodId* method_id = method->GetMethodId(); - dex_ir::CodeItem* code_item = method->GetCodeItem(); + const dex_ir::MethodId* method_id = method.GetMethodId(); + dex_ir::CodeItem* code_item = method.GetCodeItem(); if (code_item != nullptr && code_item->DebugInfo() != nullptr) { const uint32_t debug_info_offset = code_item->DebugInfo()->GetOffset(); const uint32_t method_idx = method_id->GetIndex(); @@ -232,14 +231,13 @@ uint32_t CompactDexWriter::Deduper::Dedupe(uint32_t data_start, } void CompactDexWriter::SortDebugInfosByMethodIndex() { - dex_ir::Collections& collections = header_->GetCollections(); static constexpr InvokeType invoke_types[] = { kDirect, kVirtual }; std::map method_idx_map; for (InvokeType invoke_type : invoke_types) { - for (std::unique_ptr& class_def : collections.ClassDefs()) { + for (auto& class_def : header_->ClassDefs()) { // Skip classes that are not defined in this dex file. dex_ir::ClassData* class_data = class_def->GetClassData(); if (class_data == nullptr) { @@ -248,8 +246,8 @@ void CompactDexWriter::SortDebugInfosByMethodIndex() { for (auto& method : *(invoke_type == InvokeType::kDirect ? class_data->DirectMethods() : class_data->VirtualMethods())) { - const dex_ir::MethodId* method_id = method->GetMethodId(); - dex_ir::CodeItem* code_item = method->GetCodeItem(); + const dex_ir::MethodId* method_id = method.GetMethodId(); + dex_ir::CodeItem* code_item = method.GetCodeItem(); if (code_item != nullptr && code_item->DebugInfo() != nullptr) { const dex_ir::DebugInfoItem* debug_item = code_item->DebugInfo(); method_idx_map.insert(std::make_pair(debug_item, method_id->GetIndex())); @@ -257,8 +255,8 @@ void CompactDexWriter::SortDebugInfosByMethodIndex() { } } } - std::sort(collections.DebugInfoItems().begin(), - collections.DebugInfoItems().end(), + std::sort(header_->DebugInfoItems().begin(), + header_->DebugInfoItems().end(), [&](const std::unique_ptr& a, const std::unique_ptr& b) { auto it_a = method_idx_map.find(a.get()); @@ -282,20 +280,19 @@ void CompactDexWriter::WriteHeader(Stream* stream) { header.endian_tag_ = header_->EndianTag(); header.link_size_ = header_->LinkSize(); header.link_off_ = header_->LinkOffset(); - const dex_ir::Collections& collections = header_->GetCollections(); - header.map_off_ = collections.MapListOffset(); - header.string_ids_size_ = collections.StringIdsSize(); - header.string_ids_off_ = collections.StringIdsOffset(); - header.type_ids_size_ = collections.TypeIdsSize(); - header.type_ids_off_ = collections.TypeIdsOffset(); - header.proto_ids_size_ = collections.ProtoIdsSize(); - header.proto_ids_off_ = collections.ProtoIdsOffset(); - header.field_ids_size_ = collections.FieldIdsSize(); - header.field_ids_off_ = collections.FieldIdsOffset(); - header.method_ids_size_ = collections.MethodIdsSize(); - header.method_ids_off_ = collections.MethodIdsOffset(); - header.class_defs_size_ = collections.ClassDefsSize(); - header.class_defs_off_ = collections.ClassDefsOffset(); + header.map_off_ = header_->MapListOffset(); + header.string_ids_size_ = header_->StringIds().Size(); + header.string_ids_off_ = header_->StringIds().GetOffset(); + header.type_ids_size_ = header_->TypeIds().Size(); + header.type_ids_off_ = header_->TypeIds().GetOffset(); + header.proto_ids_size_ = header_->ProtoIds().Size(); + header.proto_ids_off_ = header_->ProtoIds().GetOffset(); + header.field_ids_size_ = header_->FieldIds().Size(); + header.field_ids_off_ = header_->FieldIds().GetOffset(); + header.method_ids_size_ = header_->MethodIds().Size(); + header.method_ids_off_ = header_->MethodIds().GetOffset(); + header.class_defs_size_ = header_->ClassDefs().Size(); + header.class_defs_off_ = header_->ClassDefs().GetOffset(); header.data_size_ = header_->DataSize(); header.data_off_ = header_->DataOffset(); header.owned_data_begin_ = owned_data_begin_; @@ -332,16 +329,15 @@ void CompactDexWriter::WriteStringData(Stream* stream, dex_ir::StringData* strin } bool CompactDexWriter::CanGenerateCompactDex(std::string* error_msg) { - dex_ir::Collections& collections = header_->GetCollections(); static constexpr InvokeType invoke_types[] = { kDirect, kVirtual }; - std::vector saw_method_id(collections.MethodIdsSize(), false); - std::vector method_id_code_item(collections.MethodIdsSize(), nullptr); - std::vector method_id_debug_info(collections.MethodIdsSize(), nullptr); + std::vector saw_method_id(header_->MethodIds().Size(), false); + std::vector method_id_code_item(header_->MethodIds().Size(), nullptr); + std::vector method_id_debug_info(header_->MethodIds().Size(), nullptr); for (InvokeType invoke_type : invoke_types) { - for (std::unique_ptr& class_def : collections.ClassDefs()) { + for (auto& class_def : header_->ClassDefs()) { // Skip classes that are not defined in this dex file. dex_ir::ClassData* class_data = class_def->GetClassData(); if (class_data == nullptr) { @@ -350,8 +346,8 @@ bool CompactDexWriter::CanGenerateCompactDex(std::string* error_msg) { for (auto& method : *(invoke_type == InvokeType::kDirect ? class_data->DirectMethods() : class_data->VirtualMethods())) { - const uint32_t idx = method->GetMethodId()->GetIndex(); - dex_ir::CodeItem* code_item = method->GetCodeItem(); + const uint32_t idx = method.GetMethodId()->GetIndex(); + dex_ir::CodeItem* code_item = method.GetCodeItem(); dex_ir:: DebugInfoItem* debug_info_item = nullptr; if (code_item != nullptr) { debug_info_item = code_item->DebugInfo(); @@ -407,8 +403,6 @@ bool CompactDexWriter::Write(DexContainer* output, std::string* error_msg) { // Starting offset is right after the header. main_stream->Seek(GetHeaderSize()); - dex_ir::Collections& collection = header_->GetCollections(); - // Based on: https://source.android.com/devices/tech/dalvik/dex-format // Since the offsets may not be calculated already, the writing must be done in the correct order. const uint32_t string_ids_offset = main_stream->Tell(); @@ -469,16 +463,16 @@ bool CompactDexWriter::Write(DexContainer* output, std::string* error_msg) { // Write the map list. if (compute_offsets_) { data_stream->AlignTo(SectionAlignment(DexFile::kDexTypeMapList)); - collection.SetMapListOffset(data_stream->Tell()); + header_->SetMapListOffset(data_stream->Tell()); } else { - data_stream->Seek(collection.MapListOffset()); + data_stream->Seek(header_->MapListOffset()); } // Map items are included in the data section. GenerateAndWriteMapItems(data_stream); // Write link data if it exists. - const std::vector& link_data = collection.LinkData(); + const std::vector& link_data = header_->LinkData(); if (link_data.size() > 0) { CHECK_EQ(header_->LinkSize(), static_cast(link_data.size())); if (compute_offsets_) { diff --git a/dexlayout/compact_dex_writer.h b/dexlayout/compact_dex_writer.h index 4b142a85bbd1ef26f8a4b75d4274be8bd88020e3..e7d5ed953d40d21cf416de112874d87c76828113 100644 --- a/dexlayout/compact_dex_writer.h +++ b/dexlayout/compact_dex_writer.h @@ -22,7 +22,7 @@ #include // For unique_ptr #include -#include "base/utils.h" +#include "base/data_hash.h" #include "dex_writer.h" namespace art { diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc index 1525d537b7281f963117636cfcdc7fbfda26405a..3917847ea743fec9bbb1521057c5b47aab74c542 100644 --- a/dexlayout/dex_ir.cc +++ b/dexlayout/dex_ir.cc @@ -30,825 +30,11 @@ namespace art { namespace dex_ir { -static uint64_t ReadVarWidth(const uint8_t** data, uint8_t length, bool sign_extend) { - uint64_t value = 0; - for (uint32_t i = 0; i <= length; i++) { - value |= static_cast(*(*data)++) << (i * 8); - } - if (sign_extend) { - int shift = (7 - length) * 8; - return (static_cast(value) << shift) >> shift; - } - return value; -} - -static uint32_t GetDebugInfoStreamSize(const uint8_t* debug_info_stream) { - const uint8_t* stream = debug_info_stream; - DecodeUnsignedLeb128(&stream); // line_start - uint32_t parameters_size = DecodeUnsignedLeb128(&stream); - for (uint32_t i = 0; i < parameters_size; ++i) { - DecodeUnsignedLeb128P1(&stream); // Parameter name. - } - - for (;;) { - uint8_t opcode = *stream++; - switch (opcode) { - case DexFile::DBG_END_SEQUENCE: - return stream - debug_info_stream; // end of stream. - case DexFile::DBG_ADVANCE_PC: - DecodeUnsignedLeb128(&stream); // addr_diff - break; - case DexFile::DBG_ADVANCE_LINE: - DecodeSignedLeb128(&stream); // line_diff - break; - case DexFile::DBG_START_LOCAL: - DecodeUnsignedLeb128(&stream); // register_num - DecodeUnsignedLeb128P1(&stream); // name_idx - DecodeUnsignedLeb128P1(&stream); // type_idx - break; - case DexFile::DBG_START_LOCAL_EXTENDED: - DecodeUnsignedLeb128(&stream); // register_num - DecodeUnsignedLeb128P1(&stream); // name_idx - DecodeUnsignedLeb128P1(&stream); // type_idx - DecodeUnsignedLeb128P1(&stream); // sig_idx - break; - case DexFile::DBG_END_LOCAL: - case DexFile::DBG_RESTART_LOCAL: - DecodeUnsignedLeb128(&stream); // register_num - break; - case DexFile::DBG_SET_PROLOGUE_END: - case DexFile::DBG_SET_EPILOGUE_BEGIN: - break; - case DexFile::DBG_SET_FILE: { - DecodeUnsignedLeb128P1(&stream); // name_idx - break; - } - default: { - break; - } - } - } -} - -static bool GetIdFromInstruction(Collections& collections, - const Instruction* dec_insn, - std::vector* type_ids, - std::vector* string_ids, - std::vector* method_ids, - std::vector* field_ids) { - // Determine index and width of the string. - uint32_t index = 0; - switch (Instruction::FormatOf(dec_insn->Opcode())) { - // SOME NOT SUPPORTED: - // case Instruction::k20bc: - case Instruction::k21c: - case Instruction::k35c: - // case Instruction::k35ms: - case Instruction::k3rc: - // case Instruction::k3rms: - // case Instruction::k35mi: - // case Instruction::k3rmi: - case Instruction::k45cc: - case Instruction::k4rcc: - index = dec_insn->VRegB(); - break; - case Instruction::k31c: - index = dec_insn->VRegB(); - break; - case Instruction::k22c: - // case Instruction::k22cs: - index = dec_insn->VRegC(); - break; - default: - break; - } // switch - - // Determine index type, and add reference to the appropriate collection. - switch (Instruction::IndexTypeOf(dec_insn->Opcode())) { - case Instruction::kIndexTypeRef: - if (index < collections.TypeIdsSize()) { - type_ids->push_back(collections.GetTypeId(index)); - return true; - } - break; - case Instruction::kIndexStringRef: - if (index < collections.StringIdsSize()) { - string_ids->push_back(collections.GetStringId(index)); - return true; - } - break; - case Instruction::kIndexMethodRef: - case Instruction::kIndexMethodAndProtoRef: - if (index < collections.MethodIdsSize()) { - method_ids->push_back(collections.GetMethodId(index)); - return true; - } - break; - case Instruction::kIndexFieldRef: - if (index < collections.FieldIdsSize()) { - field_ids->push_back(collections.GetFieldId(index)); - return true; - } - break; - case Instruction::kIndexUnknown: - case Instruction::kIndexNone: - case Instruction::kIndexVtableOffset: - case Instruction::kIndexFieldOffset: - default: - break; - } // switch - return false; -} - -/* - * Get all the types, strings, methods, and fields referred to from bytecode. - */ -static bool GetIdsFromByteCode(Collections& collections, - const CodeItem* code, - std::vector* type_ids, - std::vector* string_ids, - std::vector* method_ids, - std::vector* field_ids) { - bool has_id = false; - IterationRange instructions = code->Instructions(); - SafeDexInstructionIterator it(instructions.begin(), instructions.end()); - for (; !it.IsErrorState() && it < instructions.end(); ++it) { - // In case the instruction goes past the end of the code item, make sure to not process it. - SafeDexInstructionIterator next = it; - ++next; - if (next.IsErrorState()) { - break; - } - has_id |= GetIdFromInstruction(collections, - &it.Inst(), - type_ids, - string_ids, - method_ids, - field_ids); - } // for - return has_id; -} - -EncodedValue* Collections::ReadEncodedValue(const DexFile& dex_file, const uint8_t** data) { - const uint8_t encoded_value = *(*data)++; - const uint8_t type = encoded_value & 0x1f; - EncodedValue* item = new EncodedValue(type); - ReadEncodedValue(dex_file, data, type, encoded_value >> 5, item); - return item; -} - -EncodedValue* Collections::ReadEncodedValue(const DexFile& dex_file, - const uint8_t** data, - uint8_t type, - uint8_t length) { - EncodedValue* item = new EncodedValue(type); - ReadEncodedValue(dex_file, data, type, length, item); - return item; -} - -void Collections::ReadEncodedValue(const DexFile& dex_file, - const uint8_t** data, - uint8_t type, - uint8_t length, - EncodedValue* item) { - switch (type) { - case DexFile::kDexAnnotationByte: - item->SetByte(static_cast(ReadVarWidth(data, length, false))); - break; - case DexFile::kDexAnnotationShort: - item->SetShort(static_cast(ReadVarWidth(data, length, true))); - break; - case DexFile::kDexAnnotationChar: - item->SetChar(static_cast(ReadVarWidth(data, length, false))); - break; - case DexFile::kDexAnnotationInt: - item->SetInt(static_cast(ReadVarWidth(data, length, true))); - break; - case DexFile::kDexAnnotationLong: - item->SetLong(static_cast(ReadVarWidth(data, length, true))); - break; - case DexFile::kDexAnnotationFloat: { - // Fill on right. - union { - float f; - uint32_t data; - } conv; - conv.data = static_cast(ReadVarWidth(data, length, false)) << (3 - length) * 8; - item->SetFloat(conv.f); - break; - } - case DexFile::kDexAnnotationDouble: { - // Fill on right. - union { - double d; - uint64_t data; - } conv; - conv.data = ReadVarWidth(data, length, false) << (7 - length) * 8; - item->SetDouble(conv.d); - break; - } - case DexFile::kDexAnnotationMethodType: { - const uint32_t proto_index = static_cast(ReadVarWidth(data, length, false)); - item->SetProtoId(GetProtoId(proto_index)); - break; - } - case DexFile::kDexAnnotationMethodHandle: { - const uint32_t method_handle_index = static_cast(ReadVarWidth(data, length, false)); - item->SetMethodHandle(GetMethodHandle(method_handle_index)); - break; - } - case DexFile::kDexAnnotationString: { - const uint32_t string_index = static_cast(ReadVarWidth(data, length, false)); - item->SetStringId(GetStringId(string_index)); - break; - } - case DexFile::kDexAnnotationType: { - const uint32_t string_index = static_cast(ReadVarWidth(data, length, false)); - item->SetTypeId(GetTypeId(string_index)); - break; - } - case DexFile::kDexAnnotationField: - case DexFile::kDexAnnotationEnum: { - const uint32_t field_index = static_cast(ReadVarWidth(data, length, false)); - item->SetFieldId(GetFieldId(field_index)); - break; - } - case DexFile::kDexAnnotationMethod: { - const uint32_t method_index = static_cast(ReadVarWidth(data, length, false)); - item->SetMethodId(GetMethodId(method_index)); - break; - } - case DexFile::kDexAnnotationArray: { - EncodedValueVector* values = new EncodedValueVector(); - const uint32_t offset = *data - dex_file.DataBegin(); - const uint32_t size = DecodeUnsignedLeb128(data); - // Decode all elements. - for (uint32_t i = 0; i < size; i++) { - values->push_back(std::unique_ptr(ReadEncodedValue(dex_file, data))); - } - EncodedArrayItem* array_item = new EncodedArrayItem(values); - if (eagerly_assign_offsets_) { - array_item->SetOffset(offset); - } - item->SetEncodedArray(array_item); - break; - } - case DexFile::kDexAnnotationAnnotation: { - AnnotationElementVector* elements = new AnnotationElementVector(); - const uint32_t type_idx = DecodeUnsignedLeb128(data); - const uint32_t size = DecodeUnsignedLeb128(data); - // Decode all name=value pairs. - for (uint32_t i = 0; i < size; i++) { - const uint32_t name_index = DecodeUnsignedLeb128(data); - elements->push_back(std::unique_ptr( - new AnnotationElement(GetStringId(name_index), ReadEncodedValue(dex_file, data)))); - } - item->SetEncodedAnnotation(new EncodedAnnotation(GetTypeId(type_idx), elements)); - break; - } - case DexFile::kDexAnnotationNull: - break; - case DexFile::kDexAnnotationBoolean: - item->SetBoolean(length != 0); - break; - default: - break; - } -} - -void Collections::CreateStringId(const DexFile& dex_file, uint32_t i) { - const DexFile::StringId& disk_string_id = dex_file.GetStringId(dex::StringIndex(i)); - StringData* string_data = new StringData(dex_file.GetStringData(disk_string_id)); - AddItem(string_datas_map_, string_datas_, string_data, disk_string_id.string_data_off_); - - StringId* string_id = new StringId(string_data); - AddIndexedItem(string_ids_, string_id, StringIdsOffset() + i * StringId::ItemSize(), i); -} - -void Collections::CreateTypeId(const DexFile& dex_file, uint32_t i) { - const DexFile::TypeId& disk_type_id = dex_file.GetTypeId(dex::TypeIndex(i)); - TypeId* type_id = new TypeId(GetStringId(disk_type_id.descriptor_idx_.index_)); - AddIndexedItem(type_ids_, type_id, TypeIdsOffset() + i * TypeId::ItemSize(), i); -} - -void Collections::CreateProtoId(const DexFile& dex_file, uint32_t i) { - const DexFile::ProtoId& disk_proto_id = dex_file.GetProtoId(i); - const DexFile::TypeList* type_list = dex_file.GetProtoParameters(disk_proto_id); - TypeList* parameter_type_list = CreateTypeList(type_list, disk_proto_id.parameters_off_); - - ProtoId* proto_id = new ProtoId(GetStringId(disk_proto_id.shorty_idx_.index_), - GetTypeId(disk_proto_id.return_type_idx_.index_), - parameter_type_list); - AddIndexedItem(proto_ids_, proto_id, ProtoIdsOffset() + i * ProtoId::ItemSize(), i); -} - -void Collections::CreateFieldId(const DexFile& dex_file, uint32_t i) { - const DexFile::FieldId& disk_field_id = dex_file.GetFieldId(i); - FieldId* field_id = new FieldId(GetTypeId(disk_field_id.class_idx_.index_), - GetTypeId(disk_field_id.type_idx_.index_), - GetStringId(disk_field_id.name_idx_.index_)); - AddIndexedItem(field_ids_, field_id, FieldIdsOffset() + i * FieldId::ItemSize(), i); -} - -void Collections::CreateMethodId(const DexFile& dex_file, uint32_t i) { - const DexFile::MethodId& disk_method_id = dex_file.GetMethodId(i); - MethodId* method_id = new MethodId(GetTypeId(disk_method_id.class_idx_.index_), - GetProtoId(disk_method_id.proto_idx_), - GetStringId(disk_method_id.name_idx_.index_)); - AddIndexedItem(method_ids_, method_id, MethodIdsOffset() + i * MethodId::ItemSize(), i); -} - -void Collections::CreateClassDef(const DexFile& dex_file, uint32_t i) { - const DexFile::ClassDef& disk_class_def = dex_file.GetClassDef(i); - const TypeId* class_type = GetTypeId(disk_class_def.class_idx_.index_); - uint32_t access_flags = disk_class_def.access_flags_; - const TypeId* superclass = GetTypeIdOrNullPtr(disk_class_def.superclass_idx_.index_); - - const DexFile::TypeList* type_list = dex_file.GetInterfacesList(disk_class_def); - TypeList* interfaces_type_list = CreateTypeList(type_list, disk_class_def.interfaces_off_); - - const StringId* source_file = GetStringIdOrNullPtr(disk_class_def.source_file_idx_.index_); - // Annotations. - AnnotationsDirectoryItem* annotations = nullptr; - const DexFile::AnnotationsDirectoryItem* disk_annotations_directory_item = - dex_file.GetAnnotationsDirectory(disk_class_def); - if (disk_annotations_directory_item != nullptr) { - annotations = CreateAnnotationsDirectoryItem( - dex_file, disk_annotations_directory_item, disk_class_def.annotations_off_); - } - // Static field initializers. - const uint8_t* static_data = dex_file.GetEncodedStaticFieldValuesArray(disk_class_def); - EncodedArrayItem* static_values = - CreateEncodedArrayItem(dex_file, static_data, disk_class_def.static_values_off_); - ClassData* class_data = CreateClassData( - dex_file, dex_file.GetClassData(disk_class_def), disk_class_def.class_data_off_); - ClassDef* class_def = new ClassDef(class_type, access_flags, superclass, interfaces_type_list, - source_file, annotations, static_values, class_data); - AddIndexedItem(class_defs_, class_def, ClassDefsOffset() + i * ClassDef::ItemSize(), i); -} - -TypeList* Collections::CreateTypeList(const DexFile::TypeList* dex_type_list, uint32_t offset) { - if (dex_type_list == nullptr) { - return nullptr; - } - TypeList* type_list = type_lists_map_.GetExistingObject(offset); - if (type_list == nullptr) { - TypeIdVector* type_vector = new TypeIdVector(); - uint32_t size = dex_type_list->Size(); - for (uint32_t index = 0; index < size; ++index) { - type_vector->push_back(GetTypeId(dex_type_list->GetTypeItem(index).type_idx_.index_)); - } - type_list = new TypeList(type_vector); - AddItem(type_lists_map_, type_lists_, type_list, offset); - } - return type_list; -} - -EncodedArrayItem* Collections::CreateEncodedArrayItem(const DexFile& dex_file, - const uint8_t* static_data, - uint32_t offset) { - if (static_data == nullptr) { - return nullptr; - } - EncodedArrayItem* encoded_array_item = encoded_array_items_map_.GetExistingObject(offset); - if (encoded_array_item == nullptr) { - uint32_t size = DecodeUnsignedLeb128(&static_data); - EncodedValueVector* values = new EncodedValueVector(); - for (uint32_t i = 0; i < size; ++i) { - values->push_back(std::unique_ptr(ReadEncodedValue(dex_file, &static_data))); - } - // TODO: Calculate the size of the encoded array. - encoded_array_item = new EncodedArrayItem(values); - AddItem(encoded_array_items_map_, encoded_array_items_, encoded_array_item, offset); - } - return encoded_array_item; -} - -void Collections::AddAnnotationsFromMapListSection(const DexFile& dex_file, - uint32_t start_offset, - uint32_t count) { - uint32_t current_offset = start_offset; - for (size_t i = 0; i < count; ++i) { - // Annotation that we didn't process already, add it to the set. - const DexFile::AnnotationItem* annotation = dex_file.GetAnnotationItemAtOffset(current_offset); - AnnotationItem* annotation_item = CreateAnnotationItem(dex_file, annotation); - DCHECK(annotation_item != nullptr); - current_offset += annotation_item->GetSize(); - } -} - -AnnotationItem* Collections::CreateAnnotationItem(const DexFile& dex_file, - const DexFile::AnnotationItem* annotation) { - const uint8_t* const start_data = reinterpret_cast(annotation); - const uint32_t offset = start_data - dex_file.DataBegin(); - AnnotationItem* annotation_item = annotation_items_map_.GetExistingObject(offset); - if (annotation_item == nullptr) { - uint8_t visibility = annotation->visibility_; - const uint8_t* annotation_data = annotation->annotation_; - std::unique_ptr encoded_value( - ReadEncodedValue(dex_file, &annotation_data, DexFile::kDexAnnotationAnnotation, 0)); - annotation_item = new AnnotationItem(visibility, encoded_value->ReleaseEncodedAnnotation()); - annotation_item->SetSize(annotation_data - start_data); - AddItem(annotation_items_map_, annotation_items_, annotation_item, offset); - } - return annotation_item; -} - - -AnnotationSetItem* Collections::CreateAnnotationSetItem(const DexFile& dex_file, - const DexFile::AnnotationSetItem* disk_annotations_item, uint32_t offset) { - if (disk_annotations_item == nullptr || (disk_annotations_item->size_ == 0 && offset == 0)) { - return nullptr; - } - AnnotationSetItem* annotation_set_item = annotation_set_items_map_.GetExistingObject(offset); - if (annotation_set_item == nullptr) { - std::vector* items = new std::vector(); - for (uint32_t i = 0; i < disk_annotations_item->size_; ++i) { - const DexFile::AnnotationItem* annotation = - dex_file.GetAnnotationItem(disk_annotations_item, i); - if (annotation == nullptr) { - continue; - } - AnnotationItem* annotation_item = CreateAnnotationItem(dex_file, annotation); - items->push_back(annotation_item); - } - annotation_set_item = new AnnotationSetItem(items); - AddItem(annotation_set_items_map_, annotation_set_items_, annotation_set_item, offset); - } - return annotation_set_item; -} - -AnnotationsDirectoryItem* Collections::CreateAnnotationsDirectoryItem(const DexFile& dex_file, - const DexFile::AnnotationsDirectoryItem* disk_annotations_item, uint32_t offset) { - AnnotationsDirectoryItem* annotations_directory_item = - annotations_directory_items_map_.GetExistingObject(offset); - if (annotations_directory_item != nullptr) { - return annotations_directory_item; - } - const DexFile::AnnotationSetItem* class_set_item = - dex_file.GetClassAnnotationSet(disk_annotations_item); - AnnotationSetItem* class_annotation = nullptr; - if (class_set_item != nullptr) { - uint32_t item_offset = disk_annotations_item->class_annotations_off_; - class_annotation = CreateAnnotationSetItem(dex_file, class_set_item, item_offset); - } - const DexFile::FieldAnnotationsItem* fields = - dex_file.GetFieldAnnotations(disk_annotations_item); - FieldAnnotationVector* field_annotations = nullptr; - if (fields != nullptr) { - field_annotations = new FieldAnnotationVector(); - for (uint32_t i = 0; i < disk_annotations_item->fields_size_; ++i) { - FieldId* field_id = GetFieldId(fields[i].field_idx_); - const DexFile::AnnotationSetItem* field_set_item = - dex_file.GetFieldAnnotationSetItem(fields[i]); - uint32_t annotation_set_offset = fields[i].annotations_off_; - AnnotationSetItem* annotation_set_item = - CreateAnnotationSetItem(dex_file, field_set_item, annotation_set_offset); - field_annotations->push_back(std::unique_ptr( - new FieldAnnotation(field_id, annotation_set_item))); - } - } - const DexFile::MethodAnnotationsItem* methods = - dex_file.GetMethodAnnotations(disk_annotations_item); - MethodAnnotationVector* method_annotations = nullptr; - if (methods != nullptr) { - method_annotations = new MethodAnnotationVector(); - for (uint32_t i = 0; i < disk_annotations_item->methods_size_; ++i) { - MethodId* method_id = GetMethodId(methods[i].method_idx_); - const DexFile::AnnotationSetItem* method_set_item = - dex_file.GetMethodAnnotationSetItem(methods[i]); - uint32_t annotation_set_offset = methods[i].annotations_off_; - AnnotationSetItem* annotation_set_item = - CreateAnnotationSetItem(dex_file, method_set_item, annotation_set_offset); - method_annotations->push_back(std::unique_ptr( - new MethodAnnotation(method_id, annotation_set_item))); - } - } - const DexFile::ParameterAnnotationsItem* parameters = - dex_file.GetParameterAnnotations(disk_annotations_item); - ParameterAnnotationVector* parameter_annotations = nullptr; - if (parameters != nullptr) { - parameter_annotations = new ParameterAnnotationVector(); - for (uint32_t i = 0; i < disk_annotations_item->parameters_size_; ++i) { - MethodId* method_id = GetMethodId(parameters[i].method_idx_); - const DexFile::AnnotationSetRefList* list = - dex_file.GetParameterAnnotationSetRefList(¶meters[i]); - parameter_annotations->push_back(std::unique_ptr( - GenerateParameterAnnotation(dex_file, method_id, list, parameters[i].annotations_off_))); - } - } - // TODO: Calculate the size of the annotations directory. -annotations_directory_item = new AnnotationsDirectoryItem( - class_annotation, field_annotations, method_annotations, parameter_annotations); - AddItem(annotations_directory_items_map_, - annotations_directory_items_, - annotations_directory_item, - offset); - return annotations_directory_item; -} - -ParameterAnnotation* Collections::GenerateParameterAnnotation( - const DexFile& dex_file, MethodId* method_id, - const DexFile::AnnotationSetRefList* annotation_set_ref_list, uint32_t offset) { - AnnotationSetRefList* set_ref_list = annotation_set_ref_lists_map_.GetExistingObject(offset); - if (set_ref_list == nullptr) { - std::vector* annotations = new std::vector(); - for (uint32_t i = 0; i < annotation_set_ref_list->size_; ++i) { - const DexFile::AnnotationSetItem* annotation_set_item = - dex_file.GetSetRefItemItem(&annotation_set_ref_list->list_[i]); - uint32_t set_offset = annotation_set_ref_list->list_[i].annotations_off_; - annotations->push_back(CreateAnnotationSetItem(dex_file, annotation_set_item, set_offset)); - } - set_ref_list = new AnnotationSetRefList(annotations); - AddItem(annotation_set_ref_lists_map_, annotation_set_ref_lists_, set_ref_list, offset); - } - return new ParameterAnnotation(method_id, set_ref_list); -} - -CodeItem* Collections::DedupeOrCreateCodeItem(const DexFile& dex_file, - const DexFile::CodeItem* disk_code_item, - uint32_t offset, - uint32_t dex_method_index) { - if (disk_code_item == nullptr) { - return nullptr; - } - CodeItemDebugInfoAccessor accessor(dex_file, disk_code_item, dex_method_index); - const uint32_t debug_info_offset = accessor.DebugInfoOffset(); - - // Create the offsets pair and dedupe based on it. - std::pair offsets_pair(offset, debug_info_offset); - auto existing = code_items_map_.find(offsets_pair); - if (existing != code_items_map_.end()) { - return existing->second; - } - - const uint8_t* debug_info_stream = dex_file.GetDebugInfoStream(debug_info_offset); - DebugInfoItem* debug_info = nullptr; - if (debug_info_stream != nullptr) { - debug_info = debug_info_items_map_.GetExistingObject(debug_info_offset); - if (debug_info == nullptr) { - uint32_t debug_info_size = GetDebugInfoStreamSize(debug_info_stream); - uint8_t* debug_info_buffer = new uint8_t[debug_info_size]; - memcpy(debug_info_buffer, debug_info_stream, debug_info_size); - debug_info = new DebugInfoItem(debug_info_size, debug_info_buffer); - AddItem(debug_info_items_map_, debug_info_items_, debug_info, debug_info_offset); - } - } - - uint32_t insns_size = accessor.InsnsSizeInCodeUnits(); - uint16_t* insns = new uint16_t[insns_size]; - memcpy(insns, accessor.Insns(), insns_size * sizeof(uint16_t)); - - TryItemVector* tries = nullptr; - CatchHandlerVector* handler_list = nullptr; - if (accessor.TriesSize() > 0) { - tries = new TryItemVector(); - handler_list = new CatchHandlerVector(); - for (const DexFile::TryItem& disk_try_item : accessor.TryItems()) { - uint32_t start_addr = disk_try_item.start_addr_; - uint16_t insn_count = disk_try_item.insn_count_; - uint16_t handler_off = disk_try_item.handler_off_; - const CatchHandler* handlers = nullptr; - for (std::unique_ptr& existing_handlers : *handler_list) { - if (handler_off == existing_handlers->GetListOffset()) { - handlers = existing_handlers.get(); - break; - } - } - if (handlers == nullptr) { - bool catch_all = false; - TypeAddrPairVector* addr_pairs = new TypeAddrPairVector(); - for (CatchHandlerIterator it(accessor, disk_try_item); it.HasNext(); it.Next()) { - const dex::TypeIndex type_index = it.GetHandlerTypeIndex(); - const TypeId* type_id = GetTypeIdOrNullPtr(type_index.index_); - catch_all |= type_id == nullptr; - addr_pairs->push_back(std::unique_ptr( - new TypeAddrPair(type_id, it.GetHandlerAddress()))); - } - handlers = new CatchHandler(catch_all, handler_off, addr_pairs); - handler_list->push_back(std::unique_ptr(handlers)); - } - TryItem* try_item = new TryItem(start_addr, insn_count, handlers); - tries->push_back(std::unique_ptr(try_item)); - } - // Manually walk catch handlers list and add any missing handlers unreferenced by try items. - const uint8_t* handlers_base = accessor.GetCatchHandlerData(); - const uint8_t* handlers_data = handlers_base; - uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_data); - while (handlers_size > handler_list->size()) { - bool already_added = false; - uint16_t handler_off = handlers_data - handlers_base; - for (std::unique_ptr& existing_handlers : *handler_list) { - if (handler_off == existing_handlers->GetListOffset()) { - already_added = true; - break; - } - } - int32_t size = DecodeSignedLeb128(&handlers_data); - bool has_catch_all = size <= 0; - if (has_catch_all) { - size = -size; - } - if (already_added) { - for (int32_t i = 0; i < size; i++) { - DecodeUnsignedLeb128(&handlers_data); - DecodeUnsignedLeb128(&handlers_data); - } - if (has_catch_all) { - DecodeUnsignedLeb128(&handlers_data); - } - continue; - } - TypeAddrPairVector* addr_pairs = new TypeAddrPairVector(); - for (int32_t i = 0; i < size; i++) { - const TypeId* type_id = GetTypeIdOrNullPtr(DecodeUnsignedLeb128(&handlers_data)); - uint32_t addr = DecodeUnsignedLeb128(&handlers_data); - addr_pairs->push_back( - std::unique_ptr(new TypeAddrPair(type_id, addr))); - } - if (has_catch_all) { - uint32_t addr = DecodeUnsignedLeb128(&handlers_data); - addr_pairs->push_back( - std::unique_ptr(new TypeAddrPair(nullptr, addr))); - } - const CatchHandler* handler = new CatchHandler(has_catch_all, handler_off, addr_pairs); - handler_list->push_back(std::unique_ptr(handler)); - } - } - - uint32_t size = dex_file.GetCodeItemSize(*disk_code_item); - CodeItem* code_item = new CodeItem(accessor.RegistersSize(), - accessor.InsSize(), - accessor.OutsSize(), - debug_info, - insns_size, - insns, - tries, - handler_list); - code_item->SetSize(size); - - // Add the code item to the map. - DCHECK(!code_item->OffsetAssigned()); - if (eagerly_assign_offsets_) { - code_item->SetOffset(offset); - } - code_items_map_.emplace(offsets_pair, code_item); - code_items_.AddItem(code_item); - - // Add "fixup" references to types, strings, methods, and fields. - // This is temporary, as we will probably want more detailed parsing of the - // instructions here. - std::vector type_ids; - std::vector string_ids; - std::vector method_ids; - std::vector field_ids; - if (GetIdsFromByteCode(*this, - code_item, - /*out*/ &type_ids, - /*out*/ &string_ids, - /*out*/ &method_ids, - /*out*/ &field_ids)) { - CodeFixups* fixups = new CodeFixups(std::move(type_ids), - std::move(string_ids), - std::move(method_ids), - std::move(field_ids)); - code_item->SetCodeFixups(fixups); - } - - return code_item; -} - -MethodItem* Collections::GenerateMethodItem(const DexFile& dex_file, ClassDataItemIterator& cdii) { - MethodId* method_id = GetMethodId(cdii.GetMemberIndex()); - uint32_t access_flags = cdii.GetRawMemberAccessFlags(); - const DexFile::CodeItem* disk_code_item = cdii.GetMethodCodeItem(); - // Temporary hack to prevent incorrectly deduping code items if they have the same offset since - // they may have different debug info streams. - CodeItem* code_item = DedupeOrCreateCodeItem(dex_file, - disk_code_item, - cdii.GetMethodCodeItemOffset(), - cdii.GetMemberIndex()); - return new MethodItem(access_flags, method_id, code_item); -} - -ClassData* Collections::CreateClassData( - const DexFile& dex_file, const uint8_t* encoded_data, uint32_t offset) { - // Read the fields and methods defined by the class, resolving the circular reference from those - // to classes by setting class at the same time. - ClassData* class_data = class_datas_map_.GetExistingObject(offset); - if (class_data == nullptr && encoded_data != nullptr) { - ClassDataItemIterator cdii(dex_file, encoded_data); - // Static fields. - FieldItemVector* static_fields = new FieldItemVector(); - for (; cdii.HasNextStaticField(); cdii.Next()) { - FieldId* field_item = GetFieldId(cdii.GetMemberIndex()); - uint32_t access_flags = cdii.GetRawMemberAccessFlags(); - static_fields->push_back(std::unique_ptr(new FieldItem(access_flags, field_item))); - } - // Instance fields. - FieldItemVector* instance_fields = new FieldItemVector(); - for (; cdii.HasNextInstanceField(); cdii.Next()) { - FieldId* field_item = GetFieldId(cdii.GetMemberIndex()); - uint32_t access_flags = cdii.GetRawMemberAccessFlags(); - instance_fields->push_back( - std::unique_ptr(new FieldItem(access_flags, field_item))); - } - // Direct methods. - MethodItemVector* direct_methods = new MethodItemVector(); - for (; cdii.HasNextDirectMethod(); cdii.Next()) { - direct_methods->push_back(std::unique_ptr(GenerateMethodItem(dex_file, cdii))); - } - // Virtual methods. - MethodItemVector* virtual_methods = new MethodItemVector(); - for (; cdii.HasNextVirtualMethod(); cdii.Next()) { - virtual_methods->push_back(std::unique_ptr(GenerateMethodItem(dex_file, cdii))); - } - class_data = new ClassData(static_fields, instance_fields, direct_methods, virtual_methods); - class_data->SetSize(cdii.EndDataPointer() - encoded_data); - AddItem(class_datas_map_, class_datas_, class_data, offset); - } - return class_data; -} - -void Collections::CreateCallSitesAndMethodHandles(const DexFile& dex_file) { - // Iterate through the map list and set the offset of the CallSiteIds and MethodHandleItems. - const DexFile::MapList* map = dex_file.GetMapList(); - for (uint32_t i = 0; i < map->size_; ++i) { - const DexFile::MapItem* item = map->list_ + i; - switch (item->type_) { - case DexFile::kDexTypeCallSiteIdItem: - SetCallSiteIdsOffset(item->offset_); - break; - case DexFile::kDexTypeMethodHandleItem: - SetMethodHandleItemsOffset(item->offset_); - break; - default: - break; - } - } - // Populate MethodHandleItems first (CallSiteIds may depend on them). - for (uint32_t i = 0; i < dex_file.NumMethodHandles(); i++) { - CreateMethodHandleItem(dex_file, i); - } - // Populate CallSiteIds. - for (uint32_t i = 0; i < dex_file.NumCallSiteIds(); i++) { - CreateCallSiteId(dex_file, i); - } -} - -void Collections::CreateCallSiteId(const DexFile& dex_file, uint32_t i) { - const DexFile::CallSiteIdItem& disk_call_site_id = dex_file.GetCallSiteId(i); - const uint8_t* disk_call_item_ptr = dex_file.DataBegin() + disk_call_site_id.data_off_; - EncodedArrayItem* call_site_item = - CreateEncodedArrayItem(dex_file, disk_call_item_ptr, disk_call_site_id.data_off_); - - CallSiteId* call_site_id = new CallSiteId(call_site_item); - AddIndexedItem(call_site_ids_, call_site_id, CallSiteIdsOffset() + i * CallSiteId::ItemSize(), i); -} - -void Collections::CreateMethodHandleItem(const DexFile& dex_file, uint32_t i) { - const DexFile::MethodHandleItem& disk_method_handle = dex_file.GetMethodHandle(i); - uint16_t index = disk_method_handle.field_or_method_idx_; - DexFile::MethodHandleType type = - static_cast(disk_method_handle.method_handle_type_); - bool is_invoke = type == DexFile::MethodHandleType::kInvokeStatic || - type == DexFile::MethodHandleType::kInvokeInstance || - type == DexFile::MethodHandleType::kInvokeConstructor || - type == DexFile::MethodHandleType::kInvokeDirect || - type == DexFile::MethodHandleType::kInvokeInterface; - static_assert(DexFile::MethodHandleType::kLast == DexFile::MethodHandleType::kInvokeInterface, - "Unexpected method handle types."); - IndexedItem* field_or_method_id; - if (is_invoke) { - field_or_method_id = GetMethodId(index); - } else { - field_or_method_id = GetFieldId(index); - } - MethodHandleItem* method_handle = new MethodHandleItem(type, field_or_method_id); - AddIndexedItem(method_handle_items_, - method_handle, - MethodHandleItemsOffset() + i * MethodHandleItem::ItemSize(), - i); -} - -void Collections::SortVectorsByMapOrder() { - string_datas_.SortByMapOrder(string_datas_map_.Collection()); - type_lists_.SortByMapOrder(type_lists_map_.Collection()); - encoded_array_items_.SortByMapOrder(encoded_array_items_map_.Collection()); - annotation_items_.SortByMapOrder(annotation_items_map_.Collection()); - annotation_set_items_.SortByMapOrder(annotation_set_items_map_.Collection()); - annotation_set_ref_lists_.SortByMapOrder(annotation_set_ref_lists_map_.Collection()); - annotations_directory_items_.SortByMapOrder(annotations_directory_items_map_.Collection()); - debug_info_items_.SortByMapOrder(debug_info_items_map_.Collection()); - code_items_.SortByMapOrder(code_items_map_); - class_datas_.SortByMapOrder(class_datas_map_.Collection()); -} - -static uint32_t HeaderOffset(const dex_ir::Collections& collections ATTRIBUTE_UNUSED) { +static uint32_t HeaderOffset(const dex_ir::Header* header ATTRIBUTE_UNUSED) { return 0; } -static uint32_t HeaderSize(const dex_ir::Collections& collections ATTRIBUTE_UNUSED) { +static uint32_t HeaderSize(const dex_ir::Header* header ATTRIBUTE_UNUSED) { // Size is in elements, so there is only one header. return 1; } @@ -859,9 +45,9 @@ struct FileSectionDescriptor { std::string name; uint16_t type; // A function that when applied to a collection object, gives the size of the section. - std::function size_fn; + std::function size_fn; // A function that when applied to a collection object, gives the offset of the section. - std::function offset_fn; + std::function offset_fn; }; static const FileSectionDescriptor kFileSectionDescriptors[] = { @@ -873,106 +59,105 @@ static const FileSectionDescriptor kFileSectionDescriptors[] = { }, { "StringId", DexFile::kDexTypeStringIdItem, - &dex_ir::Collections::StringIdsSize, - &dex_ir::Collections::StringIdsOffset + [](const dex_ir::Header* h) { return h->StringIds().Size(); }, + [](const dex_ir::Header* h) { return h->StringIds().GetOffset(); } }, { "TypeId", DexFile::kDexTypeTypeIdItem, - &dex_ir::Collections::TypeIdsSize, - &dex_ir::Collections::TypeIdsOffset + [](const dex_ir::Header* h) { return h->TypeIds().Size(); }, + [](const dex_ir::Header* h) { return h->TypeIds().GetOffset(); } }, { "ProtoId", DexFile::kDexTypeProtoIdItem, - &dex_ir::Collections::ProtoIdsSize, - &dex_ir::Collections::ProtoIdsOffset + [](const dex_ir::Header* h) { return h->ProtoIds().Size(); }, + [](const dex_ir::Header* h) { return h->ProtoIds().GetOffset(); } }, { "FieldId", DexFile::kDexTypeFieldIdItem, - &dex_ir::Collections::FieldIdsSize, - &dex_ir::Collections::FieldIdsOffset + [](const dex_ir::Header* h) { return h->FieldIds().Size(); }, + [](const dex_ir::Header* h) { return h->FieldIds().GetOffset(); } }, { "MethodId", DexFile::kDexTypeMethodIdItem, - &dex_ir::Collections::MethodIdsSize, - &dex_ir::Collections::MethodIdsOffset + [](const dex_ir::Header* h) { return h->MethodIds().Size(); }, + [](const dex_ir::Header* h) { return h->MethodIds().GetOffset(); } }, { "ClassDef", DexFile::kDexTypeClassDefItem, - &dex_ir::Collections::ClassDefsSize, - &dex_ir::Collections::ClassDefsOffset + [](const dex_ir::Header* h) { return h->ClassDefs().Size(); }, + [](const dex_ir::Header* h) { return h->ClassDefs().GetOffset(); } }, { "CallSiteId", DexFile::kDexTypeCallSiteIdItem, - &dex_ir::Collections::CallSiteIdsSize, - &dex_ir::Collections::CallSiteIdsOffset + [](const dex_ir::Header* h) { return h->CallSiteIds().Size(); }, + [](const dex_ir::Header* h) { return h->CallSiteIds().GetOffset(); } }, { "MethodHandle", DexFile::kDexTypeMethodHandleItem, - &dex_ir::Collections::MethodHandleItemsSize, - &dex_ir::Collections::MethodHandleItemsOffset + [](const dex_ir::Header* h) { return h->MethodHandleItems().Size(); }, + [](const dex_ir::Header* h) { return h->MethodHandleItems().GetOffset(); } }, { "StringData", DexFile::kDexTypeStringDataItem, - &dex_ir::Collections::StringDatasSize, - &dex_ir::Collections::StringDatasOffset + [](const dex_ir::Header* h) { return h->StringDatas().Size(); }, + [](const dex_ir::Header* h) { return h->StringDatas().GetOffset(); } }, { "TypeList", DexFile::kDexTypeTypeList, - &dex_ir::Collections::TypeListsSize, - &dex_ir::Collections::TypeListsOffset + [](const dex_ir::Header* h) { return h->TypeLists().Size(); }, + [](const dex_ir::Header* h) { return h->TypeLists().GetOffset(); } }, { "EncArr", DexFile::kDexTypeEncodedArrayItem, - &dex_ir::Collections::EncodedArrayItemsSize, - &dex_ir::Collections::EncodedArrayItemsOffset + [](const dex_ir::Header* h) { return h->EncodedArrayItems().Size(); }, + [](const dex_ir::Header* h) { return h->EncodedArrayItems().GetOffset(); } }, { "Annotation", DexFile::kDexTypeAnnotationItem, - &dex_ir::Collections::AnnotationItemsSize, - &dex_ir::Collections::AnnotationItemsOffset + [](const dex_ir::Header* h) { return h->AnnotationItems().Size(); }, + [](const dex_ir::Header* h) { return h->AnnotationItems().GetOffset(); } }, { "AnnoSet", DexFile::kDexTypeAnnotationSetItem, - &dex_ir::Collections::AnnotationSetItemsSize, - &dex_ir::Collections::AnnotationSetItemsOffset + [](const dex_ir::Header* h) { return h->AnnotationSetItems().Size(); }, + [](const dex_ir::Header* h) { return h->AnnotationSetItems().GetOffset(); } }, { "AnnoSetRL", DexFile::kDexTypeAnnotationSetRefList, - &dex_ir::Collections::AnnotationSetRefListsSize, - &dex_ir::Collections::AnnotationSetRefListsOffset + [](const dex_ir::Header* h) { return h->AnnotationSetRefLists().Size(); }, + [](const dex_ir::Header* h) { return h->AnnotationSetRefLists().GetOffset(); } }, { "AnnoDir", DexFile::kDexTypeAnnotationsDirectoryItem, - &dex_ir::Collections::AnnotationsDirectoryItemsSize, - &dex_ir::Collections::AnnotationsDirectoryItemsOffset + [](const dex_ir::Header* h) { return h->AnnotationsDirectoryItems().Size(); }, + [](const dex_ir::Header* h) { return h->AnnotationsDirectoryItems().GetOffset(); } }, { "DebugInfo", DexFile::kDexTypeDebugInfoItem, - &dex_ir::Collections::DebugInfoItemsSize, - &dex_ir::Collections::DebugInfoItemsOffset + [](const dex_ir::Header* h) { return h->DebugInfoItems().Size(); }, + [](const dex_ir::Header* h) { return h->DebugInfoItems().GetOffset(); } }, { "CodeItem", DexFile::kDexTypeCodeItem, - &dex_ir::Collections::CodeItemsSize, - &dex_ir::Collections::CodeItemsOffset + [](const dex_ir::Header* h) { return h->CodeItems().Size(); }, + [](const dex_ir::Header* h) { return h->CodeItems().GetOffset(); } }, { "ClassData", DexFile::kDexTypeClassDataItem, - &dex_ir::Collections::ClassDatasSize, - &dex_ir::Collections::ClassDatasOffset + [](const dex_ir::Header* h) { return h->ClassDatas().Size(); }, + [](const dex_ir::Header* h) { return h->ClassDatas().GetOffset(); } } }; std::vector GetSortedDexFileSections(dex_ir::Header* header, dex_ir::SortDirection direction) { - const dex_ir::Collections& collections = header->GetCollections(); std::vector sorted_sections; // Build the table that will map from offset to color for (const FileSectionDescriptor& s : kFileSectionDescriptors) { sorted_sections.push_back(dex_ir::DexFileSection(s.name, s.type, - s.size_fn(collections), - s.offset_fn(collections))); + s.size_fn(header), + s.offset_fn(header))); } // Sort by offset. std::sort(sorted_sections.begin(), diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h index 5ecad2bf87f8db7e3ce7cfc540643ba49fead1a1..9f355ba9e83c53c2138f8a3747b99bcc93df4eb9 100644 --- a/dexlayout/dex_ir.h +++ b/dexlayout/dex_ir.h @@ -24,6 +24,7 @@ #include #include +#include "base/iteration_range.h" #include "base/leb128.h" #include "base/stl_util.h" #include "dex/dex_file-inl.h" @@ -107,377 +108,195 @@ class AbstractDispatcher { DISALLOW_COPY_AND_ASSIGN(AbstractDispatcher); }; -// Collections become owners of the objects added by moving them into unique pointers. -template class CollectionBase { +template class Iterator : public std::iterator { public: - CollectionBase() = default; + using value_type = typename std::iterator::value_type; + using difference_type = + typename std::iterator::difference_type; + using pointer = typename std::iterator::pointer; + using reference = typename std::iterator::reference; + + Iterator(const Iterator&) = default; + Iterator(Iterator&&) = default; + Iterator& operator=(const Iterator&) = default; + Iterator& operator=(Iterator&&) = default; + + Iterator(const std::vector& vector, + uint32_t position, + uint32_t iterator_end) + : vector_(&vector), + position_(position), + iterator_end_(iterator_end) { } + Iterator() : vector_(nullptr), position_(0U), iterator_end_(0U) { } + + bool IsValid() const { return position_ < iterator_end_; } + + bool operator==(const Iterator& rhs) const { return position_ == rhs.position_; } + bool operator!=(const Iterator& rhs) const { return !(*this == rhs); } + bool operator<(const Iterator& rhs) const { return position_ < rhs.position_; } + bool operator>(const Iterator& rhs) const { return rhs < *this; } + bool operator<=(const Iterator& rhs) const { return !(rhs < *this); } + bool operator>=(const Iterator& rhs) const { return !(*this < rhs); } + + Iterator& operator++() { // Value after modification. + ++position_; + return *this; + } - uint32_t GetOffset() const { - return offset_; + Iterator operator++(int) { + Iterator temp = *this; + ++position_; + return temp; } - void SetOffset(uint32_t new_offset) { - offset_ = new_offset; + + Iterator& operator+=(difference_type delta) { + position_ += delta; + return *this; } - private: - // Start out unassigned. - uint32_t offset_ = 0u; + Iterator operator+(difference_type delta) const { + Iterator temp = *this; + temp += delta; + return temp; + } - DISALLOW_COPY_AND_ASSIGN(CollectionBase); -}; + Iterator& operator--() { // Value after modification. + --position_; + return *this; + } -template class CollectionVector : public CollectionBase { - public: - using Vector = std::vector>; - CollectionVector() = default; + Iterator operator--(int) { + Iterator temp = *this; + --position_; + return temp; + } - uint32_t Size() const { return collection_.size(); } - Vector& Collection() { return collection_; } - const Vector& Collection() const { return collection_; } + Iterator& operator-=(difference_type delta) { + position_ -= delta; + return *this; + } - // Sort the vector by copying pointers over. - template - void SortByMapOrder(const MapType& map) { - auto it = map.begin(); - CHECK_EQ(map.size(), Size()); - for (size_t i = 0; i < Size(); ++i) { - // There are times when the array will temporarily contain the same pointer twice, doing the - // release here sure there is no double free errors. - Collection()[i].release(); - Collection()[i].reset(it->second); - ++it; - } + Iterator operator-(difference_type delta) const { + Iterator temp = *this; + temp -= delta; + return temp; } - protected: - Vector collection_; + difference_type operator-(const Iterator& rhs) { + return position_ - rhs.position_; + } - void AddItem(T* object) { - collection_.push_back(std::unique_ptr(object)); + reference operator*() const { + return const_cast((*vector_)[position_]); } - private: - friend class Collections; - DISALLOW_COPY_AND_ASSIGN(CollectionVector); -}; + pointer operator->() const { + return const_cast(&((*vector_)[position_])); + } -template class IndexedCollectionVector : public CollectionVector { - public: - using Vector = std::vector>; - IndexedCollectionVector() = default; + reference operator[](difference_type n) const { + return (*vector_)[position_ + n]; + } private: - void AddIndexedItem(T* object, uint32_t index) { - object->SetIndex(index); - CollectionVector::collection_.push_back(std::unique_ptr(object)); - } + const std::vector* vector_; + uint32_t position_; + uint32_t iterator_end_; - friend class Collections; - DISALLOW_COPY_AND_ASSIGN(IndexedCollectionVector); + template + friend bool operator<(const Iterator& lhs, const Iterator& rhs); }; -template class CollectionMap : public CollectionBase { +// Collections become owners of the objects added by moving them into unique pointers. +class CollectionBase { public: - CollectionMap() = default; - - // Returns the existing item if it is already inserted, null otherwise. - T* GetExistingObject(uint32_t offset) { - auto it = collection_.find(offset); - return it != collection_.end() ? it->second : nullptr; - } + CollectionBase() = default; + virtual ~CollectionBase() { } - // Lower case for template interop with std::map. - uint32_t size() const { return collection_.size(); } - std::map& Collection() { return collection_; } + uint32_t GetOffset() const { return offset_; } + void SetOffset(uint32_t new_offset) { offset_ = new_offset; } + virtual uint32_t Size() const { return 0U; } private: - std::map collection_; - - void AddItem(T* object, uint32_t offset) { - auto it = collection_.emplace(offset, object); - CHECK(it.second) << "CollectionMap already has an object with offset " << offset << " " - << " and address " << it.first->second; - } + // Start out unassigned. + uint32_t offset_ = 0u; - friend class Collections; - DISALLOW_COPY_AND_ASSIGN(CollectionMap); + DISALLOW_COPY_AND_ASSIGN(CollectionBase); }; -class Collections { +template class CollectionVector : public CollectionBase { public: - Collections() = default; - - CollectionVector::Vector& StringIds() { return string_ids_.Collection(); } - CollectionVector::Vector& TypeIds() { return type_ids_.Collection(); } - CollectionVector::Vector& ProtoIds() { return proto_ids_.Collection(); } - CollectionVector::Vector& FieldIds() { return field_ids_.Collection(); } - CollectionVector::Vector& MethodIds() { return method_ids_.Collection(); } - CollectionVector::Vector& ClassDefs() { return class_defs_.Collection(); } - CollectionVector::Vector& CallSiteIds() { return call_site_ids_.Collection(); } - CollectionVector::Vector& MethodHandleItems() - { return method_handle_items_.Collection(); } - CollectionVector::Vector& StringDatas() { return string_datas_.Collection(); } - CollectionVector::Vector& TypeLists() { return type_lists_.Collection(); } - CollectionVector::Vector& EncodedArrayItems() - { return encoded_array_items_.Collection(); } - CollectionVector::Vector& AnnotationItems() - { return annotation_items_.Collection(); } - CollectionVector::Vector& AnnotationSetItems() - { return annotation_set_items_.Collection(); } - CollectionVector::Vector& AnnotationSetRefLists() - { return annotation_set_ref_lists_.Collection(); } - CollectionVector::Vector& AnnotationsDirectoryItems() - { return annotations_directory_items_.Collection(); } - CollectionVector::Vector& DebugInfoItems() - { return debug_info_items_.Collection(); } - CollectionVector::Vector& CodeItems() { return code_items_.Collection(); } - CollectionVector::Vector& ClassDatas() { return class_datas_.Collection(); } - - const CollectionVector::Vector& ClassDefs() const { return class_defs_.Collection(); } - - void CreateStringId(const DexFile& dex_file, uint32_t i); - void CreateTypeId(const DexFile& dex_file, uint32_t i); - void CreateProtoId(const DexFile& dex_file, uint32_t i); - void CreateFieldId(const DexFile& dex_file, uint32_t i); - void CreateMethodId(const DexFile& dex_file, uint32_t i); - void CreateClassDef(const DexFile& dex_file, uint32_t i); - void CreateCallSiteId(const DexFile& dex_file, uint32_t i); - void CreateMethodHandleItem(const DexFile& dex_file, uint32_t i); - - void CreateCallSitesAndMethodHandles(const DexFile& dex_file); - - TypeList* CreateTypeList(const DexFile::TypeList* type_list, uint32_t offset); - EncodedArrayItem* CreateEncodedArrayItem(const DexFile& dex_file, - const uint8_t* static_data, - uint32_t offset); - AnnotationItem* CreateAnnotationItem(const DexFile& dex_file, - const DexFile::AnnotationItem* annotation); - AnnotationSetItem* CreateAnnotationSetItem(const DexFile& dex_file, - const DexFile::AnnotationSetItem* disk_annotations_item, uint32_t offset); - AnnotationsDirectoryItem* CreateAnnotationsDirectoryItem(const DexFile& dex_file, - const DexFile::AnnotationsDirectoryItem* disk_annotations_item, uint32_t offset); - CodeItem* DedupeOrCreateCodeItem(const DexFile& dex_file, - const DexFile::CodeItem* disk_code_item, - uint32_t offset, - uint32_t dex_method_index); - ClassData* CreateClassData(const DexFile& dex_file, const uint8_t* encoded_data, uint32_t offset); - void AddAnnotationsFromMapListSection(const DexFile& dex_file, - uint32_t start_offset, - uint32_t count); - - StringId* GetStringId(uint32_t index) { - CHECK_LT(index, StringIdsSize()); - return StringIds()[index].get(); - } - TypeId* GetTypeId(uint32_t index) { - CHECK_LT(index, TypeIdsSize()); - return TypeIds()[index].get(); - } - ProtoId* GetProtoId(uint32_t index) { - CHECK_LT(index, ProtoIdsSize()); - return ProtoIds()[index].get(); - } - FieldId* GetFieldId(uint32_t index) { - CHECK_LT(index, FieldIdsSize()); - return FieldIds()[index].get(); - } - MethodId* GetMethodId(uint32_t index) { - CHECK_LT(index, MethodIdsSize()); - return MethodIds()[index].get(); - } - ClassDef* GetClassDef(uint32_t index) { - CHECK_LT(index, ClassDefsSize()); - return ClassDefs()[index].get(); - } - CallSiteId* GetCallSiteId(uint32_t index) { - CHECK_LT(index, CallSiteIdsSize()); - return CallSiteIds()[index].get(); - } - MethodHandleItem* GetMethodHandle(uint32_t index) { - CHECK_LT(index, MethodHandleItemsSize()); - return MethodHandleItems()[index].get(); - } + using ElementType = std::unique_ptr; - StringId* GetStringIdOrNullPtr(uint32_t index) { - return index == dex::kDexNoIndex ? nullptr : GetStringId(index); + CollectionVector() { } + explicit CollectionVector(size_t size) { + // Preallocate so that assignment does not invalidate pointers into the vector. + collection_.reserve(size); } - TypeId* GetTypeIdOrNullPtr(uint16_t index) { - return index == DexFile::kDexNoIndex16 ? nullptr : GetTypeId(index); + virtual ~CollectionVector() OVERRIDE { } + + template + T* CreateAndAddItem(Args&&... args) { + T* object = new T(std::forward(args)...); + collection_.push_back(std::unique_ptr(object)); + return object; } - uint32_t StringIdsOffset() const { return string_ids_.GetOffset(); } - uint32_t TypeIdsOffset() const { return type_ids_.GetOffset(); } - uint32_t ProtoIdsOffset() const { return proto_ids_.GetOffset(); } - uint32_t FieldIdsOffset() const { return field_ids_.GetOffset(); } - uint32_t MethodIdsOffset() const { return method_ids_.GetOffset(); } - uint32_t ClassDefsOffset() const { return class_defs_.GetOffset(); } - uint32_t CallSiteIdsOffset() const { return call_site_ids_.GetOffset(); } - uint32_t MethodHandleItemsOffset() const { return method_handle_items_.GetOffset(); } - uint32_t StringDatasOffset() const { return string_datas_.GetOffset(); } - uint32_t TypeListsOffset() const { return type_lists_.GetOffset(); } - uint32_t EncodedArrayItemsOffset() const { return encoded_array_items_.GetOffset(); } - uint32_t AnnotationItemsOffset() const { return annotation_items_.GetOffset(); } - uint32_t AnnotationSetItemsOffset() const { return annotation_set_items_.GetOffset(); } - uint32_t AnnotationSetRefListsOffset() const { return annotation_set_ref_lists_.GetOffset(); } - uint32_t AnnotationsDirectoryItemsOffset() const - { return annotations_directory_items_.GetOffset(); } - uint32_t DebugInfoItemsOffset() const { return debug_info_items_.GetOffset(); } - uint32_t CodeItemsOffset() const { return code_items_.GetOffset(); } - uint32_t ClassDatasOffset() const { return class_datas_.GetOffset(); } - uint32_t MapListOffset() const { return map_list_offset_; } + virtual uint32_t Size() const OVERRIDE { return collection_.size(); } - void SetStringIdsOffset(uint32_t new_offset) { string_ids_.SetOffset(new_offset); } - void SetTypeIdsOffset(uint32_t new_offset) { type_ids_.SetOffset(new_offset); } - void SetProtoIdsOffset(uint32_t new_offset) { proto_ids_.SetOffset(new_offset); } - void SetFieldIdsOffset(uint32_t new_offset) { field_ids_.SetOffset(new_offset); } - void SetMethodIdsOffset(uint32_t new_offset) { method_ids_.SetOffset(new_offset); } - void SetClassDefsOffset(uint32_t new_offset) { class_defs_.SetOffset(new_offset); } - void SetCallSiteIdsOffset(uint32_t new_offset) { call_site_ids_.SetOffset(new_offset); } - void SetMethodHandleItemsOffset(uint32_t new_offset) - { method_handle_items_.SetOffset(new_offset); } - void SetStringDatasOffset(uint32_t new_offset) { string_datas_.SetOffset(new_offset); } - void SetTypeListsOffset(uint32_t new_offset) { type_lists_.SetOffset(new_offset); } - void SetEncodedArrayItemsOffset(uint32_t new_offset) - { encoded_array_items_.SetOffset(new_offset); } - void SetAnnotationItemsOffset(uint32_t new_offset) { annotation_items_.SetOffset(new_offset); } - void SetAnnotationSetItemsOffset(uint32_t new_offset) - { annotation_set_items_.SetOffset(new_offset); } - void SetAnnotationSetRefListsOffset(uint32_t new_offset) - { annotation_set_ref_lists_.SetOffset(new_offset); } - void SetAnnotationsDirectoryItemsOffset(uint32_t new_offset) - { annotations_directory_items_.SetOffset(new_offset); } - void SetDebugInfoItemsOffset(uint32_t new_offset) { debug_info_items_.SetOffset(new_offset); } - void SetCodeItemsOffset(uint32_t new_offset) { code_items_.SetOffset(new_offset); } - void SetClassDatasOffset(uint32_t new_offset) { class_datas_.SetOffset(new_offset); } - void SetMapListOffset(uint32_t new_offset) { map_list_offset_ = new_offset; } + Iterator begin() const { return Iterator(collection_, 0U, Size()); } + Iterator end() const { return Iterator(collection_, Size(), Size()); } - uint32_t StringIdsSize() const { return string_ids_.Size(); } - uint32_t TypeIdsSize() const { return type_ids_.Size(); } - uint32_t ProtoIdsSize() const { return proto_ids_.Size(); } - uint32_t FieldIdsSize() const { return field_ids_.Size(); } - uint32_t MethodIdsSize() const { return method_ids_.Size(); } - uint32_t ClassDefsSize() const { return class_defs_.Size(); } - uint32_t CallSiteIdsSize() const { return call_site_ids_.Size(); } - uint32_t MethodHandleItemsSize() const { return method_handle_items_.Size(); } - uint32_t StringDatasSize() const { return string_datas_.Size(); } - uint32_t TypeListsSize() const { return type_lists_.Size(); } - uint32_t EncodedArrayItemsSize() const { return encoded_array_items_.Size(); } - uint32_t AnnotationItemsSize() const { return annotation_items_.Size(); } - uint32_t AnnotationSetItemsSize() const { return annotation_set_items_.Size(); } - uint32_t AnnotationSetRefListsSize() const { return annotation_set_ref_lists_.Size(); } - uint32_t AnnotationsDirectoryItemsSize() const { return annotations_directory_items_.Size(); } - uint32_t DebugInfoItemsSize() const { return debug_info_items_.Size(); } - uint32_t CodeItemsSize() const { return code_items_.Size(); } - uint32_t ClassDatasSize() const { return class_datas_.Size(); } - - // Sort the vectors buy map order (same order that was used in the input file). - void SortVectorsByMapOrder(); - - template - void AddItem(CollectionMap& map, - CollectionVector& vector, - Type* item, - uint32_t offset) { - DCHECK(!map.GetExistingObject(offset)); - DCHECK(!item->OffsetAssigned()); - if (eagerly_assign_offsets_) { - item->SetOffset(offset); - } - map.AddItem(item, offset); - vector.AddItem(item); + const ElementType& operator[](size_t index) const { + DCHECK_LT(index, Size()); + return collection_[index]; } - - template - void AddIndexedItem(IndexedCollectionVector& vector, - Type* item, - uint32_t offset, - uint32_t index) { - DCHECK(!item->OffsetAssigned()); - if (eagerly_assign_offsets_) { - item->SetOffset(offset); - } - vector.AddIndexedItem(item, index); + ElementType& operator[](size_t index) { + DCHECK_LT(index, Size()); + return collection_[index]; } - void SetEagerlyAssignOffsets(bool eagerly_assign_offsets) { - eagerly_assign_offsets_ = eagerly_assign_offsets; - } - - void SetLinkData(std::vector&& link_data) { - link_data_ = std::move(link_data); + // Sort the vector by copying pointers over. + template + void SortByMapOrder(const MapType& map) { + auto it = map.begin(); + CHECK_EQ(map.size(), Size()); + for (size_t i = 0; i < Size(); ++i) { + // There are times when the array will temporarily contain the same pointer twice, doing the + // release here sure there is no double free errors. + collection_[i].release(); + collection_[i].reset(it->second); + ++it; + } } - const std::vector& LinkData() const { - return link_data_; - } + protected: + std::vector collection_; private: - EncodedValue* ReadEncodedValue(const DexFile& dex_file, const uint8_t** data); - EncodedValue* ReadEncodedValue(const DexFile& dex_file, - const uint8_t** data, - uint8_t type, - uint8_t length); - void ReadEncodedValue(const DexFile& dex_file, - const uint8_t** data, - uint8_t type, - uint8_t length, - EncodedValue* item); - - ParameterAnnotation* GenerateParameterAnnotation(const DexFile& dex_file, MethodId* method_id, - const DexFile::AnnotationSetRefList* annotation_set_ref_list, uint32_t offset); - MethodItem* GenerateMethodItem(const DexFile& dex_file, ClassDataItemIterator& cdii); - - // Collection vectors own the IR data. - IndexedCollectionVector string_ids_; - IndexedCollectionVector type_ids_; - IndexedCollectionVector proto_ids_; - IndexedCollectionVector field_ids_; - IndexedCollectionVector method_ids_; - IndexedCollectionVector call_site_ids_; - IndexedCollectionVector method_handle_items_; - IndexedCollectionVector string_datas_; - IndexedCollectionVector type_lists_; - IndexedCollectionVector encoded_array_items_; - IndexedCollectionVector annotation_items_; - IndexedCollectionVector annotation_set_items_; - IndexedCollectionVector annotation_set_ref_lists_; - IndexedCollectionVector annotations_directory_items_; - IndexedCollectionVector class_defs_; - // The order of the vectors controls the layout of the output file by index order, to change the - // layout just sort the vector. Note that you may only change the order of the non indexed vectors - // below. Indexed vectors are accessed by indices in other places, changing the sorting order will - // invalidate the existing indices and is not currently supported. - CollectionVector debug_info_items_; - CollectionVector code_items_; - CollectionVector class_datas_; - - // Note that the maps do not have ownership, the vectors do. - // TODO: These maps should only be required for building the IR and should be put in a separate - // IR builder class. - CollectionMap string_datas_map_; - CollectionMap type_lists_map_; - CollectionMap encoded_array_items_map_; - CollectionMap annotation_items_map_; - CollectionMap annotation_set_items_map_; - CollectionMap annotation_set_ref_lists_map_; - CollectionMap annotations_directory_items_map_; - CollectionMap debug_info_items_map_; - // Code item maps need to check both the debug info offset and debug info offset, do not use - // CollectionMap. - // First offset is the code item offset, second is the debug info offset. - std::map, CodeItem*> code_items_map_; - CollectionMap class_datas_map_; + DISALLOW_COPY_AND_ASSIGN(CollectionVector); +}; - uint32_t map_list_offset_ = 0; +template class IndexedCollectionVector : public CollectionVector { + public: + using Vector = std::vector>; + IndexedCollectionVector() = default; + explicit IndexedCollectionVector(size_t size) : CollectionVector(size) { } - // Link data. - std::vector link_data_; + template + T* CreateAndAddIndexedItem(uint32_t index, Args&&... args) { + T* object = CollectionVector::CreateAndAddItem(std::forward(args)...); + object->SetIndex(index); + return object; + } - // If we eagerly assign offsets during IR building or later after layout. Must be false if - // changing the layout is enabled. - bool eagerly_assign_offsets_; + T* operator[](size_t index) const { + DCHECK_NE(CollectionVector::collection_[index].get(), static_cast(nullptr)); + return CollectionVector::collection_[index].get(); + } - DISALLOW_COPY_AND_ASSIGN(Collections); + private: + DISALLOW_COPY_AND_ASSIGN(IndexedCollectionVector); }; class Item { @@ -485,6 +304,8 @@ class Item { Item() { } virtual ~Item() { } + Item(Item&&) = default; + // Return the assigned offset. uint32_t GetOffset() const WARN_UNUSED { CHECK(OffsetAssigned()); @@ -536,18 +357,54 @@ class Header : public Item { uint32_t data_size, uint32_t data_offset, bool support_default_methods) + : Item(0, kHeaderItemSize), support_default_methods_(support_default_methods) { + ConstructorHelper(magic, + checksum, + signature, + endian_tag, + file_size, + header_size, + link_size, + link_offset, + data_size, + data_offset); + } + + Header(const uint8_t* magic, + uint32_t checksum, + const uint8_t* signature, + uint32_t endian_tag, + uint32_t file_size, + uint32_t header_size, + uint32_t link_size, + uint32_t link_offset, + uint32_t data_size, + uint32_t data_offset, + bool support_default_methods, + uint32_t num_string_ids, + uint32_t num_type_ids, + uint32_t num_proto_ids, + uint32_t num_field_ids, + uint32_t num_method_ids, + uint32_t num_class_defs) : Item(0, kHeaderItemSize), - checksum_(checksum), - endian_tag_(endian_tag), - file_size_(file_size), - header_size_(header_size), - link_size_(link_size), - link_offset_(link_offset), - data_size_(data_size), - data_offset_(data_offset), - support_default_methods_(support_default_methods) { - memcpy(magic_, magic, sizeof(magic_)); - memcpy(signature_, signature, sizeof(signature_)); + support_default_methods_(support_default_methods), + string_ids_(num_string_ids), + type_ids_(num_type_ids), + proto_ids_(num_proto_ids), + field_ids_(num_field_ids), + method_ids_(num_method_ids), + class_defs_(num_class_defs) { + ConstructorHelper(magic, + checksum, + signature, + endian_tag, + file_size, + header_size, + link_size, + link_offset, + data_size, + data_offset); } ~Header() OVERRIDE { } @@ -575,7 +432,69 @@ class Header : public Item { void SetDataSize(uint32_t new_data_size) { data_size_ = new_data_size; } void SetDataOffset(uint32_t new_data_offset) { data_offset_ = new_data_offset; } - Collections& GetCollections() { return collections_; } + IndexedCollectionVector& StringIds() { return string_ids_; } + const IndexedCollectionVector& StringIds() const { return string_ids_; } + IndexedCollectionVector& TypeIds() { return type_ids_; } + const IndexedCollectionVector& TypeIds() const { return type_ids_; } + IndexedCollectionVector& ProtoIds() { return proto_ids_; } + const IndexedCollectionVector& ProtoIds() const { return proto_ids_; } + IndexedCollectionVector& FieldIds() { return field_ids_; } + const IndexedCollectionVector& FieldIds() const { return field_ids_; } + IndexedCollectionVector& MethodIds() { return method_ids_; } + const IndexedCollectionVector& MethodIds() const { return method_ids_; } + IndexedCollectionVector& ClassDefs() { return class_defs_; } + const IndexedCollectionVector& ClassDefs() const { return class_defs_; } + IndexedCollectionVector& CallSiteIds() { return call_site_ids_; } + const IndexedCollectionVector& CallSiteIds() const { return call_site_ids_; } + IndexedCollectionVector& MethodHandleItems() { return method_handle_items_; } + const IndexedCollectionVector& MethodHandleItems() const { + return method_handle_items_; + } + CollectionVector& StringDatas() { return string_datas_; } + const CollectionVector& StringDatas() const { return string_datas_; } + CollectionVector& TypeLists() { return type_lists_; } + const CollectionVector& TypeLists() const { return type_lists_; } + CollectionVector& EncodedArrayItems() { return encoded_array_items_; } + const CollectionVector& EncodedArrayItems() const { + return encoded_array_items_; + } + CollectionVector& AnnotationItems() { return annotation_items_; } + const CollectionVector& AnnotationItems() const { return annotation_items_; } + CollectionVector& AnnotationSetItems() { return annotation_set_items_; } + const CollectionVector& AnnotationSetItems() const { + return annotation_set_items_; + } + CollectionVector& AnnotationSetRefLists() { + return annotation_set_ref_lists_; + } + const CollectionVector& AnnotationSetRefLists() const { + return annotation_set_ref_lists_; + } + CollectionVector& AnnotationsDirectoryItems() { + return annotations_directory_items_; + } + const CollectionVector& AnnotationsDirectoryItems() const { + return annotations_directory_items_; + } + CollectionVector& DebugInfoItems() { return debug_info_items_; } + const CollectionVector& DebugInfoItems() const { return debug_info_items_; } + CollectionVector& CodeItems() { return code_items_; } + const CollectionVector& CodeItems() const { return code_items_; } + CollectionVector& ClassDatas() { return class_datas_; } + const CollectionVector& ClassDatas() const { return class_datas_; } + + StringId* GetStringIdOrNullPtr(uint32_t index) { + return index == dex::kDexNoIndex ? nullptr : StringIds()[index]; + } + TypeId* GetTypeIdOrNullPtr(uint16_t index) { + return index == DexFile::kDexNoIndex16 ? nullptr : TypeIds()[index]; + } + + uint32_t MapListOffset() const { return map_list_offset_; } + void SetMapListOffset(uint32_t new_offset) { map_list_offset_ = new_offset; } + + const std::vector& LinkData() const { return link_data_; } + void SetLinkData(std::vector&& link_data) { link_data_ = std::move(link_data); } void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); } @@ -596,7 +515,56 @@ class Header : public Item { uint32_t data_offset_; const bool support_default_methods_; - Collections collections_; + void ConstructorHelper(const uint8_t* magic, + uint32_t checksum, + const uint8_t* signature, + uint32_t endian_tag, + uint32_t file_size, + uint32_t header_size, + uint32_t link_size, + uint32_t link_offset, + uint32_t data_size, + uint32_t data_offset) { + checksum_ = checksum; + endian_tag_ = endian_tag; + file_size_ = file_size; + header_size_ = header_size; + link_size_ = link_size; + link_offset_ = link_offset; + data_size_ = data_size; + data_offset_ = data_offset; + memcpy(magic_, magic, sizeof(magic_)); + memcpy(signature_, signature, sizeof(signature_)); + } + + // Collection vectors own the IR data. + IndexedCollectionVector string_ids_; + IndexedCollectionVector type_ids_; + IndexedCollectionVector proto_ids_; + IndexedCollectionVector field_ids_; + IndexedCollectionVector method_ids_; + IndexedCollectionVector class_defs_; + IndexedCollectionVector call_site_ids_; + IndexedCollectionVector method_handle_items_; + IndexedCollectionVector string_datas_; + IndexedCollectionVector type_lists_; + IndexedCollectionVector encoded_array_items_; + IndexedCollectionVector annotation_items_; + IndexedCollectionVector annotation_set_items_; + IndexedCollectionVector annotation_set_ref_lists_; + IndexedCollectionVector annotations_directory_items_; + // The order of the vectors controls the layout of the output file by index order, to change the + // layout just sort the vector. Note that you may only change the order of the non indexed vectors + // below. Indexed vectors are accessed by indices in other places, changing the sorting order will + // invalidate the existing indices and is not currently supported. + CollectionVector debug_info_items_; + CollectionVector code_items_; + CollectionVector class_datas_; + + uint32_t map_list_offset_ = 0; + + // Link data. + std::vector link_data_; DISALLOW_COPY_AND_ASSIGN(Header); }; @@ -744,6 +712,8 @@ class FieldItem : public Item { : access_flags_(access_flags), field_id_(field_id) { } ~FieldItem() OVERRIDE { } + FieldItem(FieldItem&&) = default; + uint32_t GetAccessFlags() const { return access_flags_; } const FieldId* GetFieldId() const { return field_id_; } @@ -756,7 +726,7 @@ class FieldItem : public Item { DISALLOW_COPY_AND_ASSIGN(FieldItem); }; -using FieldItemVector = std::vector>; +using FieldItemVector = std::vector; class MethodItem : public Item { public: @@ -764,6 +734,8 @@ class MethodItem : public Item { : access_flags_(access_flags), method_id_(method_id), code_(code) { } ~MethodItem() OVERRIDE { } + MethodItem(MethodItem&&) = default; + uint32_t GetAccessFlags() const { return access_flags_; } const MethodId* GetMethodId() const { return method_id_; } CodeItem* GetCodeItem() { return code_; } @@ -778,7 +750,7 @@ class MethodItem : public Item { DISALLOW_COPY_AND_ASSIGN(MethodItem); }; -using MethodItemVector = std::vector>; +using MethodItemVector = std::vector; class EncodedValue { public: diff --git a/dexlayout/dex_ir_builder.cc b/dexlayout/dex_ir_builder.cc index 4f9bcdd7426b4521a9309f13d949aa3cb9b8879f..a04a2349c4bc17e15302892fab18806aa3aa3356 100644 --- a/dexlayout/dex_ir_builder.cc +++ b/dexlayout/dex_ir_builder.cc @@ -20,14 +20,226 @@ #include #include "dex_ir_builder.h" + +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_file_exception_helpers.h" #include "dexlayout.h" namespace art { namespace dex_ir { -static void CheckAndSetRemainingOffsets(const DexFile& dex_file, - Collections* collections, - const Options& options); +static uint64_t ReadVarWidth(const uint8_t** data, uint8_t length, bool sign_extend) { + uint64_t value = 0; + for (uint32_t i = 0; i <= length; i++) { + value |= static_cast(*(*data)++) << (i * 8); + } + if (sign_extend) { + int shift = (7 - length) * 8; + return (static_cast(value) << shift) >> shift; + } + return value; +} + +static uint32_t GetDebugInfoStreamSize(const uint8_t* debug_info_stream) { + const uint8_t* stream = debug_info_stream; + DecodeUnsignedLeb128(&stream); // line_start + uint32_t parameters_size = DecodeUnsignedLeb128(&stream); + for (uint32_t i = 0; i < parameters_size; ++i) { + DecodeUnsignedLeb128P1(&stream); // Parameter name. + } + + for (;;) { + uint8_t opcode = *stream++; + switch (opcode) { + case DexFile::DBG_END_SEQUENCE: + return stream - debug_info_stream; // end of stream. + case DexFile::DBG_ADVANCE_PC: + DecodeUnsignedLeb128(&stream); // addr_diff + break; + case DexFile::DBG_ADVANCE_LINE: + DecodeSignedLeb128(&stream); // line_diff + break; + case DexFile::DBG_START_LOCAL: + DecodeUnsignedLeb128(&stream); // register_num + DecodeUnsignedLeb128P1(&stream); // name_idx + DecodeUnsignedLeb128P1(&stream); // type_idx + break; + case DexFile::DBG_START_LOCAL_EXTENDED: + DecodeUnsignedLeb128(&stream); // register_num + DecodeUnsignedLeb128P1(&stream); // name_idx + DecodeUnsignedLeb128P1(&stream); // type_idx + DecodeUnsignedLeb128P1(&stream); // sig_idx + break; + case DexFile::DBG_END_LOCAL: + case DexFile::DBG_RESTART_LOCAL: + DecodeUnsignedLeb128(&stream); // register_num + break; + case DexFile::DBG_SET_PROLOGUE_END: + case DexFile::DBG_SET_EPILOGUE_BEGIN: + break; + case DexFile::DBG_SET_FILE: { + DecodeUnsignedLeb128P1(&stream); // name_idx + break; + } + default: { + break; + } + } + } +} + +template class CollectionMap : public CollectionBase { + public: + CollectionMap() = default; + virtual ~CollectionMap() OVERRIDE { } + + template + T* CreateAndAddItem(CollectionVector& vector, + bool eagerly_assign_offsets, + uint32_t offset, + Args&&... args) { + T* item = vector.CreateAndAddItem(std::forward(args)...); + DCHECK(!GetExistingObject(offset)); + DCHECK(!item->OffsetAssigned()); + if (eagerly_assign_offsets) { + item->SetOffset(offset); + } + AddItem(item, offset); + return item; + } + + // Returns the existing item if it is already inserted, null otherwise. + T* GetExistingObject(uint32_t offset) { + auto it = collection_.find(offset); + return it != collection_.end() ? it->second : nullptr; + } + + // Lower case for template interop with std::map. + uint32_t size() const { return collection_.size(); } + std::map& Collection() { return collection_; } + + private: + std::map collection_; + + // CollectionMaps do not own the objects they contain, therefore AddItem is supported + // rather than CreateAndAddItem. + void AddItem(T* object, uint32_t offset) { + auto it = collection_.emplace(offset, object); + CHECK(it.second) << "CollectionMap already has an object with offset " << offset << " " + << " and address " << it.first->second; + } + + DISALLOW_COPY_AND_ASSIGN(CollectionMap); +}; + +class BuilderMaps { + public: + BuilderMaps(Header* header, bool eagerly_assign_offsets) + : header_(header), eagerly_assign_offsets_(eagerly_assign_offsets) { } + + void CreateStringId(const DexFile& dex_file, uint32_t i); + void CreateTypeId(const DexFile& dex_file, uint32_t i); + void CreateProtoId(const DexFile& dex_file, uint32_t i); + void CreateFieldId(const DexFile& dex_file, uint32_t i); + void CreateMethodId(const DexFile& dex_file, uint32_t i); + void CreateClassDef(const DexFile& dex_file, uint32_t i); + void CreateCallSiteId(const DexFile& dex_file, uint32_t i); + void CreateMethodHandleItem(const DexFile& dex_file, uint32_t i); + + void CreateCallSitesAndMethodHandles(const DexFile& dex_file); + + TypeList* CreateTypeList(const DexFile::TypeList* type_list, uint32_t offset); + EncodedArrayItem* CreateEncodedArrayItem(const DexFile& dex_file, + const uint8_t* static_data, + uint32_t offset); + AnnotationItem* CreateAnnotationItem(const DexFile& dex_file, + const DexFile::AnnotationItem* annotation); + AnnotationSetItem* CreateAnnotationSetItem(const DexFile& dex_file, + const DexFile::AnnotationSetItem* disk_annotations_item, uint32_t offset); + AnnotationsDirectoryItem* CreateAnnotationsDirectoryItem(const DexFile& dex_file, + const DexFile::AnnotationsDirectoryItem* disk_annotations_item, uint32_t offset); + CodeItem* DedupeOrCreateCodeItem(const DexFile& dex_file, + const DexFile::CodeItem* disk_code_item, + uint32_t offset, + uint32_t dex_method_index); + ClassData* CreateClassData(const DexFile& dex_file, const uint8_t* encoded_data, uint32_t offset); + + void AddAnnotationsFromMapListSection(const DexFile& dex_file, + uint32_t start_offset, + uint32_t count); + + void CheckAndSetRemainingOffsets(const DexFile& dex_file, const Options& options); + + // Sort the vectors buy map order (same order that was used in the input file). + void SortVectorsByMapOrder(); + + private: + bool GetIdsFromByteCode(const CodeItem* code, + std::vector* type_ids, + std::vector* string_ids, + std::vector* method_ids, + std::vector* field_ids); + + bool GetIdFromInstruction(const Instruction* dec_insn, + std::vector* type_ids, + std::vector* string_ids, + std::vector* method_ids, + std::vector* field_ids); + + EncodedValue* ReadEncodedValue(const DexFile& dex_file, const uint8_t** data); + EncodedValue* ReadEncodedValue(const DexFile& dex_file, + const uint8_t** data, + uint8_t type, + uint8_t length); + void ReadEncodedValue(const DexFile& dex_file, + const uint8_t** data, + uint8_t type, + uint8_t length, + EncodedValue* item); + + MethodItem GenerateMethodItem(const DexFile& dex_file, ClassDataItemIterator& cdii); + + ParameterAnnotation* GenerateParameterAnnotation( + const DexFile& dex_file, + MethodId* method_id, + const DexFile::AnnotationSetRefList* annotation_set_ref_list, + uint32_t offset); + + template + Type* CreateAndAddIndexedItem(IndexedCollectionVector& vector, + uint32_t offset, + uint32_t index, + Args&&... args) { + Type* item = vector.CreateAndAddIndexedItem(index, std::forward(args)...); + DCHECK(!item->OffsetAssigned()); + if (eagerly_assign_offsets_) { + item->SetOffset(offset); + } + return item; + } + + Header* header_; + // If we eagerly assign offsets during IR building or later after layout. Must be false if + // changing the layout is enabled. + bool eagerly_assign_offsets_; + + // Note: maps do not have ownership. + CollectionMap string_datas_map_; + CollectionMap type_lists_map_; + CollectionMap encoded_array_items_map_; + CollectionMap annotation_items_map_; + CollectionMap annotation_set_items_map_; + CollectionMap annotation_set_ref_lists_map_; + CollectionMap annotations_directory_items_map_; + CollectionMap debug_info_items_map_; + // Code item maps need to check both the debug info offset and debug info offset, do not use + // CollectionMap. + // First offset is the code item offset, second is the debug info offset. + std::map, CodeItem*> code_items_map_; + CollectionMap class_datas_map_; + + DISALLOW_COPY_AND_ASSIGN(BuilderMaps); +}; Header* DexIrBuilder(const DexFile& dex_file, bool eagerly_assign_offsets, @@ -43,37 +255,42 @@ Header* DexIrBuilder(const DexFile& dex_file, disk_header.link_off_, disk_header.data_size_, disk_header.data_off_, - dex_file.SupportsDefaultMethods()); - Collections& collections = header->GetCollections(); - collections.SetEagerlyAssignOffsets(eagerly_assign_offsets); + dex_file.SupportsDefaultMethods(), + dex_file.NumStringIds(), + dex_file.NumTypeIds(), + dex_file.NumProtoIds(), + dex_file.NumFieldIds(), + dex_file.NumMethodIds(), + dex_file.NumClassDefs()); + BuilderMaps builder_maps(header, eagerly_assign_offsets); // Walk the rest of the header fields. // StringId table. - collections.SetStringIdsOffset(disk_header.string_ids_off_); + header->StringIds().SetOffset(disk_header.string_ids_off_); for (uint32_t i = 0; i < dex_file.NumStringIds(); ++i) { - collections.CreateStringId(dex_file, i); + builder_maps.CreateStringId(dex_file, i); } // TypeId table. - collections.SetTypeIdsOffset(disk_header.type_ids_off_); + header->TypeIds().SetOffset(disk_header.type_ids_off_); for (uint32_t i = 0; i < dex_file.NumTypeIds(); ++i) { - collections.CreateTypeId(dex_file, i); + builder_maps.CreateTypeId(dex_file, i); } // ProtoId table. - collections.SetProtoIdsOffset(disk_header.proto_ids_off_); + header->ProtoIds().SetOffset(disk_header.proto_ids_off_); for (uint32_t i = 0; i < dex_file.NumProtoIds(); ++i) { - collections.CreateProtoId(dex_file, i); + builder_maps.CreateProtoId(dex_file, i); } // FieldId table. - collections.SetFieldIdsOffset(disk_header.field_ids_off_); + header->FieldIds().SetOffset(disk_header.field_ids_off_); for (uint32_t i = 0; i < dex_file.NumFieldIds(); ++i) { - collections.CreateFieldId(dex_file, i); + builder_maps.CreateFieldId(dex_file, i); } // MethodId table. - collections.SetMethodIdsOffset(disk_header.method_ids_off_); + header->MethodIds().SetOffset(disk_header.method_ids_off_); for (uint32_t i = 0; i < dex_file.NumMethodIds(); ++i) { - collections.CreateMethodId(dex_file, i); + builder_maps.CreateMethodId(dex_file, i); } // ClassDef table. - collections.SetClassDefsOffset(disk_header.class_defs_off_); + header->ClassDefs().SetOffset(disk_header.class_defs_off_); for (uint32_t i = 0; i < dex_file.NumClassDefs(); ++i) { if (!options.class_filter_.empty()) { // If the filter is enabled (not empty), filter out classes that don't have a matching @@ -84,28 +301,29 @@ Header* DexIrBuilder(const DexFile& dex_file, continue; } } - collections.CreateClassDef(dex_file, i); + builder_maps.CreateClassDef(dex_file, i); } // MapItem. - collections.SetMapListOffset(disk_header.map_off_); + header->SetMapListOffset(disk_header.map_off_); // CallSiteIds and MethodHandleItems. - collections.CreateCallSitesAndMethodHandles(dex_file); - CheckAndSetRemainingOffsets(dex_file, &collections, options); + builder_maps.CreateCallSitesAndMethodHandles(dex_file); + builder_maps.CheckAndSetRemainingOffsets(dex_file, options); // Sort the vectors by the map order (same order as the file). - collections.SortVectorsByMapOrder(); + builder_maps.SortVectorsByMapOrder(); // Load the link data if it exists. - collections.SetLinkData(std::vector( + header->SetLinkData(std::vector( dex_file.DataBegin() + dex_file.GetHeader().link_off_, dex_file.DataBegin() + dex_file.GetHeader().link_off_ + dex_file.GetHeader().link_size_)); return header; } -static void CheckAndSetRemainingOffsets(const DexFile& dex_file, - Collections* collections, - const Options& options) { +/* + * Get all the types, strings, methods, and fields referred to from bytecode. + */ +void BuilderMaps::CheckAndSetRemainingOffsets(const DexFile& dex_file, const Options& options) { const DexFile::Header& disk_header = dex_file.GetHeader(); // Read MapItems and validate/set remaining offsets. const DexFile::MapList* map = dex_file.GetMapList(); @@ -118,74 +336,74 @@ static void CheckAndSetRemainingOffsets(const DexFile& dex_file, CHECK_EQ(item->offset_, 0u); break; case DexFile::kDexTypeStringIdItem: - CHECK_EQ(item->size_, collections->StringIdsSize()); - CHECK_EQ(item->offset_, collections->StringIdsOffset()); + CHECK_EQ(item->size_, header_->StringIds().Size()); + CHECK_EQ(item->offset_, header_->StringIds().GetOffset()); break; case DexFile::kDexTypeTypeIdItem: - CHECK_EQ(item->size_, collections->TypeIdsSize()); - CHECK_EQ(item->offset_, collections->TypeIdsOffset()); + CHECK_EQ(item->size_, header_->TypeIds().Size()); + CHECK_EQ(item->offset_, header_->TypeIds().GetOffset()); break; case DexFile::kDexTypeProtoIdItem: - CHECK_EQ(item->size_, collections->ProtoIdsSize()); - CHECK_EQ(item->offset_, collections->ProtoIdsOffset()); + CHECK_EQ(item->size_, header_->ProtoIds().Size()); + CHECK_EQ(item->offset_, header_->ProtoIds().GetOffset()); break; case DexFile::kDexTypeFieldIdItem: - CHECK_EQ(item->size_, collections->FieldIdsSize()); - CHECK_EQ(item->offset_, collections->FieldIdsOffset()); + CHECK_EQ(item->size_, header_->FieldIds().Size()); + CHECK_EQ(item->offset_, header_->FieldIds().GetOffset()); break; case DexFile::kDexTypeMethodIdItem: - CHECK_EQ(item->size_, collections->MethodIdsSize()); - CHECK_EQ(item->offset_, collections->MethodIdsOffset()); + CHECK_EQ(item->size_, header_->MethodIds().Size()); + CHECK_EQ(item->offset_, header_->MethodIds().GetOffset()); break; case DexFile::kDexTypeClassDefItem: if (options.class_filter_.empty()) { // The filter may have removed some classes, this will get fixed up during writing. - CHECK_EQ(item->size_, collections->ClassDefsSize()); + CHECK_EQ(item->size_, header_->ClassDefs().Size()); } - CHECK_EQ(item->offset_, collections->ClassDefsOffset()); + CHECK_EQ(item->offset_, header_->ClassDefs().GetOffset()); break; case DexFile::kDexTypeCallSiteIdItem: - CHECK_EQ(item->size_, collections->CallSiteIdsSize()); - CHECK_EQ(item->offset_, collections->CallSiteIdsOffset()); + CHECK_EQ(item->size_, header_->CallSiteIds().Size()); + CHECK_EQ(item->offset_, header_->CallSiteIds().GetOffset()); break; case DexFile::kDexTypeMethodHandleItem: - CHECK_EQ(item->size_, collections->MethodHandleItemsSize()); - CHECK_EQ(item->offset_, collections->MethodHandleItemsOffset()); + CHECK_EQ(item->size_, header_->MethodHandleItems().Size()); + CHECK_EQ(item->offset_, header_->MethodHandleItems().GetOffset()); break; case DexFile::kDexTypeMapList: CHECK_EQ(item->size_, 1u); CHECK_EQ(item->offset_, disk_header.map_off_); break; case DexFile::kDexTypeTypeList: - collections->SetTypeListsOffset(item->offset_); + header_->TypeLists().SetOffset(item->offset_); break; case DexFile::kDexTypeAnnotationSetRefList: - collections->SetAnnotationSetRefListsOffset(item->offset_); + header_->AnnotationSetRefLists().SetOffset(item->offset_); break; case DexFile::kDexTypeAnnotationSetItem: - collections->SetAnnotationSetItemsOffset(item->offset_); + header_->AnnotationSetItems().SetOffset(item->offset_); break; case DexFile::kDexTypeClassDataItem: - collections->SetClassDatasOffset(item->offset_); + header_->ClassDatas().SetOffset(item->offset_); break; case DexFile::kDexTypeCodeItem: - collections->SetCodeItemsOffset(item->offset_); + header_->CodeItems().SetOffset(item->offset_); break; case DexFile::kDexTypeStringDataItem: - collections->SetStringDatasOffset(item->offset_); + header_->StringDatas().SetOffset(item->offset_); break; case DexFile::kDexTypeDebugInfoItem: - collections->SetDebugInfoItemsOffset(item->offset_); + header_->DebugInfoItems().SetOffset(item->offset_); break; case DexFile::kDexTypeAnnotationItem: - collections->SetAnnotationItemsOffset(item->offset_); - collections->AddAnnotationsFromMapListSection(dex_file, item->offset_, item->size_); + header_->AnnotationItems().SetOffset(item->offset_); + AddAnnotationsFromMapListSection(dex_file, item->offset_, item->size_); break; case DexFile::kDexTypeEncodedArrayItem: - collections->SetEncodedArrayItemsOffset(item->offset_); + header_->EncodedArrayItems().SetOffset(item->offset_); break; case DexFile::kDexTypeAnnotationsDirectoryItem: - collections->SetAnnotationsDirectoryItemsOffset(item->offset_); + header_->AnnotationsDirectoryItems().SetOffset(item->offset_); break; default: LOG(ERROR) << "Unknown map list item type."; @@ -193,5 +411,798 @@ static void CheckAndSetRemainingOffsets(const DexFile& dex_file, } } +void BuilderMaps::CreateStringId(const DexFile& dex_file, uint32_t i) { + const DexFile::StringId& disk_string_id = dex_file.GetStringId(dex::StringIndex(i)); + StringData* string_data = + string_datas_map_.CreateAndAddItem(header_->StringDatas(), + eagerly_assign_offsets_, + disk_string_id.string_data_off_, + dex_file.GetStringData(disk_string_id)); + CreateAndAddIndexedItem(header_->StringIds(), + header_->StringIds().GetOffset() + i * StringId::ItemSize(), + i, + string_data); +} + +void BuilderMaps::CreateTypeId(const DexFile& dex_file, uint32_t i) { + const DexFile::TypeId& disk_type_id = dex_file.GetTypeId(dex::TypeIndex(i)); + CreateAndAddIndexedItem(header_->TypeIds(), + header_->TypeIds().GetOffset() + i * TypeId::ItemSize(), + i, + header_->StringIds()[disk_type_id.descriptor_idx_.index_]); +} + +void BuilderMaps::CreateProtoId(const DexFile& dex_file, uint32_t i) { + const DexFile::ProtoId& disk_proto_id = dex_file.GetProtoId(dex::ProtoIndex(i)); + const DexFile::TypeList* type_list = dex_file.GetProtoParameters(disk_proto_id); + TypeList* parameter_type_list = CreateTypeList(type_list, disk_proto_id.parameters_off_); + + CreateAndAddIndexedItem(header_->ProtoIds(), + header_->ProtoIds().GetOffset() + i * ProtoId::ItemSize(), + i, + header_->StringIds()[disk_proto_id.shorty_idx_.index_], + header_->TypeIds()[disk_proto_id.return_type_idx_.index_], + parameter_type_list); +} + +void BuilderMaps::CreateFieldId(const DexFile& dex_file, uint32_t i) { + const DexFile::FieldId& disk_field_id = dex_file.GetFieldId(i); + CreateAndAddIndexedItem(header_->FieldIds(), + header_->FieldIds().GetOffset() + i * FieldId::ItemSize(), + i, + header_->TypeIds()[disk_field_id.class_idx_.index_], + header_->TypeIds()[disk_field_id.type_idx_.index_], + header_->StringIds()[disk_field_id.name_idx_.index_]); +} + +void BuilderMaps::CreateMethodId(const DexFile& dex_file, uint32_t i) { + const DexFile::MethodId& disk_method_id = dex_file.GetMethodId(i); + CreateAndAddIndexedItem(header_->MethodIds(), + header_->MethodIds().GetOffset() + i * MethodId::ItemSize(), + i, + header_->TypeIds()[disk_method_id.class_idx_.index_], + header_->ProtoIds()[disk_method_id.proto_idx_.index_], + header_->StringIds()[disk_method_id.name_idx_.index_]); +} + +void BuilderMaps::CreateClassDef(const DexFile& dex_file, uint32_t i) { + const DexFile::ClassDef& disk_class_def = dex_file.GetClassDef(i); + const TypeId* class_type = header_->TypeIds()[disk_class_def.class_idx_.index_]; + uint32_t access_flags = disk_class_def.access_flags_; + const TypeId* superclass = header_->GetTypeIdOrNullPtr(disk_class_def.superclass_idx_.index_); + + const DexFile::TypeList* type_list = dex_file.GetInterfacesList(disk_class_def); + TypeList* interfaces_type_list = CreateTypeList(type_list, disk_class_def.interfaces_off_); + + const StringId* source_file = + header_->GetStringIdOrNullPtr(disk_class_def.source_file_idx_.index_); + // Annotations. + AnnotationsDirectoryItem* annotations = nullptr; + const DexFile::AnnotationsDirectoryItem* disk_annotations_directory_item = + dex_file.GetAnnotationsDirectory(disk_class_def); + if (disk_annotations_directory_item != nullptr) { + annotations = CreateAnnotationsDirectoryItem( + dex_file, disk_annotations_directory_item, disk_class_def.annotations_off_); + } + // Static field initializers. + const uint8_t* static_data = dex_file.GetEncodedStaticFieldValuesArray(disk_class_def); + EncodedArrayItem* static_values = + CreateEncodedArrayItem(dex_file, static_data, disk_class_def.static_values_off_); + ClassData* class_data = CreateClassData( + dex_file, dex_file.GetClassData(disk_class_def), disk_class_def.class_data_off_); + CreateAndAddIndexedItem(header_->ClassDefs(), + header_->ClassDefs().GetOffset() + i * ClassDef::ItemSize(), + i, + class_type, + access_flags, + superclass, + interfaces_type_list, + source_file, + annotations, + static_values, + class_data); +} + +void BuilderMaps::CreateCallSiteId(const DexFile& dex_file, uint32_t i) { + const DexFile::CallSiteIdItem& disk_call_site_id = dex_file.GetCallSiteId(i); + const uint8_t* disk_call_item_ptr = dex_file.DataBegin() + disk_call_site_id.data_off_; + EncodedArrayItem* call_site_item = + CreateEncodedArrayItem(dex_file, disk_call_item_ptr, disk_call_site_id.data_off_); + + CreateAndAddIndexedItem(header_->CallSiteIds(), + header_->CallSiteIds().GetOffset() + i * CallSiteId::ItemSize(), + i, + call_site_item); +} + +void BuilderMaps::CreateMethodHandleItem(const DexFile& dex_file, uint32_t i) { + const DexFile::MethodHandleItem& disk_method_handle = dex_file.GetMethodHandle(i); + uint16_t index = disk_method_handle.field_or_method_idx_; + DexFile::MethodHandleType type = + static_cast(disk_method_handle.method_handle_type_); + bool is_invoke = type == DexFile::MethodHandleType::kInvokeStatic || + type == DexFile::MethodHandleType::kInvokeInstance || + type == DexFile::MethodHandleType::kInvokeConstructor || + type == DexFile::MethodHandleType::kInvokeDirect || + type == DexFile::MethodHandleType::kInvokeInterface; + static_assert(DexFile::MethodHandleType::kLast == DexFile::MethodHandleType::kInvokeInterface, + "Unexpected method handle types."); + IndexedItem* field_or_method_id; + if (is_invoke) { + field_or_method_id = header_->MethodIds()[index]; + } else { + field_or_method_id = header_->FieldIds()[index]; + } + CreateAndAddIndexedItem(header_->MethodHandleItems(), + header_->MethodHandleItems().GetOffset() + + i * MethodHandleItem::ItemSize(), + i, + type, + field_or_method_id); +} + +void BuilderMaps::CreateCallSitesAndMethodHandles(const DexFile& dex_file) { + // Iterate through the map list and set the offset of the CallSiteIds and MethodHandleItems. + const DexFile::MapList* map = dex_file.GetMapList(); + for (uint32_t i = 0; i < map->size_; ++i) { + const DexFile::MapItem* item = map->list_ + i; + switch (item->type_) { + case DexFile::kDexTypeCallSiteIdItem: + header_->CallSiteIds().SetOffset(item->offset_); + break; + case DexFile::kDexTypeMethodHandleItem: + header_->MethodHandleItems().SetOffset(item->offset_); + break; + default: + break; + } + } + // Populate MethodHandleItems first (CallSiteIds may depend on them). + for (uint32_t i = 0; i < dex_file.NumMethodHandles(); i++) { + CreateMethodHandleItem(dex_file, i); + } + // Populate CallSiteIds. + for (uint32_t i = 0; i < dex_file.NumCallSiteIds(); i++) { + CreateCallSiteId(dex_file, i); + } +} + +TypeList* BuilderMaps::CreateTypeList(const DexFile::TypeList* dex_type_list, uint32_t offset) { + if (dex_type_list == nullptr) { + return nullptr; + } + TypeList* type_list = type_lists_map_.GetExistingObject(offset); + if (type_list == nullptr) { + TypeIdVector* type_vector = new TypeIdVector(); + uint32_t size = dex_type_list->Size(); + for (uint32_t index = 0; index < size; ++index) { + type_vector->push_back(header_->TypeIds()[ + dex_type_list->GetTypeItem(index).type_idx_.index_]); + } + type_list = type_lists_map_.CreateAndAddItem(header_->TypeLists(), + eagerly_assign_offsets_, + offset, + type_vector); + } + return type_list; +} + +EncodedArrayItem* BuilderMaps::CreateEncodedArrayItem(const DexFile& dex_file, + const uint8_t* static_data, + uint32_t offset) { + if (static_data == nullptr) { + return nullptr; + } + EncodedArrayItem* encoded_array_item = encoded_array_items_map_.GetExistingObject(offset); + if (encoded_array_item == nullptr) { + uint32_t size = DecodeUnsignedLeb128(&static_data); + EncodedValueVector* values = new EncodedValueVector(); + for (uint32_t i = 0; i < size; ++i) { + values->push_back(std::unique_ptr(ReadEncodedValue(dex_file, &static_data))); + } + // TODO: Calculate the size of the encoded array. + encoded_array_item = encoded_array_items_map_.CreateAndAddItem(header_->EncodedArrayItems(), + eagerly_assign_offsets_, + offset, + values); + } + return encoded_array_item; +} + +void BuilderMaps::AddAnnotationsFromMapListSection(const DexFile& dex_file, + uint32_t start_offset, + uint32_t count) { + uint32_t current_offset = start_offset; + for (size_t i = 0; i < count; ++i) { + // Annotation that we didn't process already, add it to the set. + const DexFile::AnnotationItem* annotation = dex_file.GetAnnotationItemAtOffset(current_offset); + AnnotationItem* annotation_item = CreateAnnotationItem(dex_file, annotation); + DCHECK(annotation_item != nullptr); + current_offset += annotation_item->GetSize(); + } +} + +AnnotationItem* BuilderMaps::CreateAnnotationItem(const DexFile& dex_file, + const DexFile::AnnotationItem* annotation) { + const uint8_t* const start_data = reinterpret_cast(annotation); + const uint32_t offset = start_data - dex_file.DataBegin(); + AnnotationItem* annotation_item = annotation_items_map_.GetExistingObject(offset); + if (annotation_item == nullptr) { + uint8_t visibility = annotation->visibility_; + const uint8_t* annotation_data = annotation->annotation_; + std::unique_ptr encoded_value( + ReadEncodedValue(dex_file, &annotation_data, DexFile::kDexAnnotationAnnotation, 0)); + annotation_item = + annotation_items_map_.CreateAndAddItem(header_->AnnotationItems(), + eagerly_assign_offsets_, + offset, + visibility, + encoded_value->ReleaseEncodedAnnotation()); + annotation_item->SetSize(annotation_data - start_data); + } + return annotation_item; +} + + +AnnotationSetItem* BuilderMaps::CreateAnnotationSetItem(const DexFile& dex_file, + const DexFile::AnnotationSetItem* disk_annotations_item, uint32_t offset) { + if (disk_annotations_item == nullptr || (disk_annotations_item->size_ == 0 && offset == 0)) { + return nullptr; + } + AnnotationSetItem* annotation_set_item = annotation_set_items_map_.GetExistingObject(offset); + if (annotation_set_item == nullptr) { + std::vector* items = new std::vector(); + for (uint32_t i = 0; i < disk_annotations_item->size_; ++i) { + const DexFile::AnnotationItem* annotation = + dex_file.GetAnnotationItem(disk_annotations_item, i); + if (annotation == nullptr) { + continue; + } + AnnotationItem* annotation_item = CreateAnnotationItem(dex_file, annotation); + items->push_back(annotation_item); + } + annotation_set_item = + annotation_set_items_map_.CreateAndAddItem(header_->AnnotationSetItems(), + eagerly_assign_offsets_, + offset, + items); + } + return annotation_set_item; +} + +AnnotationsDirectoryItem* BuilderMaps::CreateAnnotationsDirectoryItem(const DexFile& dex_file, + const DexFile::AnnotationsDirectoryItem* disk_annotations_item, uint32_t offset) { + AnnotationsDirectoryItem* annotations_directory_item = + annotations_directory_items_map_.GetExistingObject(offset); + if (annotations_directory_item != nullptr) { + return annotations_directory_item; + } + const DexFile::AnnotationSetItem* class_set_item = + dex_file.GetClassAnnotationSet(disk_annotations_item); + AnnotationSetItem* class_annotation = nullptr; + if (class_set_item != nullptr) { + uint32_t item_offset = disk_annotations_item->class_annotations_off_; + class_annotation = CreateAnnotationSetItem(dex_file, class_set_item, item_offset); + } + const DexFile::FieldAnnotationsItem* fields = + dex_file.GetFieldAnnotations(disk_annotations_item); + FieldAnnotationVector* field_annotations = nullptr; + if (fields != nullptr) { + field_annotations = new FieldAnnotationVector(); + for (uint32_t i = 0; i < disk_annotations_item->fields_size_; ++i) { + FieldId* field_id = header_->FieldIds()[fields[i].field_idx_]; + const DexFile::AnnotationSetItem* field_set_item = + dex_file.GetFieldAnnotationSetItem(fields[i]); + uint32_t annotation_set_offset = fields[i].annotations_off_; + AnnotationSetItem* annotation_set_item = + CreateAnnotationSetItem(dex_file, field_set_item, annotation_set_offset); + field_annotations->push_back(std::unique_ptr( + new FieldAnnotation(field_id, annotation_set_item))); + } + } + const DexFile::MethodAnnotationsItem* methods = + dex_file.GetMethodAnnotations(disk_annotations_item); + MethodAnnotationVector* method_annotations = nullptr; + if (methods != nullptr) { + method_annotations = new MethodAnnotationVector(); + for (uint32_t i = 0; i < disk_annotations_item->methods_size_; ++i) { + MethodId* method_id = header_->MethodIds()[methods[i].method_idx_]; + const DexFile::AnnotationSetItem* method_set_item = + dex_file.GetMethodAnnotationSetItem(methods[i]); + uint32_t annotation_set_offset = methods[i].annotations_off_; + AnnotationSetItem* annotation_set_item = + CreateAnnotationSetItem(dex_file, method_set_item, annotation_set_offset); + method_annotations->push_back(std::unique_ptr( + new MethodAnnotation(method_id, annotation_set_item))); + } + } + const DexFile::ParameterAnnotationsItem* parameters = + dex_file.GetParameterAnnotations(disk_annotations_item); + ParameterAnnotationVector* parameter_annotations = nullptr; + if (parameters != nullptr) { + parameter_annotations = new ParameterAnnotationVector(); + for (uint32_t i = 0; i < disk_annotations_item->parameters_size_; ++i) { + MethodId* method_id = header_->MethodIds()[parameters[i].method_idx_]; + const DexFile::AnnotationSetRefList* list = + dex_file.GetParameterAnnotationSetRefList(¶meters[i]); + parameter_annotations->push_back(std::unique_ptr( + GenerateParameterAnnotation(dex_file, method_id, list, parameters[i].annotations_off_))); + } + } + // TODO: Calculate the size of the annotations directory. + return annotations_directory_items_map_.CreateAndAddItem(header_->AnnotationsDirectoryItems(), + eagerly_assign_offsets_, + offset, + class_annotation, + field_annotations, + method_annotations, + parameter_annotations); +} + +CodeItem* BuilderMaps::DedupeOrCreateCodeItem(const DexFile& dex_file, + const DexFile::CodeItem* disk_code_item, + uint32_t offset, + uint32_t dex_method_index) { + if (disk_code_item == nullptr) { + return nullptr; + } + CodeItemDebugInfoAccessor accessor(dex_file, disk_code_item, dex_method_index); + const uint32_t debug_info_offset = accessor.DebugInfoOffset(); + + // Create the offsets pair and dedupe based on it. + std::pair offsets_pair(offset, debug_info_offset); + auto existing = code_items_map_.find(offsets_pair); + if (existing != code_items_map_.end()) { + return existing->second; + } + + const uint8_t* debug_info_stream = dex_file.GetDebugInfoStream(debug_info_offset); + DebugInfoItem* debug_info = nullptr; + if (debug_info_stream != nullptr) { + debug_info = debug_info_items_map_.GetExistingObject(debug_info_offset); + if (debug_info == nullptr) { + uint32_t debug_info_size = GetDebugInfoStreamSize(debug_info_stream); + uint8_t* debug_info_buffer = new uint8_t[debug_info_size]; + memcpy(debug_info_buffer, debug_info_stream, debug_info_size); + debug_info = debug_info_items_map_.CreateAndAddItem(header_->DebugInfoItems(), + eagerly_assign_offsets_, + debug_info_offset, + debug_info_size, + debug_info_buffer); + } + } + + uint32_t insns_size = accessor.InsnsSizeInCodeUnits(); + uint16_t* insns = new uint16_t[insns_size]; + memcpy(insns, accessor.Insns(), insns_size * sizeof(uint16_t)); + + TryItemVector* tries = nullptr; + CatchHandlerVector* handler_list = nullptr; + if (accessor.TriesSize() > 0) { + tries = new TryItemVector(); + handler_list = new CatchHandlerVector(); + for (const DexFile::TryItem& disk_try_item : accessor.TryItems()) { + uint32_t start_addr = disk_try_item.start_addr_; + uint16_t insn_count = disk_try_item.insn_count_; + uint16_t handler_off = disk_try_item.handler_off_; + const CatchHandler* handlers = nullptr; + for (std::unique_ptr& existing_handlers : *handler_list) { + if (handler_off == existing_handlers->GetListOffset()) { + handlers = existing_handlers.get(); + break; + } + } + if (handlers == nullptr) { + bool catch_all = false; + TypeAddrPairVector* addr_pairs = new TypeAddrPairVector(); + for (CatchHandlerIterator it(accessor, disk_try_item); it.HasNext(); it.Next()) { + const dex::TypeIndex type_index = it.GetHandlerTypeIndex(); + const TypeId* type_id = header_->GetTypeIdOrNullPtr(type_index.index_); + catch_all |= type_id == nullptr; + addr_pairs->push_back(std::unique_ptr( + new TypeAddrPair(type_id, it.GetHandlerAddress()))); + } + handlers = new CatchHandler(catch_all, handler_off, addr_pairs); + handler_list->push_back(std::unique_ptr(handlers)); + } + TryItem* try_item = new TryItem(start_addr, insn_count, handlers); + tries->push_back(std::unique_ptr(try_item)); + } + // Manually walk catch handlers list and add any missing handlers unreferenced by try items. + const uint8_t* handlers_base = accessor.GetCatchHandlerData(); + const uint8_t* handlers_data = handlers_base; + uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_data); + while (handlers_size > handler_list->size()) { + bool already_added = false; + uint16_t handler_off = handlers_data - handlers_base; + for (std::unique_ptr& existing_handlers : *handler_list) { + if (handler_off == existing_handlers->GetListOffset()) { + already_added = true; + break; + } + } + int32_t size = DecodeSignedLeb128(&handlers_data); + bool has_catch_all = size <= 0; + if (has_catch_all) { + size = -size; + } + if (already_added) { + for (int32_t i = 0; i < size; i++) { + DecodeUnsignedLeb128(&handlers_data); + DecodeUnsignedLeb128(&handlers_data); + } + if (has_catch_all) { + DecodeUnsignedLeb128(&handlers_data); + } + continue; + } + TypeAddrPairVector* addr_pairs = new TypeAddrPairVector(); + for (int32_t i = 0; i < size; i++) { + const TypeId* type_id = + header_->GetTypeIdOrNullPtr(DecodeUnsignedLeb128(&handlers_data)); + uint32_t addr = DecodeUnsignedLeb128(&handlers_data); + addr_pairs->push_back( + std::unique_ptr(new TypeAddrPair(type_id, addr))); + } + if (has_catch_all) { + uint32_t addr = DecodeUnsignedLeb128(&handlers_data); + addr_pairs->push_back( + std::unique_ptr(new TypeAddrPair(nullptr, addr))); + } + const CatchHandler* handler = new CatchHandler(has_catch_all, handler_off, addr_pairs); + handler_list->push_back(std::unique_ptr(handler)); + } + } + + uint32_t size = dex_file.GetCodeItemSize(*disk_code_item); + CodeItem* code_item = header_->CodeItems().CreateAndAddItem(accessor.RegistersSize(), + accessor.InsSize(), + accessor.OutsSize(), + debug_info, + insns_size, + insns, + tries, + handler_list); + code_item->SetSize(size); + + // Add the code item to the map. + DCHECK(!code_item->OffsetAssigned()); + if (eagerly_assign_offsets_) { + code_item->SetOffset(offset); + } + code_items_map_.emplace(offsets_pair, code_item); + + // Add "fixup" references to types, strings, methods, and fields. + // This is temporary, as we will probably want more detailed parsing of the + // instructions here. + std::vector type_ids; + std::vector string_ids; + std::vector method_ids; + std::vector field_ids; + if (GetIdsFromByteCode(code_item, + /*out*/ &type_ids, + /*out*/ &string_ids, + /*out*/ &method_ids, + /*out*/ &field_ids)) { + CodeFixups* fixups = new CodeFixups(std::move(type_ids), + std::move(string_ids), + std::move(method_ids), + std::move(field_ids)); + code_item->SetCodeFixups(fixups); + } + + return code_item; +} + +ClassData* BuilderMaps::CreateClassData( + const DexFile& dex_file, const uint8_t* encoded_data, uint32_t offset) { + // Read the fields and methods defined by the class, resolving the circular reference from those + // to classes by setting class at the same time. + ClassData* class_data = class_datas_map_.GetExistingObject(offset); + if (class_data == nullptr && encoded_data != nullptr) { + ClassDataItemIterator cdii(dex_file, encoded_data); + // Static fields. + FieldItemVector* static_fields = new FieldItemVector(); + for (; cdii.HasNextStaticField(); cdii.Next()) { + FieldId* field_item = header_->FieldIds()[cdii.GetMemberIndex()]; + uint32_t access_flags = cdii.GetRawMemberAccessFlags(); + static_fields->emplace_back(access_flags, field_item); + } + // Instance fields. + FieldItemVector* instance_fields = new FieldItemVector(); + for (; cdii.HasNextInstanceField(); cdii.Next()) { + FieldId* field_item = header_->FieldIds()[cdii.GetMemberIndex()]; + uint32_t access_flags = cdii.GetRawMemberAccessFlags(); + instance_fields->emplace_back(access_flags, field_item); + } + // Direct methods. + MethodItemVector* direct_methods = new MethodItemVector(); + for (; cdii.HasNextDirectMethod(); cdii.Next()) { + direct_methods->push_back(GenerateMethodItem(dex_file, cdii)); + } + // Virtual methods. + MethodItemVector* virtual_methods = new MethodItemVector(); + for (; cdii.HasNextVirtualMethod(); cdii.Next()) { + virtual_methods->push_back(GenerateMethodItem(dex_file, cdii)); + } + class_data = class_datas_map_.CreateAndAddItem(header_->ClassDatas(), + eagerly_assign_offsets_, + offset, + static_fields, + instance_fields, + direct_methods, + virtual_methods); + class_data->SetSize(cdii.EndDataPointer() - encoded_data); + } + return class_data; +} + +void BuilderMaps::SortVectorsByMapOrder() { + header_->StringDatas().SortByMapOrder(string_datas_map_.Collection()); + header_->TypeLists().SortByMapOrder(type_lists_map_.Collection()); + header_->EncodedArrayItems().SortByMapOrder(encoded_array_items_map_.Collection()); + header_->AnnotationItems().SortByMapOrder(annotation_items_map_.Collection()); + header_->AnnotationSetItems().SortByMapOrder(annotation_set_items_map_.Collection()); + header_->AnnotationSetRefLists().SortByMapOrder(annotation_set_ref_lists_map_.Collection()); + header_->AnnotationsDirectoryItems().SortByMapOrder( + annotations_directory_items_map_.Collection()); + header_->DebugInfoItems().SortByMapOrder(debug_info_items_map_.Collection()); + header_->CodeItems().SortByMapOrder(code_items_map_); + header_->ClassDatas().SortByMapOrder(class_datas_map_.Collection()); +} + +bool BuilderMaps::GetIdsFromByteCode(const CodeItem* code, + std::vector* type_ids, + std::vector* string_ids, + std::vector* method_ids, + std::vector* field_ids) { + bool has_id = false; + IterationRange instructions = code->Instructions(); + SafeDexInstructionIterator it(instructions.begin(), instructions.end()); + for (; !it.IsErrorState() && it < instructions.end(); ++it) { + // In case the instruction goes past the end of the code item, make sure to not process it. + SafeDexInstructionIterator next = it; + ++next; + if (next.IsErrorState()) { + break; + } + has_id |= GetIdFromInstruction(&it.Inst(), type_ids, string_ids, method_ids, field_ids); + } // for + return has_id; +} + +bool BuilderMaps::GetIdFromInstruction(const Instruction* dec_insn, + std::vector* type_ids, + std::vector* string_ids, + std::vector* method_ids, + std::vector* field_ids) { + // Determine index and width of the string. + uint32_t index = 0; + switch (Instruction::FormatOf(dec_insn->Opcode())) { + // SOME NOT SUPPORTED: + // case Instruction::k20bc: + case Instruction::k21c: + case Instruction::k35c: + // case Instruction::k35ms: + case Instruction::k3rc: + // case Instruction::k3rms: + // case Instruction::k35mi: + // case Instruction::k3rmi: + case Instruction::k45cc: + case Instruction::k4rcc: + index = dec_insn->VRegB(); + break; + case Instruction::k31c: + index = dec_insn->VRegB(); + break; + case Instruction::k22c: + // case Instruction::k22cs: + index = dec_insn->VRegC(); + break; + default: + break; + } // switch + + // Determine index type, and add reference to the appropriate collection. + switch (Instruction::IndexTypeOf(dec_insn->Opcode())) { + case Instruction::kIndexTypeRef: + if (index < header_->TypeIds().Size()) { + type_ids->push_back(header_->TypeIds()[index]); + return true; + } + break; + case Instruction::kIndexStringRef: + if (index < header_->StringIds().Size()) { + string_ids->push_back(header_->StringIds()[index]); + return true; + } + break; + case Instruction::kIndexMethodRef: + case Instruction::kIndexMethodAndProtoRef: + if (index < header_->MethodIds().Size()) { + method_ids->push_back(header_->MethodIds()[index]); + return true; + } + break; + case Instruction::kIndexFieldRef: + if (index < header_->FieldIds().Size()) { + field_ids->push_back(header_->FieldIds()[index]); + return true; + } + break; + case Instruction::kIndexUnknown: + case Instruction::kIndexNone: + case Instruction::kIndexVtableOffset: + case Instruction::kIndexFieldOffset: + default: + break; + } // switch + return false; +} + +EncodedValue* BuilderMaps::ReadEncodedValue(const DexFile& dex_file, const uint8_t** data) { + const uint8_t encoded_value = *(*data)++; + const uint8_t type = encoded_value & 0x1f; + EncodedValue* item = new EncodedValue(type); + ReadEncodedValue(dex_file, data, type, encoded_value >> 5, item); + return item; +} + +EncodedValue* BuilderMaps::ReadEncodedValue(const DexFile& dex_file, + const uint8_t** data, + uint8_t type, + uint8_t length) { + EncodedValue* item = new EncodedValue(type); + ReadEncodedValue(dex_file, data, type, length, item); + return item; +} + +void BuilderMaps::ReadEncodedValue(const DexFile& dex_file, + const uint8_t** data, + uint8_t type, + uint8_t length, + EncodedValue* item) { + switch (type) { + case DexFile::kDexAnnotationByte: + item->SetByte(static_cast(ReadVarWidth(data, length, false))); + break; + case DexFile::kDexAnnotationShort: + item->SetShort(static_cast(ReadVarWidth(data, length, true))); + break; + case DexFile::kDexAnnotationChar: + item->SetChar(static_cast(ReadVarWidth(data, length, false))); + break; + case DexFile::kDexAnnotationInt: + item->SetInt(static_cast(ReadVarWidth(data, length, true))); + break; + case DexFile::kDexAnnotationLong: + item->SetLong(static_cast(ReadVarWidth(data, length, true))); + break; + case DexFile::kDexAnnotationFloat: { + // Fill on right. + union { + float f; + uint32_t data; + } conv; + conv.data = static_cast(ReadVarWidth(data, length, false)) << (3 - length) * 8; + item->SetFloat(conv.f); + break; + } + case DexFile::kDexAnnotationDouble: { + // Fill on right. + union { + double d; + uint64_t data; + } conv; + conv.data = ReadVarWidth(data, length, false) << (7 - length) * 8; + item->SetDouble(conv.d); + break; + } + case DexFile::kDexAnnotationMethodType: { + const uint32_t proto_index = static_cast(ReadVarWidth(data, length, false)); + item->SetProtoId(header_->ProtoIds()[proto_index]); + break; + } + case DexFile::kDexAnnotationMethodHandle: { + const uint32_t method_handle_index = static_cast(ReadVarWidth(data, length, false)); + item->SetMethodHandle(header_->MethodHandleItems()[method_handle_index]); + break; + } + case DexFile::kDexAnnotationString: { + const uint32_t string_index = static_cast(ReadVarWidth(data, length, false)); + item->SetStringId(header_->StringIds()[string_index]); + break; + } + case DexFile::kDexAnnotationType: { + const uint32_t string_index = static_cast(ReadVarWidth(data, length, false)); + item->SetTypeId(header_->TypeIds()[string_index]); + break; + } + case DexFile::kDexAnnotationField: + case DexFile::kDexAnnotationEnum: { + const uint32_t field_index = static_cast(ReadVarWidth(data, length, false)); + item->SetFieldId(header_->FieldIds()[field_index]); + break; + } + case DexFile::kDexAnnotationMethod: { + const uint32_t method_index = static_cast(ReadVarWidth(data, length, false)); + item->SetMethodId(header_->MethodIds()[method_index]); + break; + } + case DexFile::kDexAnnotationArray: { + EncodedValueVector* values = new EncodedValueVector(); + const uint32_t offset = *data - dex_file.DataBegin(); + const uint32_t size = DecodeUnsignedLeb128(data); + // Decode all elements. + for (uint32_t i = 0; i < size; i++) { + values->push_back(std::unique_ptr(ReadEncodedValue(dex_file, data))); + } + EncodedArrayItem* array_item = new EncodedArrayItem(values); + if (eagerly_assign_offsets_) { + array_item->SetOffset(offset); + } + item->SetEncodedArray(array_item); + break; + } + case DexFile::kDexAnnotationAnnotation: { + AnnotationElementVector* elements = new AnnotationElementVector(); + const uint32_t type_idx = DecodeUnsignedLeb128(data); + const uint32_t size = DecodeUnsignedLeb128(data); + // Decode all name=value pairs. + for (uint32_t i = 0; i < size; i++) { + const uint32_t name_index = DecodeUnsignedLeb128(data); + elements->push_back(std::unique_ptr( + new AnnotationElement(header_->StringIds()[name_index], + ReadEncodedValue(dex_file, data)))); + } + item->SetEncodedAnnotation(new EncodedAnnotation(header_->TypeIds()[type_idx], elements)); + break; + } + case DexFile::kDexAnnotationNull: + break; + case DexFile::kDexAnnotationBoolean: + item->SetBoolean(length != 0); + break; + default: + break; + } +} + +MethodItem BuilderMaps::GenerateMethodItem(const DexFile& dex_file, ClassDataItemIterator& cdii) { + MethodId* method_id = header_->MethodIds()[cdii.GetMemberIndex()]; + uint32_t access_flags = cdii.GetRawMemberAccessFlags(); + const DexFile::CodeItem* disk_code_item = cdii.GetMethodCodeItem(); + // Temporary hack to prevent incorrectly deduping code items if they have the same offset since + // they may have different debug info streams. + CodeItem* code_item = DedupeOrCreateCodeItem(dex_file, + disk_code_item, + cdii.GetMethodCodeItemOffset(), + cdii.GetMemberIndex()); + return MethodItem(access_flags, method_id, code_item); +} + +ParameterAnnotation* BuilderMaps::GenerateParameterAnnotation( + const DexFile& dex_file, + MethodId* method_id, + const DexFile::AnnotationSetRefList* annotation_set_ref_list, + uint32_t offset) { + AnnotationSetRefList* set_ref_list = annotation_set_ref_lists_map_.GetExistingObject(offset); + if (set_ref_list == nullptr) { + std::vector* annotations = new std::vector(); + for (uint32_t i = 0; i < annotation_set_ref_list->size_; ++i) { + const DexFile::AnnotationSetItem* annotation_set_item = + dex_file.GetSetRefItemItem(&annotation_set_ref_list->list_[i]); + uint32_t set_offset = annotation_set_ref_list->list_[i].annotations_off_; + annotations->push_back(CreateAnnotationSetItem(dex_file, annotation_set_item, set_offset)); + } + set_ref_list = + annotation_set_ref_lists_map_.CreateAndAddItem(header_->AnnotationSetRefLists(), + eagerly_assign_offsets_, + offset, + annotations); + } + return new ParameterAnnotation(method_id, set_ref_list); +} + } // namespace dex_ir } // namespace art diff --git a/dexlayout/dex_verify.cc b/dexlayout/dex_verify.cc index 18ddc86e0c1b5c26ce353bb32f05298b79ff5a23..718d66feaaca600ea49b50bc1292fdea6c423ddb 100644 --- a/dexlayout/dex_verify.cc +++ b/dexlayout/dex_verify.cc @@ -31,38 +31,42 @@ using android::base::StringPrintf; bool VerifyOutputDexFile(dex_ir::Header* orig_header, dex_ir::Header* output_header, std::string* error_msg) { - dex_ir::Collections& orig = orig_header->GetCollections(); - dex_ir::Collections& output = output_header->GetCollections(); - // Compare all id sections. They have a defined order that can't be changed by dexlayout. - if (!VerifyIds(orig.StringIds(), output.StringIds(), "string ids", error_msg) || - !VerifyIds(orig.TypeIds(), output.TypeIds(), "type ids", error_msg) || - !VerifyIds(orig.ProtoIds(), output.ProtoIds(), "proto ids", error_msg) || - !VerifyIds(orig.FieldIds(), output.FieldIds(), "field ids", error_msg) || - !VerifyIds(orig.MethodIds(), output.MethodIds(), "method ids", error_msg)) { + if (!VerifyIds(orig_header->StringIds(), output_header->StringIds(), "string ids", error_msg) || + !VerifyIds(orig_header->TypeIds(), output_header->TypeIds(), "type ids", error_msg) || + !VerifyIds(orig_header->ProtoIds(), output_header->ProtoIds(), "proto ids", error_msg) || + !VerifyIds(orig_header->FieldIds(), output_header->FieldIds(), "field ids", error_msg) || + !VerifyIds(orig_header->MethodIds(), output_header->MethodIds(), "method ids", error_msg)) { return false; } // Compare class defs. The order may have been changed by dexlayout. - if (!VerifyClassDefs(orig.ClassDefs(), output.ClassDefs(), error_msg)) { + if (!VerifyClassDefs(orig_header->ClassDefs(), output_header->ClassDefs(), error_msg)) { return false; } return true; } -template bool VerifyIds(std::vector>& orig, - std::vector>& output, +template bool VerifyIds(dex_ir::CollectionVector& orig, + dex_ir::CollectionVector& output, const char* section_name, std::string* error_msg) { - if (orig.size() != output.size()) { - *error_msg = StringPrintf( - "Mismatched size for %s section: %zu vs %zu.", section_name, orig.size(), output.size()); - return false; - } - for (size_t i = 0; i < orig.size(); ++i) { - if (!VerifyId(orig[i].get(), output[i].get(), error_msg)) { + auto orig_iter = orig.begin(); + auto output_iter = output.begin(); + for (; orig_iter != orig.end() && output_iter != output.end(); ++orig_iter, ++output_iter) { + if (!VerifyId(orig_iter->get(), output_iter->get(), error_msg)) { return false; } } + if (orig_iter != orig.end() || output_iter != output.end()) { + const char* longer; + if (orig_iter == orig.end()) { + longer = "output"; + } else { + longer = "original"; + } + *error_msg = StringPrintf("Mismatch for %s section: %s is longer.", section_name, longer); + return false; + } return true; } @@ -181,29 +185,36 @@ struct ClassDefCompare { // The class defs may have a new order due to dexlayout. Use the class's class_idx to uniquely // identify them and sort them for comparison. -bool VerifyClassDefs(std::vector>& orig, - std::vector>& output, +bool VerifyClassDefs(dex_ir::CollectionVector& orig, + dex_ir::CollectionVector& output, std::string* error_msg) { - if (orig.size() != output.size()) { - *error_msg = StringPrintf( - "Mismatched size for class defs section: %zu vs %zu.", orig.size(), output.size()); - return false; - } // Store the class defs into sets sorted by the class's type index. std::set orig_set; std::set output_set; - for (size_t i = 0; i < orig.size(); ++i) { - orig_set.insert(orig[i].get()); - output_set.insert(output[i].get()); - } - auto orig_iter = orig_set.begin(); - auto output_iter = output_set.begin(); - while (orig_iter != orig_set.end() && output_iter != output_set.end()) { - if (!VerifyClassDef(*orig_iter, *output_iter, error_msg)) { + auto orig_iter = orig.begin(); + auto output_iter = output.begin(); + for (; orig_iter != orig.end() && output_iter != output.end(); ++orig_iter, ++output_iter) { + orig_set.insert(orig_iter->get()); + output_set.insert(output_iter->get()); + } + if (orig_iter != orig.end() || output_iter != output.end()) { + const char* longer; + if (orig_iter == orig.end()) { + longer = "output"; + } else { + longer = "original"; + } + *error_msg = StringPrintf("Mismatch for class defs section: %s is longer.", longer); + return false; + } + auto orig_set_iter = orig_set.begin(); + auto output_set_iter = output_set.begin(); + while (orig_set_iter != orig_set.end() && output_set_iter != output_set.end()) { + if (!VerifyClassDef(*orig_set_iter, *output_set_iter, error_msg)) { return false; } - orig_iter++; - output_iter++; + orig_set_iter++; + output_set_iter++; } return true; } @@ -769,8 +780,8 @@ bool VerifyFields(dex_ir::FieldItemVector* orig, return false; } for (size_t i = 0; i < orig->size(); ++i) { - dex_ir::FieldItem* orig_field = (*orig)[i].get(); - dex_ir::FieldItem* output_field = (*output)[i].get(); + dex_ir::FieldItem* orig_field = &(*orig)[i]; + dex_ir::FieldItem* output_field = &(*output)[i]; if (orig_field->GetFieldId()->GetIndex() != output_field->GetFieldId()->GetIndex()) { *error_msg = StringPrintf("Mismatched field index for class data at offset %x: %u vs %u.", orig_offset, @@ -802,8 +813,8 @@ bool VerifyMethods(dex_ir::MethodItemVector* orig, return false; } for (size_t i = 0; i < orig->size(); ++i) { - dex_ir::MethodItem* orig_method = (*orig)[i].get(); - dex_ir::MethodItem* output_method = (*output)[i].get(); + dex_ir::MethodItem* orig_method = &(*orig)[i]; + dex_ir::MethodItem* output_method = &(*output)[i]; if (orig_method->GetMethodId()->GetIndex() != output_method->GetMethodId()->GetIndex()) { *error_msg = StringPrintf("Mismatched method index for class data at offset %x: %u vs %u.", orig_offset, @@ -907,7 +918,7 @@ bool VerifyDebugInfo(dex_ir::DebugInfoItem* orig, *error_msg = "DebugInfo null/non-null mismatch."; return false; } - if (memcmp(orig_data, output_data, orig_size) != 0) { + if (orig_data != nullptr && memcmp(orig_data, output_data, orig_size) != 0) { *error_msg = "DebugInfo bytes mismatch."; return false; } diff --git a/dexlayout/dex_verify.h b/dexlayout/dex_verify.h index 998939bbce33168a14a43f79426c41ae2852d297..4943defe16ccc464bb33f746da9bf759756ce150 100644 --- a/dexlayout/dex_verify.h +++ b/dexlayout/dex_verify.h @@ -30,8 +30,8 @@ bool VerifyOutputDexFile(dex_ir::Header* orig_header, dex_ir::Header* output_header, std::string* error_msg); -template bool VerifyIds(std::vector>& orig, - std::vector>& output, +template bool VerifyIds(dex_ir::CollectionVector& orig, + dex_ir::CollectionVector& output, const char* section_name, std::string* error_msg); bool VerifyId(dex_ir::StringId* orig, dex_ir::StringId* output, std::string* error_msg); @@ -40,8 +40,8 @@ bool VerifyId(dex_ir::ProtoId* orig, dex_ir::ProtoId* output, std::string* error bool VerifyId(dex_ir::FieldId* orig, dex_ir::FieldId* output, std::string* error_msg); bool VerifyId(dex_ir::MethodId* orig, dex_ir::MethodId* output, std::string* error_msg); -bool VerifyClassDefs(std::vector>& orig, - std::vector>& output, +bool VerifyClassDefs(dex_ir::CollectionVector& orig, + dex_ir::CollectionVector& output, std::string* error_msg); bool VerifyClassDef(dex_ir::ClassDef* orig, dex_ir::ClassDef* output, std::string* error_msg); diff --git a/dexlayout/dex_visualize.cc b/dexlayout/dex_visualize.cc index 516a3382fd21cf046a6dc88353ea92b5732d8edf..4a36744e9768060c9728d0f1ac42571db359b944 100644 --- a/dexlayout/dex_visualize.cc +++ b/dexlayout/dex_visualize.cc @@ -33,7 +33,7 @@ #include "dex_ir.h" #include "dexlayout.h" -#include "jit/profile_compilation_info.h" +#include "profile/profile_compilation_info.h" namespace art { @@ -252,9 +252,9 @@ void VisualizeDexLayout(dex_ir::Header* header, return; } - const uint32_t class_defs_size = header->GetCollections().ClassDefsSize(); + const uint32_t class_defs_size = header->ClassDefs().Size(); for (uint32_t class_index = 0; class_index < class_defs_size; class_index++) { - dex_ir::ClassDef* class_def = header->GetCollections().GetClassDef(class_index); + dex_ir::ClassDef* class_def = header->ClassDefs()[class_index]; dex::TypeIndex type_idx(class_def->ClassType()->GetIndex()); if (profile_info != nullptr && !profile_info->ContainsClass(*dex_file, type_idx)) { continue; @@ -279,22 +279,22 @@ void VisualizeDexLayout(dex_ir::Header* header, dumper->DumpAddressRange(class_data, class_index); if (class_data->StaticFields()) { for (auto& field_item : *class_data->StaticFields()) { - dumper->DumpFieldItem(field_item.get(), class_index); + dumper->DumpFieldItem(&field_item, class_index); } } if (class_data->InstanceFields()) { for (auto& field_item : *class_data->InstanceFields()) { - dumper->DumpFieldItem(field_item.get(), class_index); + dumper->DumpFieldItem(&field_item, class_index); } } if (class_data->DirectMethods()) { for (auto& method_item : *class_data->DirectMethods()) { - dumper->DumpMethodItem(method_item.get(), dex_file, class_index, profile_info); + dumper->DumpMethodItem(&method_item, dex_file, class_index, profile_info); } } if (class_data->VirtualMethods()) { for (auto& method_item : *class_data->VirtualMethods()) { - dumper->DumpMethodItem(method_item.get(), dex_file, class_index, profile_info); + dumper->DumpMethodItem(&method_item, dex_file, class_index, profile_info); } } } @@ -305,7 +305,7 @@ static uint32_t FindNextByteAfterSection(dex_ir::Header* header, const std::vector& sorted_sections, size_t section_index) { for (size_t i = section_index + 1; i < sorted_sections.size(); ++i) { - const dex_ir::DexFileSection& section = sorted_sections.at(i); + const dex_ir::DexFileSection& section = sorted_sections[i]; if (section.size != 0) { return section.offset; } diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc index eead13f69a9f838cf585f77ef585e3f897309e66..a4c5cda4bab8a7caccff0b30fc1378ad00b712c8 100644 --- a/dexlayout/dex_writer.cc +++ b/dexlayout/dex_writer.cc @@ -207,21 +207,21 @@ void DexWriter::WriteEncodedAnnotation(Stream* stream, dex_ir::EncodedAnnotation void DexWriter::WriteEncodedFields(Stream* stream, dex_ir::FieldItemVector* fields) { uint32_t prev_index = 0; - for (std::unique_ptr& field : *fields) { - uint32_t index = field->GetFieldId()->GetIndex(); + for (auto& field : *fields) { + uint32_t index = field.GetFieldId()->GetIndex(); stream->WriteUleb128(index - prev_index); - stream->WriteUleb128(field->GetAccessFlags()); + stream->WriteUleb128(field.GetAccessFlags()); prev_index = index; } } void DexWriter::WriteEncodedMethods(Stream* stream, dex_ir::MethodItemVector* methods) { uint32_t prev_index = 0; - for (std::unique_ptr& method : *methods) { - uint32_t index = method->GetMethodId()->GetIndex(); - uint32_t code_off = method->GetCodeItem() == nullptr ? 0 : method->GetCodeItem()->GetOffset(); + for (auto& method : *methods) { + uint32_t index = method.GetMethodId()->GetIndex(); + uint32_t code_off = method.GetCodeItem() == nullptr ? 0 : method.GetCodeItem()->GetOffset(); stream->WriteUleb128(index - prev_index); - stream->WriteUleb128(method->GetAccessFlags()); + stream->WriteUleb128(method.GetAccessFlags()); stream->WriteUleb128(code_off); prev_index = index; } @@ -231,7 +231,7 @@ void DexWriter::WriteEncodedMethods(Stream* stream, dex_ir::MethodItemVector* me // function that takes a CollectionVector and uses overloading. void DexWriter::WriteStringIds(Stream* stream, bool reserve_only) { const uint32_t start = stream->Tell(); - for (std::unique_ptr& string_id : header_->GetCollections().StringIds()) { + for (auto& string_id : header_->StringIds()) { stream->AlignTo(SectionAlignment(DexFile::kDexTypeStringIdItem)); if (reserve_only) { stream->Skip(string_id->GetSize()); @@ -241,7 +241,7 @@ void DexWriter::WriteStringIds(Stream* stream, bool reserve_only) { } } if (compute_offsets_ && start != stream->Tell()) { - header_->GetCollections().SetStringIdsOffset(start); + header_->StringIds().SetOffset(start); } } @@ -256,25 +256,25 @@ void DexWriter::WriteStringData(Stream* stream, dex_ir::StringData* string_data) void DexWriter::WriteStringDatas(Stream* stream) { const uint32_t start = stream->Tell(); - for (std::unique_ptr& string_data : header_->GetCollections().StringDatas()) { + for (auto& string_data : header_->StringDatas()) { WriteStringData(stream, string_data.get()); } if (compute_offsets_ && start != stream->Tell()) { - header_->GetCollections().SetStringDatasOffset(start); + header_->StringDatas().SetOffset(start); } } void DexWriter::WriteTypeIds(Stream* stream) { uint32_t descriptor_idx[1]; const uint32_t start = stream->Tell(); - for (std::unique_ptr& type_id : header_->GetCollections().TypeIds()) { + for (auto& type_id : header_->TypeIds()) { stream->AlignTo(SectionAlignment(DexFile::kDexTypeTypeIdItem)); ProcessOffset(stream, type_id.get()); descriptor_idx[0] = type_id->GetStringId()->GetIndex(); stream->Write(descriptor_idx, type_id->GetSize()); } if (compute_offsets_ && start != stream->Tell()) { - header_->GetCollections().SetTypeIdsOffset(start); + header_->TypeIds().SetOffset(start); } } @@ -282,7 +282,7 @@ void DexWriter::WriteTypeLists(Stream* stream) { uint32_t size[1]; uint16_t list[1]; const uint32_t start = stream->Tell(); - for (std::unique_ptr& type_list : header_->GetCollections().TypeLists()) { + for (auto& type_list : header_->TypeLists()) { stream->AlignTo(SectionAlignment(DexFile::kDexTypeTypeList)); size[0] = type_list->GetTypeList()->size(); ProcessOffset(stream, type_list.get()); @@ -293,14 +293,14 @@ void DexWriter::WriteTypeLists(Stream* stream) { } } if (compute_offsets_ && start != stream->Tell()) { - header_->GetCollections().SetTypeListsOffset(start); + header_->TypeLists().SetOffset(start); } } void DexWriter::WriteProtoIds(Stream* stream, bool reserve_only) { uint32_t buffer[3]; const uint32_t start = stream->Tell(); - for (std::unique_ptr& proto_id : header_->GetCollections().ProtoIds()) { + for (auto& proto_id : header_->ProtoIds()) { stream->AlignTo(SectionAlignment(DexFile::kDexTypeProtoIdItem)); ProcessOffset(stream, proto_id.get()); if (reserve_only) { @@ -313,14 +313,14 @@ void DexWriter::WriteProtoIds(Stream* stream, bool reserve_only) { } } if (compute_offsets_ && start != stream->Tell()) { - header_->GetCollections().SetProtoIdsOffset(start); + header_->ProtoIds().SetOffset(start); } } void DexWriter::WriteFieldIds(Stream* stream) { uint16_t buffer[4]; const uint32_t start = stream->Tell(); - for (std::unique_ptr& field_id : header_->GetCollections().FieldIds()) { + for (auto& field_id : header_->FieldIds()) { stream->AlignTo(SectionAlignment(DexFile::kDexTypeFieldIdItem)); ProcessOffset(stream, field_id.get()); buffer[0] = field_id->Class()->GetIndex(); @@ -330,14 +330,14 @@ void DexWriter::WriteFieldIds(Stream* stream) { stream->Write(buffer, field_id->GetSize()); } if (compute_offsets_ && start != stream->Tell()) { - header_->GetCollections().SetFieldIdsOffset(start); + header_->FieldIds().SetOffset(start); } } void DexWriter::WriteMethodIds(Stream* stream) { uint16_t buffer[4]; const uint32_t start = stream->Tell(); - for (std::unique_ptr& method_id : header_->GetCollections().MethodIds()) { + for (auto& method_id : header_->MethodIds()) { stream->AlignTo(SectionAlignment(DexFile::kDexTypeMethodIdItem)); ProcessOffset(stream, method_id.get()); buffer[0] = method_id->Class()->GetIndex(); @@ -347,28 +347,26 @@ void DexWriter::WriteMethodIds(Stream* stream) { stream->Write(buffer, method_id->GetSize()); } if (compute_offsets_ && start != stream->Tell()) { - header_->GetCollections().SetMethodIdsOffset(start); + header_->MethodIds().SetOffset(start); } } void DexWriter::WriteEncodedArrays(Stream* stream) { const uint32_t start = stream->Tell(); - for (std::unique_ptr& encoded_array : - header_->GetCollections().EncodedArrayItems()) { + for (auto& encoded_array : header_->EncodedArrayItems()) { stream->AlignTo(SectionAlignment(DexFile::kDexTypeEncodedArrayItem)); ProcessOffset(stream, encoded_array.get()); WriteEncodedArray(stream, encoded_array->GetEncodedValues()); } if (compute_offsets_ && start != stream->Tell()) { - header_->GetCollections().SetEncodedArrayItemsOffset(start); + header_->EncodedArrayItems().SetOffset(start); } } void DexWriter::WriteAnnotations(Stream* stream) { uint8_t visibility[1]; const uint32_t start = stream->Tell(); - for (std::unique_ptr& annotation : - header_->GetCollections().AnnotationItems()) { + for (auto& annotation : header_->AnnotationItems()) { stream->AlignTo(SectionAlignment(DexFile::kDexTypeAnnotationItem)); visibility[0] = annotation->GetVisibility(); ProcessOffset(stream, annotation.get()); @@ -376,7 +374,7 @@ void DexWriter::WriteAnnotations(Stream* stream) { WriteEncodedAnnotation(stream, annotation->GetAnnotation()); } if (compute_offsets_ && start != stream->Tell()) { - header_->GetCollections().SetAnnotationItemsOffset(start); + header_->AnnotationItems().SetOffset(start); } } @@ -384,8 +382,7 @@ void DexWriter::WriteAnnotationSets(Stream* stream) { uint32_t size[1]; uint32_t annotation_off[1]; const uint32_t start = stream->Tell(); - for (std::unique_ptr& annotation_set : - header_->GetCollections().AnnotationSetItems()) { + for (auto& annotation_set : header_->AnnotationSetItems()) { stream->AlignTo(SectionAlignment(DexFile::kDexTypeAnnotationSetItem)); size[0] = annotation_set->GetItems()->size(); ProcessOffset(stream, annotation_set.get()); @@ -396,7 +393,7 @@ void DexWriter::WriteAnnotationSets(Stream* stream) { } } if (compute_offsets_ && start != stream->Tell()) { - header_->GetCollections().SetAnnotationSetItemsOffset(start); + header_->AnnotationSetItems().SetOffset(start); } } @@ -404,8 +401,7 @@ void DexWriter::WriteAnnotationSetRefs(Stream* stream) { uint32_t size[1]; uint32_t annotations_off[1]; const uint32_t start = stream->Tell(); - for (std::unique_ptr& annotation_set_ref : - header_->GetCollections().AnnotationSetRefLists()) { + for (auto& annotation_set_ref : header_->AnnotationSetRefLists()) { stream->AlignTo(SectionAlignment(DexFile::kDexTypeAnnotationSetRefList)); size[0] = annotation_set_ref->GetItems()->size(); ProcessOffset(stream, annotation_set_ref.get()); @@ -416,7 +412,7 @@ void DexWriter::WriteAnnotationSetRefs(Stream* stream) { } } if (compute_offsets_ && start != stream->Tell()) { - header_->GetCollections().SetAnnotationSetRefListsOffset(start); + header_->AnnotationSetRefLists().SetOffset(start); } } @@ -424,8 +420,7 @@ void DexWriter::WriteAnnotationsDirectories(Stream* stream) { uint32_t directory_buffer[4]; uint32_t annotation_buffer[2]; const uint32_t start = stream->Tell(); - for (std::unique_ptr& annotations_directory : - header_->GetCollections().AnnotationsDirectoryItems()) { + for (auto& annotations_directory : header_->AnnotationsDirectoryItems()) { stream->AlignTo(SectionAlignment(DexFile::kDexTypeAnnotationsDirectoryItem)); ProcessOffset(stream, annotations_directory.get()); directory_buffer[0] = annotations_directory->GetClassAnnotation() == nullptr ? 0 : @@ -463,7 +458,7 @@ void DexWriter::WriteAnnotationsDirectories(Stream* stream) { } } if (compute_offsets_ && start != stream->Tell()) { - header_->GetCollections().SetAnnotationsDirectoryItemsOffset(start); + header_->AnnotationsDirectoryItems().SetOffset(start); } } @@ -475,12 +470,11 @@ void DexWriter::WriteDebugInfoItem(Stream* stream, dex_ir::DebugInfoItem* debug_ void DexWriter::WriteDebugInfoItems(Stream* stream) { const uint32_t start = stream->Tell(); - for (std::unique_ptr& debug_info : - header_->GetCollections().DebugInfoItems()) { + for (auto& debug_info : header_->DebugInfoItems()) { WriteDebugInfoItem(stream, debug_info.get()); } if (compute_offsets_ && start != stream->Tell()) { - header_->GetCollections().SetDebugInfoItemsOffset(start); + header_->DebugInfoItems().SetOffset(start); } } @@ -558,7 +552,7 @@ void DexWriter::WriteCodeItems(Stream* stream, bool reserve_only) { DexLayoutSections::SectionType::kSectionTypeCode)]; } const uint32_t start = stream->Tell(); - for (auto& code_item : header_->GetCollections().CodeItems()) { + for (auto& code_item : header_->CodeItems()) { uint32_t start_offset = stream->Tell(); WriteCodeItem(stream, code_item.get(), reserve_only); // Only add the section hotness info once. @@ -573,14 +567,14 @@ void DexWriter::WriteCodeItems(Stream* stream, bool reserve_only) { } if (compute_offsets_ && start != stream->Tell()) { - header_->GetCollections().SetCodeItemsOffset(start); + header_->CodeItems().SetOffset(start); } } void DexWriter::WriteClassDefs(Stream* stream, bool reserve_only) { const uint32_t start = stream->Tell(); uint32_t class_def_buffer[8]; - for (std::unique_ptr& class_def : header_->GetCollections().ClassDefs()) { + for (auto& class_def : header_->ClassDefs()) { stream->AlignTo(SectionAlignment(DexFile::kDexTypeClassDefItem)); if (reserve_only) { stream->Skip(class_def->GetSize()); @@ -602,14 +596,14 @@ void DexWriter::WriteClassDefs(Stream* stream, bool reserve_only) { } } if (compute_offsets_ && start != stream->Tell()) { - header_->GetCollections().SetClassDefsOffset(start); + header_->ClassDefs().SetOffset(start); } } void DexWriter::WriteClassDatas(Stream* stream) { const uint32_t start = stream->Tell(); for (const std::unique_ptr& class_data : - header_->GetCollections().ClassDatas()) { + header_->ClassDatas()) { stream->AlignTo(SectionAlignment(DexFile::kDexTypeClassDataItem)); ProcessOffset(stream, class_data.get()); stream->WriteUleb128(class_data->StaticFields()->size()); @@ -622,15 +616,14 @@ void DexWriter::WriteClassDatas(Stream* stream) { WriteEncodedMethods(stream, class_data->VirtualMethods()); } if (compute_offsets_ && start != stream->Tell()) { - header_->GetCollections().SetClassDatasOffset(start); + header_->ClassDatas().SetOffset(start); } } void DexWriter::WriteCallSiteIds(Stream* stream, bool reserve_only) { const uint32_t start = stream->Tell(); uint32_t call_site_off[1]; - for (std::unique_ptr& call_site_id : - header_->GetCollections().CallSiteIds()) { + for (auto& call_site_id : header_->CallSiteIds()) { stream->AlignTo(SectionAlignment(DexFile::kDexTypeCallSiteIdItem)); if (reserve_only) { stream->Skip(call_site_id->GetSize()); @@ -640,15 +633,14 @@ void DexWriter::WriteCallSiteIds(Stream* stream, bool reserve_only) { } } if (compute_offsets_ && start != stream->Tell()) { - header_->GetCollections().SetCallSiteIdsOffset(start); + header_->CallSiteIds().SetOffset(start); } } void DexWriter::WriteMethodHandles(Stream* stream) { const uint32_t start = stream->Tell(); uint16_t method_handle_buff[4]; - for (std::unique_ptr& method_handle : - header_->GetCollections().MethodHandleItems()) { + for (auto& method_handle : header_->MethodHandleItems()) { stream->AlignTo(SectionAlignment(DexFile::kDexTypeMethodHandleItem)); method_handle_buff[0] = static_cast(method_handle->GetMethodHandleType()); method_handle_buff[1] = 0; // unused. @@ -657,7 +649,7 @@ void DexWriter::WriteMethodHandles(Stream* stream) { stream->Write(method_handle_buff, method_handle->GetSize()); } if (compute_offsets_ && start != stream->Tell()) { - header_->GetCollections().SetMethodHandleItemsOffset(start); + header_->MethodHandleItems().SetOffset(start); } } @@ -678,67 +670,66 @@ void DexWriter::WriteMapItems(Stream* stream, MapItemQueue* queue) { } void DexWriter::GenerateAndWriteMapItems(Stream* stream) { - dex_ir::Collections& collection = header_->GetCollections(); MapItemQueue queue; // Header and index section. queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeHeaderItem, 1, 0)); queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeStringIdItem, - collection.StringIdsSize(), - collection.StringIdsOffset())); + header_->StringIds().Size(), + header_->StringIds().GetOffset())); queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeTypeIdItem, - collection.TypeIdsSize(), - collection.TypeIdsOffset())); + header_->TypeIds().Size(), + header_->TypeIds().GetOffset())); queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeProtoIdItem, - collection.ProtoIdsSize(), - collection.ProtoIdsOffset())); + header_->ProtoIds().Size(), + header_->ProtoIds().GetOffset())); queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeFieldIdItem, - collection.FieldIdsSize(), - collection.FieldIdsOffset())); + header_->FieldIds().Size(), + header_->FieldIds().GetOffset())); queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeMethodIdItem, - collection.MethodIdsSize(), - collection.MethodIdsOffset())); + header_->MethodIds().Size(), + header_->MethodIds().GetOffset())); queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeClassDefItem, - collection.ClassDefsSize(), - collection.ClassDefsOffset())); + header_->ClassDefs().Size(), + header_->ClassDefs().GetOffset())); queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeCallSiteIdItem, - collection.CallSiteIdsSize(), - collection.CallSiteIdsOffset())); + header_->CallSiteIds().Size(), + header_->CallSiteIds().GetOffset())); queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeMethodHandleItem, - collection.MethodHandleItemsSize(), - collection.MethodHandleItemsOffset())); + header_->MethodHandleItems().Size(), + header_->MethodHandleItems().GetOffset())); // Data section. - queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeMapList, 1, collection.MapListOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeMapList, 1, header_->MapListOffset())); queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeTypeList, - collection.TypeListsSize(), - collection.TypeListsOffset())); + header_->TypeLists().Size(), + header_->TypeLists().GetOffset())); queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationSetRefList, - collection.AnnotationSetRefListsSize(), - collection.AnnotationSetRefListsOffset())); + header_->AnnotationSetRefLists().Size(), + header_->AnnotationSetRefLists().GetOffset())); queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationSetItem, - collection.AnnotationSetItemsSize(), - collection.AnnotationSetItemsOffset())); + header_->AnnotationSetItems().Size(), + header_->AnnotationSetItems().GetOffset())); queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeClassDataItem, - collection.ClassDatasSize(), - collection.ClassDatasOffset())); + header_->ClassDatas().Size(), + header_->ClassDatas().GetOffset())); queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeCodeItem, - collection.CodeItemsSize(), - collection.CodeItemsOffset())); + header_->CodeItems().Size(), + header_->CodeItems().GetOffset())); queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeStringDataItem, - collection.StringDatasSize(), - collection.StringDatasOffset())); + header_->StringDatas().Size(), + header_->StringDatas().GetOffset())); queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeDebugInfoItem, - collection.DebugInfoItemsSize(), - collection.DebugInfoItemsOffset())); + header_->DebugInfoItems().Size(), + header_->DebugInfoItems().GetOffset())); queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationItem, - collection.AnnotationItemsSize(), - collection.AnnotationItemsOffset())); + header_->AnnotationItems().Size(), + header_->AnnotationItems().GetOffset())); queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeEncodedArrayItem, - collection.EncodedArrayItemsSize(), - collection.EncodedArrayItemsOffset())); + header_->EncodedArrayItems().Size(), + header_->EncodedArrayItems().GetOffset())); queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationsDirectoryItem, - collection.AnnotationsDirectoryItemsSize(), - collection.AnnotationsDirectoryItemsOffset())); + header_->AnnotationsDirectoryItems().Size(), + header_->AnnotationsDirectoryItems().GetOffset())); WriteMapItems(stream, &queue); } @@ -761,20 +752,19 @@ void DexWriter::WriteHeader(Stream* stream) { header.endian_tag_ = header_->EndianTag(); header.link_size_ = header_->LinkSize(); header.link_off_ = header_->LinkOffset(); - const dex_ir::Collections& collections = header_->GetCollections(); - header.map_off_ = collections.MapListOffset(); - header.string_ids_size_ = collections.StringIdsSize(); - header.string_ids_off_ = collections.StringIdsOffset(); - header.type_ids_size_ = collections.TypeIdsSize(); - header.type_ids_off_ = collections.TypeIdsOffset(); - header.proto_ids_size_ = collections.ProtoIdsSize(); - header.proto_ids_off_ = collections.ProtoIdsOffset(); - header.field_ids_size_ = collections.FieldIdsSize(); - header.field_ids_off_ = collections.FieldIdsOffset(); - header.method_ids_size_ = collections.MethodIdsSize(); - header.method_ids_off_ = collections.MethodIdsOffset(); - header.class_defs_size_ = collections.ClassDefsSize(); - header.class_defs_off_ = collections.ClassDefsOffset(); + header.map_off_ = header_->MapListOffset(); + header.string_ids_size_ = header_->StringIds().Size(); + header.string_ids_off_ = header_->StringIds().GetOffset(); + header.type_ids_size_ = header_->TypeIds().Size(); + header.type_ids_off_ = header_->TypeIds().GetOffset(); + header.proto_ids_size_ = header_->ProtoIds().Size(); + header.proto_ids_off_ = header_->ProtoIds().GetOffset(); + header.field_ids_size_ = header_->FieldIds().Size(); + header.field_ids_off_ = header_->FieldIds().GetOffset(); + header.method_ids_size_ = header_->MethodIds().Size(); + header.method_ids_off_ = header_->MethodIds().GetOffset(); + header.class_defs_size_ = header_->ClassDefs().Size(); + header.class_defs_off_ = header_->ClassDefs().GetOffset(); header.data_size_ = header_->DataSize(); header.data_off_ = header_->DataOffset(); @@ -797,8 +787,6 @@ bool DexWriter::Write(DexContainer* output, std::string* error_msg) { // Starting offset is right after the header. stream->Seek(GetHeaderSize()); - dex_ir::Collections& collection = header_->GetCollections(); - // Based on: https://source.android.com/devices/tech/dalvik/dex-format // Since the offsets may not be calculated already, the writing must be done in the correct order. const uint32_t string_ids_offset = stream->Tell(); @@ -863,9 +851,9 @@ bool DexWriter::Write(DexContainer* output, std::string* error_msg) { // Write the map list. if (compute_offsets_) { stream->AlignTo(SectionAlignment(DexFile::kDexTypeMapList)); - collection.SetMapListOffset(stream->Tell()); + header_->SetMapListOffset(stream->Tell()); } else { - stream->Seek(collection.MapListOffset()); + stream->Seek(header_->MapListOffset()); } GenerateAndWriteMapItems(stream); stream->AlignTo(kDataSectionAlignment); @@ -882,7 +870,7 @@ bool DexWriter::Write(DexContainer* output, std::string* error_msg) { } // Write link data if it exists. - const std::vector& link_data = collection.LinkData(); + const std::vector& link_data = header_->LinkData(); if (link_data.size() > 0) { CHECK_EQ(header_->LinkSize(), static_cast(link_data.size())); if (compute_offsets_) { diff --git a/dexlayout/dexdiag.cc b/dexlayout/dexdiag.cc index 6cb141f6881ec7407b37bd634e496513b95c9176..493a8a279329c643fa0f6c051e1fa91c5a98d74e 100644 --- a/dexlayout/dexdiag.cc +++ b/dexlayout/dexdiag.cc @@ -27,7 +27,6 @@ #include "android-base/stringprintf.h" #include "base/logging.h" // For InitLogging. -#include "base/mutex.h" #include "base/stringpiece.h" #include "dexlayout.h" @@ -37,7 +36,6 @@ #ifdef ART_TARGET_ANDROID #include "pagemap/pagemap.h" #endif -#include "runtime.h" #include "vdex_file.h" namespace art { @@ -92,7 +90,9 @@ class PageCount { map_[type]++; } size_t Get(uint16_t type) const { - return map_.at(type); + auto it = map_.find(type); + DCHECK(it != map_.end()); + return it->second; } private: std::map map_; @@ -446,6 +446,11 @@ static void Usage(const char* cmd) { PrintLetterKey(); } +NO_RETURN static void Abort(const char* msg) { + std::cerr << msg; + exit(1); +} + static int DexDiagMain(int argc, char* argv[]) { if (argc < 2) { Usage(argv[0]); @@ -471,8 +476,7 @@ static int DexDiagMain(int argc, char* argv[]) { } // Art specific set up. - Locks::Init(); - InitLogging(argv, Runtime::Abort); + InitLogging(argv, Abort); MemMap::Init(); #ifdef ART_TARGET_ANDROID diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index ec0cbe6a600f85bb32483607e9ffdf44bf90b4d3..d6dd9d182914e26157310b503d1001460735e2b7 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -34,6 +34,7 @@ #include "android-base/stringprintf.h" #include "base/logging.h" // For VLOG_IS_ON. +#include "base/mem_map.h" #include "base/os.h" #include "base/utils.h" #include "dex/art_dex_file_loader.h" @@ -48,8 +49,7 @@ #include "dex_verify.h" #include "dex_visualize.h" #include "dex_writer.h" -#include "jit/profile_compilation_info.h" -#include "mem_map.h" +#include "profile/profile_compilation_info.h" namespace art { @@ -417,24 +417,24 @@ static std::unique_ptr IndexString(dex_ir::Header* header, outSize = snprintf(buf.get(), buf_size, ""); break; case Instruction::kIndexTypeRef: - if (index < header->GetCollections().TypeIdsSize()) { - const char* tp = header->GetCollections().GetTypeId(index)->GetStringId()->Data(); + if (index < header->TypeIds().Size()) { + const char* tp = header->TypeIds()[index]->GetStringId()->Data(); outSize = snprintf(buf.get(), buf_size, "%s // type@%0*x", tp, width, index); } else { outSize = snprintf(buf.get(), buf_size, " // type@%0*x", width, index); } break; case Instruction::kIndexStringRef: - if (index < header->GetCollections().StringIdsSize()) { - const char* st = header->GetCollections().GetStringId(index)->Data(); + if (index < header->StringIds().Size()) { + const char* st = header->StringIds()[index]->Data(); outSize = snprintf(buf.get(), buf_size, "\"%s\" // string@%0*x", st, width, index); } else { outSize = snprintf(buf.get(), buf_size, " // string@%0*x", width, index); } break; case Instruction::kIndexMethodRef: - if (index < header->GetCollections().MethodIdsSize()) { - dex_ir::MethodId* method_id = header->GetCollections().GetMethodId(index); + if (index < header->MethodIds().Size()) { + dex_ir::MethodId* method_id = header->MethodIds()[index]; const char* name = method_id->Name()->Data(); std::string type_descriptor = GetSignatureForProtoId(method_id->Proto()); const char* back_descriptor = method_id->Class()->GetStringId()->Data(); @@ -445,8 +445,8 @@ static std::unique_ptr IndexString(dex_ir::Header* header, } break; case Instruction::kIndexFieldRef: - if (index < header->GetCollections().FieldIdsSize()) { - dex_ir::FieldId* field_id = header->GetCollections().GetFieldId(index); + if (index < header->FieldIds().Size()) { + dex_ir::FieldId* field_id = header->FieldIds()[index]; const char* name = field_id->Name()->Data(); const char* type_descriptor = field_id->Type()->GetStringId()->Data(); const char* back_descriptor = field_id->Class()->GetStringId()->Data(); @@ -466,15 +466,15 @@ static std::unique_ptr IndexString(dex_ir::Header* header, case Instruction::kIndexMethodAndProtoRef: { std::string method(""); std::string proto(""); - if (index < header->GetCollections().MethodIdsSize()) { - dex_ir::MethodId* method_id = header->GetCollections().GetMethodId(index); + if (index < header->MethodIds().Size()) { + dex_ir::MethodId* method_id = header->MethodIds()[index]; const char* name = method_id->Name()->Data(); std::string type_descriptor = GetSignatureForProtoId(method_id->Proto()); const char* back_descriptor = method_id->Class()->GetStringId()->Data(); method = StringPrintf("%s.%s:%s", back_descriptor, name, type_descriptor.c_str()); } - if (secondary_index < header->GetCollections().ProtoIdsSize()) { - dex_ir::ProtoId* proto_id = header->GetCollections().GetProtoId(secondary_index); + if (secondary_index < header->ProtoIds().Size()) { + dex_ir::ProtoId* proto_id = header->ProtoIds()[secondary_index]; proto = GetSignatureForProtoId(proto_id); } outSize = snprintf(buf.get(), buf_size, "%s, %s // method@%0*x, proto@%0*x", @@ -596,7 +596,6 @@ void DexLayout::DumpEncodedValue(const dex_ir::EncodedValue* data) { */ void DexLayout::DumpFileHeader() { char sanitized[8 * 2 + 1]; - dex_ir::Collections& collections = header_->GetCollections(); fprintf(out_file_, "DEX file header:\n"); Asciify(sanitized, header_->Magic(), 8); fprintf(out_file_, "magic : '%s'\n", sanitized); @@ -610,24 +609,24 @@ void DexLayout::DumpFileHeader() { fprintf(out_file_, "link_size : %d\n", header_->LinkSize()); fprintf(out_file_, "link_off : %d (0x%06x)\n", header_->LinkOffset(), header_->LinkOffset()); - fprintf(out_file_, "string_ids_size : %d\n", collections.StringIdsSize()); + fprintf(out_file_, "string_ids_size : %d\n", header_->StringIds().Size()); fprintf(out_file_, "string_ids_off : %d (0x%06x)\n", - collections.StringIdsOffset(), collections.StringIdsOffset()); - fprintf(out_file_, "type_ids_size : %d\n", collections.TypeIdsSize()); + header_->StringIds().GetOffset(), header_->StringIds().GetOffset()); + fprintf(out_file_, "type_ids_size : %d\n", header_->TypeIds().Size()); fprintf(out_file_, "type_ids_off : %d (0x%06x)\n", - collections.TypeIdsOffset(), collections.TypeIdsOffset()); - fprintf(out_file_, "proto_ids_size : %d\n", collections.ProtoIdsSize()); + header_->TypeIds().GetOffset(), header_->TypeIds().GetOffset()); + fprintf(out_file_, "proto_ids_size : %d\n", header_->ProtoIds().Size()); fprintf(out_file_, "proto_ids_off : %d (0x%06x)\n", - collections.ProtoIdsOffset(), collections.ProtoIdsOffset()); - fprintf(out_file_, "field_ids_size : %d\n", collections.FieldIdsSize()); + header_->ProtoIds().GetOffset(), header_->ProtoIds().GetOffset()); + fprintf(out_file_, "field_ids_size : %d\n", header_->FieldIds().Size()); fprintf(out_file_, "field_ids_off : %d (0x%06x)\n", - collections.FieldIdsOffset(), collections.FieldIdsOffset()); - fprintf(out_file_, "method_ids_size : %d\n", collections.MethodIdsSize()); + header_->FieldIds().GetOffset(), header_->FieldIds().GetOffset()); + fprintf(out_file_, "method_ids_size : %d\n", header_->MethodIds().Size()); fprintf(out_file_, "method_ids_off : %d (0x%06x)\n", - collections.MethodIdsOffset(), collections.MethodIdsOffset()); - fprintf(out_file_, "class_defs_size : %d\n", collections.ClassDefsSize()); + header_->MethodIds().GetOffset(), header_->MethodIds().GetOffset()); + fprintf(out_file_, "class_defs_size : %d\n", header_->ClassDefs().Size()); fprintf(out_file_, "class_defs_off : %d (0x%06x)\n", - collections.ClassDefsOffset(), collections.ClassDefsOffset()); + header_->ClassDefs().GetOffset(), header_->ClassDefs().GetOffset()); fprintf(out_file_, "data_size : %d\n", header_->DataSize()); fprintf(out_file_, "data_off : %d (0x%06x)\n\n", header_->DataOffset(), header_->DataOffset()); @@ -638,7 +637,7 @@ void DexLayout::DumpFileHeader() { */ void DexLayout::DumpClassDef(int idx) { // General class information. - dex_ir::ClassDef* class_def = header_->GetCollections().GetClassDef(idx); + dex_ir::ClassDef* class_def = header_->ClassDefs()[idx]; fprintf(out_file_, "Class #%d header:\n", idx); fprintf(out_file_, "class_idx : %d\n", class_def->ClassType()->GetIndex()); fprintf(out_file_, "access_flags : %d (0x%04x)\n", @@ -719,7 +718,7 @@ void DexLayout::DumpAnnotationSetItem(dex_ir::AnnotationSetItem* set_item) { * Dumps class annotations. */ void DexLayout::DumpClassAnnotations(int idx) { - dex_ir::ClassDef* class_def = header_->GetCollections().GetClassDef(idx); + dex_ir::ClassDef* class_def = header_->ClassDefs()[idx]; dex_ir::AnnotationsDirectoryItem* annotations_directory = class_def->Annotations(); if (annotations_directory == nullptr) { return; // none @@ -1039,7 +1038,7 @@ void DexLayout::DumpInstruction(const dex_ir::CodeItem* code, * Dumps a bytecode disassembly. */ void DexLayout::DumpBytecodes(uint32_t idx, const dex_ir::CodeItem* code, uint32_t code_offset) { - dex_ir::MethodId* method_id = header_->GetCollections().GetMethodId(idx); + dex_ir::MethodId* method_id = header_->MethodIds()[idx]; const char* name = method_id->Name()->Data(); std::string type_descriptor = GetSignatureForProtoId(method_id->Proto()); const char* back_descriptor = method_id->Class()->GetStringId()->Data(); @@ -1083,16 +1082,16 @@ static void DumpLocalsCb(void* context, const DexFile::LocalInfo& entry) { /* * Lookup functions. */ -static const char* StringDataByIdx(uint32_t idx, dex_ir::Collections& collections) { - dex_ir::StringId* string_id = collections.GetStringIdOrNullPtr(idx); +static const char* StringDataByIdx(uint32_t idx, dex_ir::Header* header) { + dex_ir::StringId* string_id = header->GetStringIdOrNullPtr(idx); if (string_id == nullptr) { return nullptr; } return string_id->Data(); } -static const char* StringDataByTypeIdx(uint16_t idx, dex_ir::Collections& collections) { - dex_ir::TypeId* type_id = collections.GetTypeIdOrNullPtr(idx); +static const char* StringDataByTypeIdx(uint16_t idx, dex_ir::Header* header) { + dex_ir::TypeId* type_id = header->GetTypeIdOrNullPtr(idx); if (type_id == nullptr) { return nullptr; } @@ -1134,7 +1133,7 @@ void DexLayout::DumpCode(uint32_t idx, if (debug_info != nullptr) { DexFile::DecodeDebugPositionInfo(debug_info->GetDebugInfo(), [this](uint32_t idx) { - return StringDataByIdx(idx, this->header_->GetCollections()); + return StringDataByIdx(idx, this->header_); }, DumpPositionsCb, out_file_); @@ -1161,12 +1160,12 @@ void DexLayout::DumpCode(uint32_t idx, code->InsSize(), code->InsnsSize(), [this](uint32_t idx) { - return StringDataByIdx(idx, this->header_->GetCollections()); + return StringDataByIdx(idx, this->header_); }, [this](uint32_t idx) { return StringDataByTypeIdx(dchecked_integral_cast(idx), - this->header_->GetCollections()); + this->header_); }, DumpLocalsCb, out_file_); @@ -1182,7 +1181,7 @@ void DexLayout::DumpMethod(uint32_t idx, uint32_t flags, const dex_ir::CodeItem* return; } - dex_ir::MethodId* method_id = header_->GetCollections().GetMethodId(idx); + dex_ir::MethodId* method_id = header_->MethodIds()[idx]; const char* name = method_id->Name()->Data(); char* type_descriptor = strdup(GetSignatureForProtoId(method_id->Proto()).c_str()); const char* back_descriptor = method_id->Class()->GetStringId()->Data(); @@ -1292,7 +1291,7 @@ void DexLayout::DumpSField(uint32_t idx, uint32_t flags, int i, dex_ir::EncodedV return; } - dex_ir::FieldId* field_id = header_->GetCollections().GetFieldId(idx); + dex_ir::FieldId* field_id = header_->FieldIds()[idx]; const char* name = field_id->Name()->Data(); const char* type_descriptor = field_id->Type()->GetStringId()->Data(); const char* back_descriptor = field_id->Class()->GetStringId()->Data(); @@ -1346,7 +1345,7 @@ void DexLayout::DumpIField(uint32_t idx, uint32_t flags, int i) { * the value will be replaced with a newly-allocated string. */ void DexLayout::DumpClass(int idx, char** last_package) { - dex_ir::ClassDef* class_def = header_->GetCollections().GetClassDef(idx); + dex_ir::ClassDef* class_def = header_->ClassDefs()[idx]; // Omitting non-public class. if (options_.exports_only_ && (class_def->GetAccessFlags() & kAccPublic) == 0) { return; @@ -1364,8 +1363,7 @@ void DexLayout::DumpClass(int idx, char** last_package) { // up the classes, sort them, and dump them alphabetically so the // package name wouldn't jump around, but that's not a great plan // for something that needs to run on the device. - const char* class_descriptor = - header_->GetCollections().GetClassDef(idx)->ClassType()->GetStringId()->Data(); + const char* class_descriptor = header_->ClassDefs()[idx]->ClassType()->GetStringId()->Data(); if (!(class_descriptor[0] == 'L' && class_descriptor[strlen(class_descriptor)-1] == ';')) { // Arrays and primitives should not be defined explicitly. Keep going? @@ -1459,8 +1457,8 @@ void DexLayout::DumpClass(int idx, char** last_package) { dex_ir::FieldItemVector* static_fields = class_data->StaticFields(); if (static_fields != nullptr) { for (uint32_t i = 0; i < static_fields->size(); i++) { - DumpSField((*static_fields)[i]->GetFieldId()->GetIndex(), - (*static_fields)[i]->GetAccessFlags(), + DumpSField((*static_fields)[i].GetFieldId()->GetIndex(), + (*static_fields)[i].GetAccessFlags(), i, i < encoded_values_size ? (*encoded_values)[i].get() : nullptr); } // for @@ -1475,8 +1473,8 @@ void DexLayout::DumpClass(int idx, char** last_package) { dex_ir::FieldItemVector* instance_fields = class_data->InstanceFields(); if (instance_fields != nullptr) { for (uint32_t i = 0; i < instance_fields->size(); i++) { - DumpIField((*instance_fields)[i]->GetFieldId()->GetIndex(), - (*instance_fields)[i]->GetAccessFlags(), + DumpIField((*instance_fields)[i].GetFieldId()->GetIndex(), + (*instance_fields)[i].GetAccessFlags(), i); } // for } @@ -1490,9 +1488,9 @@ void DexLayout::DumpClass(int idx, char** last_package) { dex_ir::MethodItemVector* direct_methods = class_data->DirectMethods(); if (direct_methods != nullptr) { for (uint32_t i = 0; i < direct_methods->size(); i++) { - DumpMethod((*direct_methods)[i]->GetMethodId()->GetIndex(), - (*direct_methods)[i]->GetAccessFlags(), - (*direct_methods)[i]->GetCodeItem(), + DumpMethod((*direct_methods)[i].GetMethodId()->GetIndex(), + (*direct_methods)[i].GetAccessFlags(), + (*direct_methods)[i].GetCodeItem(), i); } // for } @@ -1506,9 +1504,9 @@ void DexLayout::DumpClass(int idx, char** last_package) { dex_ir::MethodItemVector* virtual_methods = class_data->VirtualMethods(); if (virtual_methods != nullptr) { for (uint32_t i = 0; i < virtual_methods->size(); i++) { - DumpMethod((*virtual_methods)[i]->GetMethodId()->GetIndex(), - (*virtual_methods)[i]->GetAccessFlags(), - (*virtual_methods)[i]->GetCodeItem(), + DumpMethod((*virtual_methods)[i].GetMethodId()->GetIndex(), + (*virtual_methods)[i].GetAccessFlags(), + (*virtual_methods)[i].GetCodeItem(), i); } // for } @@ -1543,7 +1541,7 @@ void DexLayout::DumpDexFile() { // Iterate over all classes. char* package = nullptr; - const uint32_t class_defs_size = header_->GetCollections().ClassDefsSize(); + const uint32_t class_defs_size = header_->ClassDefs().Size(); for (uint32_t i = 0; i < class_defs_size; i++) { DumpClass(i, &package); } // for @@ -1562,13 +1560,13 @@ void DexLayout::DumpDexFile() { void DexLayout::LayoutClassDefsAndClassData(const DexFile* dex_file) { std::vector new_class_def_order; - for (std::unique_ptr& class_def : header_->GetCollections().ClassDefs()) { + for (auto& class_def : header_->ClassDefs()) { dex::TypeIndex type_idx(class_def->ClassType()->GetIndex()); if (info_->ContainsClass(*dex_file, type_idx)) { new_class_def_order.push_back(class_def.get()); } } - for (std::unique_ptr& class_def : header_->GetCollections().ClassDefs()) { + for (auto& class_def : header_->ClassDefs()) { dex::TypeIndex type_idx(class_def->ClassType()->GetIndex()); if (!info_->ContainsClass(*dex_file, type_idx)) { new_class_def_order.push_back(class_def.get()); @@ -1576,8 +1574,7 @@ void DexLayout::LayoutClassDefsAndClassData(const DexFile* dex_file) { } std::unordered_set visited_class_data; size_t class_data_index = 0; - dex_ir::CollectionVector::Vector& class_datas = - header_->GetCollections().ClassDatas(); + auto& class_datas = header_->ClassDatas(); for (dex_ir::ClassDef* class_def : new_class_def_order) { dex_ir::ClassData* class_data = class_def->GetClassData(); if (class_data != nullptr && visited_class_data.find(class_data) == visited_class_data.end()) { @@ -1590,15 +1587,14 @@ void DexLayout::LayoutClassDefsAndClassData(const DexFile* dex_file) { ++class_data_index; } } - CHECK_EQ(class_data_index, class_datas.size()); + CHECK_EQ(class_data_index, class_datas.Size()); if (DexLayout::kChangeClassDefOrder) { // This currently produces dex files that violate the spec since the super class class_def is // supposed to occur before any subclasses. - dex_ir::CollectionVector::Vector& class_defs = - header_->GetCollections().ClassDefs(); - CHECK_EQ(new_class_def_order.size(), class_defs.size()); - for (size_t i = 0; i < class_defs.size(); ++i) { + dex_ir::CollectionVector& class_defs = header_->ClassDefs(); + CHECK_EQ(new_class_def_order.size(), class_defs.Size()); + for (size_t i = 0; i < class_defs.Size(); ++i) { // Overwrite the existing vector with the new ordering, note that the sets of objects are // equivalent, but the order changes. This is why this is not a memory leak. // TODO: Consider cleaning this up with a shared_ptr. @@ -1609,10 +1605,10 @@ void DexLayout::LayoutClassDefsAndClassData(const DexFile* dex_file) { } void DexLayout::LayoutStringData(const DexFile* dex_file) { - const size_t num_strings = header_->GetCollections().StringIds().size(); + const size_t num_strings = header_->StringIds().Size(); std::vector is_shorty(num_strings, false); std::vector from_hot_method(num_strings, false); - for (std::unique_ptr& class_def : header_->GetCollections().ClassDefs()) { + for (auto& class_def : header_->ClassDefs()) { // A name of a profile class is probably going to get looked up by ClassTable::Lookup, mark it // as hot. Add its super class and interfaces as well, which can be used during initialization. const bool is_profile_class = @@ -1636,14 +1632,14 @@ void DexLayout::LayoutStringData(const DexFile* dex_file) { } for (size_t i = 0; i < 2; ++i) { for (auto& method : *(i == 0 ? data->DirectMethods() : data->VirtualMethods())) { - const dex_ir::MethodId* method_id = method->GetMethodId(); - dex_ir::CodeItem* code_item = method->GetCodeItem(); + const dex_ir::MethodId* method_id = method.GetMethodId(); + dex_ir::CodeItem* code_item = method.GetCodeItem(); if (code_item == nullptr) { continue; } const bool is_clinit = is_profile_class && - (method->GetAccessFlags() & kAccConstructor) != 0 && - (method->GetAccessFlags() & kAccStatic) != 0; + (method.GetAccessFlags() & kAccConstructor) != 0 && + (method.GetAccessFlags() & kAccStatic) != 0; const bool method_executed = is_clinit || info_->GetMethodHotness(MethodReference(dex_file, method_id->GetIndex())).IsInProfile(); if (!method_executed) { @@ -1678,7 +1674,7 @@ void DexLayout::LayoutStringData(const DexFile* dex_file) { } // Sort string data by specified order. std::vector string_ids; - for (auto& string_id : header_->GetCollections().StringIds()) { + for (auto& string_id : header_->StringIds()) { string_ids.push_back(string_id.get()); } std::sort(string_ids.begin(), @@ -1699,8 +1695,7 @@ void DexLayout::LayoutStringData(const DexFile* dex_file) { // Order by index by default. return a->GetIndex() < b->GetIndex(); }); - dex_ir::CollectionVector::Vector& string_datas = - header_->GetCollections().StringDatas(); + auto& string_datas = header_->StringDatas(); // Now we know what order we want the string data, reorder them. size_t data_index = 0; for (dex_ir::StringId* string_id : string_ids) { @@ -1713,11 +1708,11 @@ void DexLayout::LayoutStringData(const DexFile* dex_file) { for (const std::unique_ptr& data : string_datas) { visited.insert(data.get()); } - for (auto& string_id : header_->GetCollections().StringIds()) { + for (auto& string_id : header_->StringIds()) { CHECK(visited.find(string_id->DataItem()) != visited.end()); } } - CHECK_EQ(data_index, string_datas.size()); + CHECK_EQ(data_index, string_datas.Size()); } // Orders code items according to specified class data ordering. @@ -1732,7 +1727,7 @@ void DexLayout::LayoutCodeItems(const DexFile* dex_file) { // Assign hotness flags to all code items. for (InvokeType invoke_type : invoke_types) { - for (std::unique_ptr& class_def : header_->GetCollections().ClassDefs()) { + for (auto& class_def : header_->ClassDefs()) { const bool is_profile_class = info_->ContainsClass(*dex_file, dex::TypeIndex(class_def->ClassType()->GetIndex())); @@ -1744,14 +1739,14 @@ void DexLayout::LayoutCodeItems(const DexFile* dex_file) { for (auto& method : *(invoke_type == InvokeType::kDirect ? class_data->DirectMethods() : class_data->VirtualMethods())) { - const dex_ir::MethodId *method_id = method->GetMethodId(); - dex_ir::CodeItem *code_item = method->GetCodeItem(); + const dex_ir::MethodId *method_id = method.GetMethodId(); + dex_ir::CodeItem *code_item = method.GetCodeItem(); if (code_item == nullptr) { continue; } // Separate executed methods (clinits and profiled methods) from unexecuted methods. - const bool is_clinit = (method->GetAccessFlags() & kAccConstructor) != 0 && - (method->GetAccessFlags() & kAccStatic) != 0; + const bool is_clinit = (method.GetAccessFlags() & kAccConstructor) != 0 && + (method.GetAccessFlags() & kAccStatic) != 0; const bool is_startup_clinit = is_profile_class && is_clinit; using Hotness = ProfileCompilationInfo::MethodHotness; Hotness hotness = info_->GetMethodHotness(MethodReference(dex_file, method_id->GetIndex())); @@ -1778,8 +1773,7 @@ void DexLayout::LayoutCodeItems(const DexFile* dex_file) { } } - dex_ir::CollectionVector::Vector& code_items = - header_->GetCollections().CodeItems(); + const auto& code_items = header_->CodeItems(); if (VLOG_IS_ON(dex)) { size_t layout_count[static_cast(LayoutType::kLayoutTypeCount)] = {}; for (const std::unique_ptr& code_item : code_items) { @@ -1871,7 +1865,7 @@ bool DexLayout::ProcessDexFile(const char* file_name, const bool has_output_container = dex_container != nullptr; const bool output = options_.output_dex_directory_ != nullptr || has_output_container; - // Try to avoid eagerly assigning offsets to find bugs since GetOffset will abort if the offset + // Try to avoid eagerly assigning offsets to find bugs since Offset will abort if the offset // is unassigned. bool eagerly_assign_offsets = false; if (options_.visualize_pattern_ || options_.show_section_statistics_ || options_.dump_) { diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc index f30cfee4ec0b681db4436651b7e775a506af5323..71e56d19ea2cf2678d6d8ea259a8106684bc4707 100644 --- a/dexlayout/dexlayout_main.cc +++ b/dexlayout/dexlayout_main.cc @@ -32,9 +32,8 @@ #include #include "base/logging.h" // For InitLogging. -#include "jit/profile_compilation_info.h" -#include "mem_map.h" -#include "runtime.h" +#include "base/mem_map.h" +#include "profile/profile_compilation_info.h" namespace art { @@ -66,12 +65,17 @@ static void Usage(void) { LOG(ERROR) << " -x : compact dex generation level, either 'none' or 'fast'"; } +NO_RETURN static void Abort(const char* msg) { + LOG(ERROR) << msg; + exit(1); +} + /* * Main driver of the dexlayout utility. */ int DexlayoutDriver(int argc, char** argv) { // Art specific set up. - InitLogging(argv, Runtime::Abort); + InitLogging(argv, Abort); MemMap::Init(); Options options; diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc index 6719d0848c54e7716e9a88eea0d75cd557a4f842..2b1352db16d25b22f1042c70d5cd41c731dcadb1 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -31,7 +31,7 @@ #include "dex/dex_file_loader.h" #include "dexlayout.h" #include "exec_utils.h" -#include "jit/profile_compilation_info.h" +#include "profile/profile_compilation_info.h" namespace art { @@ -468,7 +468,7 @@ class DexLayoutTest : public CommonRuntimeTest { } std::vector test_files = { dex_file, profile_file, output_dex, second_output_dex }; - for (auto test_file : test_files) { + for (const std::string& test_file : test_files) { if (!UnlinkFile(test_file)) { return false; } @@ -501,7 +501,7 @@ class DexLayoutTest : public CommonRuntimeTest { } std::vector dex_files = { input_dex, output_dex }; - for (auto dex_file : dex_files) { + for (const std::string& dex_file : dex_files) { if (!UnlinkFile(dex_file)) { return false; } diff --git a/dexlist/Android.bp b/dexlist/Android.bp index 2703732db67b7a43e1c80fd5a3664c624e1271a6..bd521acbc004e4b66b60202a48302976320b2f24 100644 --- a/dexlist/Android.bp +++ b/dexlist/Android.bp @@ -17,7 +17,11 @@ art_cc_binary { host_supported: true, srcs: ["dexlist.cc"], cflags: ["-Wall", "-Werror"], - shared_libs: ["libdexfile", "libbase"], + shared_libs: [ + "libdexfile", + "libartbase", + "libbase" + ], // TODO: fix b/72216369 and remove the need for this. include_dirs: [ "art/runtime" // dex utils. diff --git a/dexlist/dexlist_test.cc b/dexlist/dexlist_test.cc index 0b9adbddb8cf856be6dd06c631f0b7d0b3b14db5..68e67134cc5f0adbd28a94f3cce9720544bd414a 100644 --- a/dexlist/dexlist_test.cc +++ b/dexlist/dexlist_test.cc @@ -60,7 +60,7 @@ TEST_F(DexListTest, NoInputFileGiven) { TEST_F(DexListTest, CantOpenOutput) { std::string error_msg; - ASSERT_FALSE(Exec({"-o", "/joho", dex_file_}, &error_msg)) << error_msg; + ASSERT_FALSE(Exec({"-o", "/non/existent/path", dex_file_}, &error_msg)) << error_msg; } TEST_F(DexListTest, IllFormedMethod) { diff --git a/dexoptanalyzer/Android.bp b/dexoptanalyzer/Android.bp index 33366ad371e4563eab56a61841a87b974f795eda..99a11cd59b0b9193671216338ad19564164be07c 100644 --- a/dexoptanalyzer/Android.bp +++ b/dexoptanalyzer/Android.bp @@ -38,6 +38,7 @@ art_cc_binary { defaults: ["dexoptanalyzer-defaults"], shared_libs: [ "libart", + "libartbase", ], } @@ -49,6 +50,7 @@ art_cc_binary { ], shared_libs: [ "libartd", + "libartbased", ], } diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc index b5f5d6ff105ef8f09f28dd42f4652c145f95621b..eaf11be7f27cd00b29e619376c75f2e2f25096a0 100644 --- a/disassembler/disassembler_mips.cc +++ b/disassembler/disassembler_mips.cc @@ -487,6 +487,7 @@ static const MipsInstruction gMipsInstructions[] = { { kMsaMask | (0xf << 22), kMsa | (0x3 << 22) | 0x19, "copy_u", "yX" }, { kMsaMask | (0xf << 22), kMsa | (0x4 << 22) | 0x19, "insert", "YD" }, { kMsaMask | (0xff << 18), kMsa | (0xc0 << 18) | 0x1e, "fill", "vkD" }, + { kMsaMask | (0xff << 18), kMsa | (0xc1 << 18) | 0x1e, "pcnt", "vkm" }, { kMsaMask | (0x7 << 23), kMsa | (0x6 << 23) | 0x7, "ldi", "kx" }, { kMsaSpecialMask | (0xf << 2), kMsa | (0x8 << 2), "ld", "kw" }, { kMsaSpecialMask | (0xf << 2), kMsa | (0x9 << 2), "st", "kw" }, diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc index bbc8e370ea6f92ee9f539fc6bfd32e35b604a37d..dbdde647b2c812fe9e4f8ebcac66bda928192d3a 100644 --- a/disassembler/disassembler_x86.cc +++ b/disassembler/disassembler_x86.cc @@ -1194,11 +1194,19 @@ DISASSEMBLER_ENTRY(cmp, opcode1 = opcode_tmp.c_str(); } break; + case 0xD8: + case 0xD9: case 0xDA: + case 0xDC: + case 0xDD: case 0xDE: case 0xE0: case 0xE3: + case 0xE8: + case 0xE9: case 0xEA: + case 0xEC: + case 0xED: case 0xEE: if (prefix[2] == 0x66) { src_reg_file = dst_reg_file = SSE; @@ -1207,11 +1215,19 @@ DISASSEMBLER_ENTRY(cmp, src_reg_file = dst_reg_file = MMX; } switch (*instr) { + case 0xD8: opcode1 = "psubusb"; break; + case 0xD9: opcode1 = "psubusw"; break; case 0xDA: opcode1 = "pminub"; break; + case 0xDC: opcode1 = "paddusb"; break; + case 0xDD: opcode1 = "paddusw"; break; case 0xDE: opcode1 = "pmaxub"; break; case 0xE0: opcode1 = "pavgb"; break; case 0xE3: opcode1 = "pavgw"; break; + case 0xE8: opcode1 = "psubsb"; break; + case 0xE9: opcode1 = "psubsw"; break; case 0xEA: opcode1 = "pminsw"; break; + case 0xEC: opcode1 = "paddsb"; break; + case 0xED: opcode1 = "paddsw"; break; case 0xEE: opcode1 = "pmaxsw"; break; } prefix[2] = 0; diff --git a/imgdiag/Android.bp b/imgdiag/Android.bp index 2b89497e8eeab72cac877f6cc43be2b8820c8fd1..972c8f71898cfd83e933abe7478f94c36dcb0f79 100644 --- a/imgdiag/Android.bp +++ b/imgdiag/Android.bp @@ -57,6 +57,7 @@ art_cc_binary { defaults: ["imgdiag-defaults"], shared_libs: [ "libart", + "libartbase", "libart-compiler", ], } @@ -69,6 +70,7 @@ art_cc_binary { ], shared_libs: [ "libartd", + "libartbased", "libartd-compiler", ], } diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc index ddb8fe1302027e31a4d9bec8765ad6e5a8934f98..24ee0892dcf1baa22b1afafb54bd6690495d9d05 100644 --- a/imgdiag/imgdiag.cc +++ b/imgdiag/imgdiag.cc @@ -176,8 +176,11 @@ static T* FixUpRemotePointer(T* remote_ptr, uintptr_t remote = reinterpret_cast(remote_ptr); - CHECK_LE(boot_map.start, remote); - CHECK_GT(boot_map.end, remote); + // In the case the remote pointer is out of range, it probably belongs to another image. + // Just return null for this case. + if (remote < boot_map.start || remote >= boot_map.end) { + return nullptr; + } off_t boot_offset = remote - boot_map.start; @@ -338,7 +341,7 @@ class ImgObjectVisitor : public ObjectVisitor { ImgObjectVisitor(ComputeDirtyFunc dirty_func, const uint8_t* begin_image_ptr, const std::set& dirty_pages) : - dirty_func_(dirty_func), + dirty_func_(std::move(dirty_func)), begin_image_ptr_(begin_image_ptr), dirty_pages_(dirty_pages) { } @@ -356,7 +359,7 @@ class ImgObjectVisitor : public ObjectVisitor { } private: - ComputeDirtyFunc dirty_func_; + const ComputeDirtyFunc dirty_func_; const uint8_t* begin_image_ptr_; const std::set& dirty_pages_; }; @@ -649,7 +652,7 @@ class ImgArtMethodVisitor : public ArtMethodVisitor { ImgArtMethodVisitor(ComputeDirtyFunc dirty_func, const uint8_t* begin_image_ptr, const std::set& dirty_pages) : - dirty_func_(dirty_func), + dirty_func_(std::move(dirty_func)), begin_image_ptr_(begin_image_ptr), dirty_pages_(dirty_pages) { } virtual ~ImgArtMethodVisitor() OVERRIDE { } @@ -658,7 +661,7 @@ class ImgArtMethodVisitor : public ArtMethodVisitor { } private: - ComputeDirtyFunc dirty_func_; + const ComputeDirtyFunc dirty_func_; const uint8_t* begin_image_ptr_; const std::set& dirty_pages_; }; diff --git a/libartbase/Android.bp b/libartbase/Android.bp index 3c619444770f8905eba5f7a49434bb628153af14..4ee48da5e82825548a6b0694639adbd193de6a9f 100644 --- a/libartbase/Android.bp +++ b/libartbase/Android.bp @@ -19,26 +19,58 @@ cc_defaults { defaults: ["art_defaults"], host_supported: true, srcs: [ + "arch/instruction_set.cc", "base/allocator.cc", + "base/arena_allocator.cc", + "base/arena_bit_vector.cc", "base/bit_vector.cc", "base/file_magic.cc", + "base/file_utils.cc", "base/hex_dump.cc", "base/logging.cc", + "base/malloc_arena_pool.cc", + "base/memory_region.cc", + "base/mem_map.cc", + // "base/mem_map_fuchsia.cc", put in target when fuchsia supported by soong + "base/mem_map_unix.cc", "base/os_linux.cc", "base/runtime_debug.cc", "base/safe_copy.cc", + "base/scoped_arena_allocator.cc", "base/scoped_flock.cc", "base/time_utils.cc", "base/unix_file/fd_file.cc", "base/unix_file/random_access_file_utils.cc", "base/utils.cc", + "base/zip_archive.cc", ], + target: { + android: { + static_libs: [ + // ZipArchive support, the order matters here to get all symbols. + "libziparchive", + "libz", + ], + shared_libs: [ + // For android::FileMap used by libziparchive. + "libutils", + ], + }, + host: { + shared_libs: [ + "libziparchive", + "libz", + ], + }, + }, generated_sources: ["art_libartbase_operator_srcs"], cflags: ["-DBUILDING_LIBART=1"], shared_libs: [ + "liblog", + // For ashmem. + "libcutils", // For common macros. "libbase", - "liblog", ], export_include_dirs: ["."], // ART's macros.h depends on libbase's macros.h. @@ -52,6 +84,7 @@ gensrcs { cmd: "$(location generate_operator_out) art/libartbase $(in) > $(out)", tools: ["generate_operator_out"], srcs: [ + "arch/instruction_set.h", "base/allocator.h", "base/callee_save_type.h", "base/unix_file/fd_file.h", @@ -67,7 +100,10 @@ art_cc_library { strip: { keep_symbols: true, }, - shared_libs: ["libbase"], + shared_libs: [ + "libbase", + "libziparchive", + ], export_shared_lib_headers: ["libbase"], } @@ -77,29 +113,57 @@ art_cc_library { "art_debug_defaults", "libartbase_defaults", ], - shared_libs: ["libbase"], + shared_libs: [ + "libbase", + "libziparchive", + ], export_shared_lib_headers: ["libbase"], } -// For now many of these tests still use CommonRuntimeTest, almost universally because of -// ScratchFile and related. -// TODO: Remove CommonRuntimeTest dependency from these tests. +art_cc_library { + name: "libartbase-art-gtest", + defaults: ["libart-gtest-defaults"], + srcs: [ + "base/common_art_test.cc", + ], + shared_libs: [ + "libartbased", + "libdexfiled", + "libbase", + "libbacktrace", + ], + header_libs: [ + "libnativehelper_header_only", + ], + include_dirs: [ + "external/icu/icu4c/source/common", + ], +} + art_cc_test { name: "art_libartbase_tests", defaults: [ "art_gtest_defaults", ], srcs: [ + "arch/instruction_set_test.cc", + "base/arena_allocator_test.cc", "base/bit_field_test.cc", + "base/bit_memory_region_test.cc", "base/bit_string_test.cc", "base/bit_struct_test.cc", + "base/bit_table_test.cc", "base/bit_utils_test.cc", "base/bit_vector_test.cc", + "base/file_utils_test.cc", "base/hash_set_test.cc", "base/hex_dump_test.cc", "base/histogram_test.cc", + "base/indenter_test.cc", "base/leb128_test.cc", "base/logging_test.cc", + "base/memory_region_test.cc", + "base/mem_map_test.cc", "base/safe_copy_test.cc", "base/scoped_flock_test.cc", "base/time_utils_test.cc", @@ -108,6 +172,7 @@ art_cc_test { "base/unix_file/fd_file_test.cc", "base/utils_test.cc", "base/variant_map_test.cc", + "base/zip_archive_test.cc", ], shared_libs: [ "libbase", diff --git a/runtime/arch/instruction_set.cc b/libartbase/arch/instruction_set.cc similarity index 88% rename from runtime/arch/instruction_set.cc rename to libartbase/arch/instruction_set.cc index ecccdcf7ebfaef460902c11cc4b83056a23edb68..a187663062d0177ba91f640fa0cea3772058c4bf 100644 --- a/runtime/arch/instruction_set.cc +++ b/libartbase/arch/instruction_set.cc @@ -16,11 +16,9 @@ #include "instruction_set.h" -// Explicitly include our own elf.h to avoid Linux and other dependencies. -#include "../elf.h" #include "android-base/logging.h" #include "base/bit_utils.h" -#include "globals.h" +#include "base/globals.h" namespace art { @@ -83,29 +81,6 @@ InstructionSet GetInstructionSetFromString(const char* isa_str) { return InstructionSet::kNone; } -InstructionSet GetInstructionSetFromELF(uint16_t e_machine, uint32_t e_flags) { - switch (e_machine) { - case EM_ARM: - return InstructionSet::kArm; - case EM_AARCH64: - return InstructionSet::kArm64; - case EM_386: - return InstructionSet::kX86; - case EM_X86_64: - return InstructionSet::kX86_64; - case EM_MIPS: { - if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R2 || - (e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) { - return InstructionSet::kMips; - } else if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_64R6) { - return InstructionSet::kMips64; - } - break; - } - } - return InstructionSet::kNone; -} - size_t GetInstructionSetAlignment(InstructionSet isa) { switch (isa) { case InstructionSet::kArm: diff --git a/runtime/arch/instruction_set.h b/libartbase/arch/instruction_set.h similarity index 97% rename from runtime/arch/instruction_set.h rename to libartbase/arch/instruction_set.h index 6434005ddaea288357a385f951a5a218d1af113e..06bd53a6a978affdbd50333685cd413333a8bd84 100644 --- a/runtime/arch/instruction_set.h +++ b/libartbase/arch/instruction_set.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_ARCH_INSTRUCTION_SET_H_ -#define ART_RUNTIME_ARCH_INSTRUCTION_SET_H_ +#ifndef ART_LIBARTBASE_ARCH_INSTRUCTION_SET_H_ +#define ART_LIBARTBASE_ARCH_INSTRUCTION_SET_H_ #include #include @@ -89,8 +89,6 @@ const char* GetInstructionSetString(InstructionSet isa); // Note: Returns kNone when the string cannot be parsed to a known value. InstructionSet GetInstructionSetFromString(const char* instruction_set); -InstructionSet GetInstructionSetFromELF(uint16_t e_machine, uint32_t e_flags); - // Fatal logging out of line to keep the header clean of logging.h. NO_RETURN void InstructionSetAbort(InstructionSet isa); @@ -299,4 +297,4 @@ static inline TwoWordReturn GetTwoWordSuccessValue(uintptr_t hi, uintptr_t lo) { } // namespace art -#endif // ART_RUNTIME_ARCH_INSTRUCTION_SET_H_ +#endif // ART_LIBARTBASE_ARCH_INSTRUCTION_SET_H_ diff --git a/runtime/arch/instruction_set_test.cc b/libartbase/arch/instruction_set_test.cc similarity index 100% rename from runtime/arch/instruction_set_test.cc rename to libartbase/arch/instruction_set_test.cc diff --git a/libartbase/base/allocator.cc b/libartbase/base/allocator.cc index a42414507b29deea8db82a1abaff528a4ff1e0ee..c4ac180a15fd80b976ad28643a623e528ae4871e 100644 --- a/libartbase/base/allocator.cc +++ b/libartbase/base/allocator.cc @@ -21,7 +21,7 @@ #include -#include "base/atomic.h" +#include "atomic.h" namespace art { @@ -76,16 +76,16 @@ namespace TrackedAllocators { // These globals are safe since they don't have any non-trivial destructors. Atomic g_bytes_used[kAllocatorTagCount]; -volatile size_t g_max_bytes_used[kAllocatorTagCount]; +Atomic g_max_bytes_used[kAllocatorTagCount]; Atomic g_total_bytes_used[kAllocatorTagCount]; void Dump(std::ostream& os) { if (kEnableTrackingAllocator) { os << "Dumping native memory usage\n"; for (size_t i = 0; i < kAllocatorTagCount; ++i) { - uint64_t bytes_used = g_bytes_used[i].LoadRelaxed(); - uint64_t max_bytes_used = g_max_bytes_used[i]; - uint64_t total_bytes_used = g_total_bytes_used[i].LoadRelaxed(); + uint64_t bytes_used = g_bytes_used[i].load(std::memory_order_relaxed); + uint64_t max_bytes_used = g_max_bytes_used[i].load(std::memory_order_relaxed); + uint64_t total_bytes_used = g_total_bytes_used[i].load(std::memory_order_relaxed); if (total_bytes_used != 0) { os << static_cast(i) << " active=" << bytes_used << " max=" << max_bytes_used << " total=" << total_bytes_used << "\n"; diff --git a/libartbase/base/allocator.h b/libartbase/base/allocator.h index d92fe193e6b717ebfd4403e73b903b4cfd5020c0..5eb6ea6b4c597587f64136c0f1b61a432dc2e2e3 100644 --- a/libartbase/base/allocator.h +++ b/libartbase/base/allocator.h @@ -19,8 +19,8 @@ #include -#include "base/atomic.h" -#include "base/macros.h" +#include "atomic.h" +#include "macros.h" namespace art { @@ -71,12 +71,14 @@ std::ostream& operator<<(std::ostream& os, const AllocatorTag& tag); namespace TrackedAllocators { +// We use memory_order_relaxed updates of the following counters. Values are treated as approximate +// wherever concurrent updates are possible. // Running count of number of bytes used for this kind of allocation. Increased by allocations, // decreased by deallocations. extern Atomic g_bytes_used[kAllocatorTagCount]; // Largest value of bytes used seen. -extern volatile size_t g_max_bytes_used[kAllocatorTagCount]; +extern Atomic g_max_bytes_used[kAllocatorTagCount]; // Total number of bytes allocated of this kind. extern Atomic g_total_bytes_used[kAllocatorTagCount]; @@ -84,15 +86,17 @@ extern Atomic g_total_bytes_used[kAllocatorTagCount]; void Dump(std::ostream& os); inline void RegisterAllocation(AllocatorTag tag, size_t bytes) { - g_total_bytes_used[tag].FetchAndAddSequentiallyConsistent(bytes); - size_t new_bytes = g_bytes_used[tag].FetchAndAddSequentiallyConsistent(bytes) + bytes; - if (g_max_bytes_used[tag] < new_bytes) { - g_max_bytes_used[tag] = new_bytes; + g_total_bytes_used[tag].fetch_add(bytes, std::memory_order_relaxed); + size_t new_bytes = g_bytes_used[tag].fetch_add(bytes, std::memory_order_relaxed) + bytes; + size_t max_bytes = g_max_bytes_used[tag].load(std::memory_order_relaxed); + while (max_bytes < new_bytes + && !g_max_bytes_used[tag].compare_exchange_weak(max_bytes /* updated */, new_bytes, + std::memory_order_relaxed)) { } } inline void RegisterFree(AllocatorTag tag, size_t bytes) { - g_bytes_used[tag].FetchAndSubSequentiallyConsistent(bytes); + g_bytes_used[tag].fetch_sub(bytes, std::memory_order_relaxed); } } // namespace TrackedAllocators diff --git a/runtime/base/arena_allocator-inl.h b/libartbase/base/arena_allocator-inl.h similarity index 86% rename from runtime/base/arena_allocator-inl.h rename to libartbase/base/arena_allocator-inl.h index 0e4383741ecc8b1d1bd628f5d7986e31343dad9f..a03e9df3fe445d45db1af6d34915b5356bd26247 100644 --- a/runtime/base/arena_allocator-inl.h +++ b/libartbase/base/arena_allocator-inl.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_ARENA_ALLOCATOR_INL_H_ -#define ART_RUNTIME_BASE_ARENA_ALLOCATOR_INL_H_ +#ifndef ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_INL_H_ +#define ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_INL_H_ #include "arena_allocator.h" @@ -31,4 +31,4 @@ static constexpr size_t kArenaDefaultSize = kArenaAllocatorPreciseTracking } // namespace arena_allocator } // namespace art -#endif // ART_RUNTIME_BASE_ARENA_ALLOCATOR_INL_H_ +#endif // ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_INL_H_ diff --git a/runtime/base/arena_allocator.cc b/libartbase/base/arena_allocator.cc similarity index 67% rename from runtime/base/arena_allocator.cc rename to libartbase/base/arena_allocator.cc index 292bde02721ff4c358d19807581804ce9e30e94d..df3deba17887b9935b30789c6210bfa5322cb650 100644 --- a/runtime/base/arena_allocator.cc +++ b/libartbase/base/arena_allocator.cc @@ -25,11 +25,6 @@ #include -#include "base/systrace.h" -#include "mem_map.h" -#include "mutex.h" -#include "thread-current-inl.h" - namespace art { constexpr size_t kMemoryToolRedZoneBytes = 8; @@ -56,6 +51,7 @@ const char* const ArenaAllocatorStatsImpl::kAllocNames[] = { "CtorFenceIns ", "InvokeInputs ", "PhiInputs ", + "TypeCheckIns ", "LoopInfo ", "LIBackEdges ", "TryCatchInf ", @@ -81,10 +77,12 @@ const char* const ArenaAllocatorStatsImpl::kAllocNames[] = { "SsaLiveness ", "SsaPhiElim ", "RefTypeProp ", + "SelectGen ", "SideEffects ", "RegAllocator ", "RegAllocVldt ", "StackMapStm ", + "BitTableBld ", "VectorNode ", "CodeGen ", "Assembler ", @@ -189,194 +187,6 @@ void ArenaAllocatorMemoryTool::DoMakeInaccessible(void* ptr, size_t size) { Arena::Arena() : bytes_allocated_(0), memory_(nullptr), size_(0), next_(nullptr) { } -class MallocArena FINAL : public Arena { - public: - explicit MallocArena(size_t size = arena_allocator::kArenaDefaultSize); - virtual ~MallocArena(); - private: - static constexpr size_t RequiredOverallocation() { - return (alignof(std::max_align_t) < ArenaAllocator::kArenaAlignment) - ? ArenaAllocator::kArenaAlignment - alignof(std::max_align_t) - : 0u; - } - - uint8_t* unaligned_memory_; -}; - -MallocArena::MallocArena(size_t size) { - // We need to guarantee kArenaAlignment aligned allocation for the new arena. - // TODO: Use std::aligned_alloc() when it becomes available with C++17. - constexpr size_t overallocation = RequiredOverallocation(); - unaligned_memory_ = reinterpret_cast(calloc(1, size + overallocation)); - CHECK(unaligned_memory_ != nullptr); // Abort on OOM. - DCHECK_ALIGNED(unaligned_memory_, alignof(std::max_align_t)); - if (overallocation == 0u) { - memory_ = unaligned_memory_; - } else { - memory_ = AlignUp(unaligned_memory_, ArenaAllocator::kArenaAlignment); - if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { - size_t head = memory_ - unaligned_memory_; - size_t tail = overallocation - head; - MEMORY_TOOL_MAKE_NOACCESS(unaligned_memory_, head); - MEMORY_TOOL_MAKE_NOACCESS(memory_ + size, tail); - } - } - DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment); - size_ = size; -} - -MallocArena::~MallocArena() { - constexpr size_t overallocation = RequiredOverallocation(); - if (overallocation != 0u && UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { - size_t head = memory_ - unaligned_memory_; - size_t tail = overallocation - head; - MEMORY_TOOL_MAKE_UNDEFINED(unaligned_memory_, head); - MEMORY_TOOL_MAKE_UNDEFINED(memory_ + size_, tail); - } - free(reinterpret_cast(unaligned_memory_)); -} - -class MemMapArena FINAL : public Arena { - public: - MemMapArena(size_t size, bool low_4gb, const char* name); - virtual ~MemMapArena(); - void Release() OVERRIDE; - - private: - std::unique_ptr map_; -}; - -MemMapArena::MemMapArena(size_t size, bool low_4gb, const char* name) { - // Round up to a full page as that's the smallest unit of allocation for mmap() - // and we want to be able to use all memory that we actually allocate. - size = RoundUp(size, kPageSize); - std::string error_msg; - map_.reset(MemMap::MapAnonymous( - name, nullptr, size, PROT_READ | PROT_WRITE, low_4gb, false, &error_msg)); - CHECK(map_.get() != nullptr) << error_msg; - memory_ = map_->Begin(); - static_assert(ArenaAllocator::kArenaAlignment <= kPageSize, - "Arena should not need stronger alignment than kPageSize."); - DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment); - size_ = map_->Size(); -} - -MemMapArena::~MemMapArena() { - // Destroys MemMap via std::unique_ptr<>. -} - -void MemMapArena::Release() { - if (bytes_allocated_ > 0) { - map_->MadviseDontNeedAndZero(); - bytes_allocated_ = 0; - } -} - -void Arena::Reset() { - if (bytes_allocated_ > 0) { - memset(Begin(), 0, bytes_allocated_); - bytes_allocated_ = 0; - } -} - -ArenaPool::ArenaPool(bool use_malloc, bool low_4gb, const char* name) - : use_malloc_(use_malloc), - lock_("Arena pool lock", kArenaPoolLock), - free_arenas_(nullptr), - low_4gb_(low_4gb), - name_(name) { - if (low_4gb) { - CHECK(!use_malloc) << "low4gb must use map implementation"; - } - if (!use_malloc) { - MemMap::Init(); - } -} - -ArenaPool::~ArenaPool() { - ReclaimMemory(); -} - -void ArenaPool::ReclaimMemory() { - while (free_arenas_ != nullptr) { - Arena* arena = free_arenas_; - free_arenas_ = free_arenas_->next_; - delete arena; - } -} - -void ArenaPool::LockReclaimMemory() { - MutexLock lock(Thread::Current(), lock_); - ReclaimMemory(); -} - -Arena* ArenaPool::AllocArena(size_t size) { - Thread* self = Thread::Current(); - Arena* ret = nullptr; - { - MutexLock lock(self, lock_); - if (free_arenas_ != nullptr && LIKELY(free_arenas_->Size() >= size)) { - ret = free_arenas_; - free_arenas_ = free_arenas_->next_; - } - } - if (ret == nullptr) { - ret = use_malloc_ ? static_cast(new MallocArena(size)) : - new MemMapArena(size, low_4gb_, name_); - } - ret->Reset(); - return ret; -} - -void ArenaPool::TrimMaps() { - if (!use_malloc_) { - ScopedTrace trace(__PRETTY_FUNCTION__); - // Doesn't work for malloc. - MutexLock lock(Thread::Current(), lock_); - for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) { - arena->Release(); - } - } -} - -size_t ArenaPool::GetBytesAllocated() const { - size_t total = 0; - MutexLock lock(Thread::Current(), lock_); - for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) { - total += arena->GetBytesAllocated(); - } - return total; -} - -void ArenaPool::FreeArenaChain(Arena* first) { - if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { - for (Arena* arena = first; arena != nullptr; arena = arena->next_) { - MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_); - } - } - - if (arena_allocator::kArenaAllocatorPreciseTracking) { - // Do not reuse arenas when tracking. - while (first != nullptr) { - Arena* next = first->next_; - delete first; - first = next; - } - return; - } - - if (first != nullptr) { - Arena* last = first; - while (last->next_ != nullptr) { - last = last->next_; - } - Thread* self = Thread::Current(); - MutexLock lock(self, lock_); - last->next_ = free_arenas_; - free_arenas_ = first; - } -} - size_t ArenaAllocator::BytesAllocated() const { return ArenaAllocatorStats::BytesAllocated(); } diff --git a/runtime/base/arena_allocator.h b/libartbase/base/arena_allocator.h similarity index 84% rename from runtime/base/arena_allocator.h rename to libartbase/base/arena_allocator.h index c3011091e856760b49d9835cd7fed9979b72d28f..a9ccae1b07369912d280d51d71090029bbbad658 100644 --- a/runtime/base/arena_allocator.h +++ b/libartbase/base/arena_allocator.h @@ -14,18 +14,17 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_ARENA_ALLOCATOR_H_ -#define ART_RUNTIME_BASE_ARENA_ALLOCATOR_H_ +#ifndef ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_H_ +#define ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_H_ #include #include -#include "base/bit_utils.h" -#include "base/debug_stack.h" -#include "base/dchecked_vector.h" -#include "base/macros.h" -#include "base/memory_tool.h" -#include "mutex.h" +#include "bit_utils.h" +#include "debug_stack.h" +#include "dchecked_vector.h" +#include "macros.h" +#include "memory_tool.h" namespace art { @@ -62,6 +61,7 @@ enum ArenaAllocKind { kArenaAllocConstructorFenceInputs, kArenaAllocInvokeInputs, kArenaAllocPhiInputs, + kArenaAllocTypeCheckInputs, kArenaAllocLoopInfo, kArenaAllocLoopInfoBackEdges, kArenaAllocTryCatchInfo, @@ -87,10 +87,12 @@ enum ArenaAllocKind { kArenaAllocSsaLiveness, kArenaAllocSsaPhiElimination, kArenaAllocReferenceTypePropagation, + kArenaAllocSelectGenerator, kArenaAllocSideEffectsAnalysis, kArenaAllocRegisterAllocator, kArenaAllocRegisterAllocatorValidate, kArenaAllocStackMapStream, + kArenaAllocBitTableBuilder, kArenaAllocVectorNode, kArenaAllocCodeGenerator, kArenaAllocAssembler, @@ -146,34 +148,9 @@ class ArenaAllocatorStatsImpl { typedef ArenaAllocatorStatsImpl ArenaAllocatorStats; -template -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 -class ArenaAllocatorMemoryToolCheckImpl { - public: - ArenaAllocatorMemoryToolCheckImpl() { } - bool IsRunningOnMemoryTool() { return kAvailable; } -}; - -typedef ArenaAllocatorMemoryToolCheckImpl - ArenaAllocatorMemoryToolCheck; - -class ArenaAllocatorMemoryTool : private ArenaAllocatorMemoryToolCheck { - public: - using ArenaAllocatorMemoryToolCheck::IsRunningOnMemoryTool; + bool IsRunningOnMemoryTool() { return kMemoryToolIsAvailable; } void MakeDefined(void* ptr, size_t size) { if (UNLIKELY(IsRunningOnMemoryTool())) { @@ -235,7 +212,8 @@ class Arena { uint8_t* memory_; size_t size_; Arena* next_; - friend class ArenaPool; + friend class MallocArenaPool; + friend class MemMapArenaPool; friend class ArenaAllocator; friend class ArenaStack; friend class ScopedArenaAllocator; @@ -249,25 +227,20 @@ class Arena { class ArenaPool { public: - explicit ArenaPool(bool use_malloc = true, - bool low_4gb = false, - const char* name = "LinearAlloc"); - ~ArenaPool(); - Arena* AllocArena(size_t size) REQUIRES(!lock_); - void FreeArenaChain(Arena* first) REQUIRES(!lock_); - size_t GetBytesAllocated() const REQUIRES(!lock_); - void ReclaimMemory() NO_THREAD_SAFETY_ANALYSIS; - void LockReclaimMemory() REQUIRES(!lock_); - // Trim the maps in arenas by madvising, used by JIT to reduce memory usage. This only works - // use_malloc is false. - void TrimMaps() REQUIRES(!lock_); + virtual ~ArenaPool() = default; + + virtual Arena* AllocArena(size_t size) = 0; + virtual void FreeArenaChain(Arena* first) = 0; + virtual size_t GetBytesAllocated() const = 0; + virtual void ReclaimMemory() = 0; + virtual void LockReclaimMemory() = 0; + // Trim the maps in arenas by madvising, used by JIT to reduce memory usage. + virtual void TrimMaps() = 0; + + protected: + ArenaPool() = default; private: - const bool use_malloc_; - mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - Arena* free_arenas_ GUARDED_BY(lock_); - const bool low_4gb_; - const char* name_; DISALLOW_COPY_AND_ASSIGN(ArenaPool); }; @@ -427,4 +400,4 @@ class MemStats { } // namespace art -#endif // ART_RUNTIME_BASE_ARENA_ALLOCATOR_H_ +#endif // ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_H_ diff --git a/runtime/base/arena_allocator_test.cc b/libartbase/base/arena_allocator_test.cc similarity index 94% rename from runtime/base/arena_allocator_test.cc rename to libartbase/base/arena_allocator_test.cc index 68e26af9f8b2d8453515085c01c3bb2f7b338fd7..6323a2b97c49f99aa32479f783b898f781eaf0a5 100644 --- a/runtime/base/arena_allocator_test.cc +++ b/libartbase/base/arena_allocator_test.cc @@ -14,10 +14,12 @@ * limitations under the License. */ -#include "base/arena_allocator-inl.h" -#include "base/arena_bit_vector.h" -#include "base/memory_tool.h" +#include "arena_allocator-inl.h" +#include "arena_bit_vector.h" +#include "base/common_art_test.h" #include "gtest/gtest.h" +#include "malloc_arena_pool.h" +#include "memory_tool.h" namespace art { @@ -33,7 +35,7 @@ class ArenaAllocatorTest : public testing::Test { }; TEST_F(ArenaAllocatorTest, Test) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); ArenaBitVector bv(&allocator, 10, true); bv.SetBit(5); @@ -44,7 +46,7 @@ TEST_F(ArenaAllocatorTest, Test) { TEST_F(ArenaAllocatorTest, MakeDefined) { // Regression test to make sure we mark the allocated area defined. - ArenaPool pool; + MallocArenaPool pool; static constexpr size_t kSmallArraySize = 10; static constexpr size_t kLargeArraySize = 50; uint32_t* small_array; @@ -71,7 +73,7 @@ TEST_F(ArenaAllocatorTest, LargeAllocations) { } { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); // Note: Leaving some space for memory tool red zones. void* alloc1 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 5 / 8); @@ -80,7 +82,7 @@ TEST_F(ArenaAllocatorTest, LargeAllocations) { ASSERT_EQ(1u, NumberOfArenas(&allocator)); } { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); void* alloc1 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 13 / 16); void* alloc2 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 11 / 16); @@ -92,7 +94,7 @@ TEST_F(ArenaAllocatorTest, LargeAllocations) { ASSERT_EQ(3u, NumberOfArenas(&allocator)); } { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); void* alloc1 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 13 / 16); void* alloc2 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 9 / 16); @@ -105,7 +107,7 @@ TEST_F(ArenaAllocatorTest, LargeAllocations) { ASSERT_EQ(2u, NumberOfArenas(&allocator)); } { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); void* alloc1 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 9 / 16); void* alloc2 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 13 / 16); @@ -118,7 +120,7 @@ TEST_F(ArenaAllocatorTest, LargeAllocations) { ASSERT_EQ(2u, NumberOfArenas(&allocator)); } { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); // Note: Leaving some space for memory tool red zones. for (size_t i = 0; i != 15; ++i) { @@ -133,7 +135,7 @@ TEST_F(ArenaAllocatorTest, LargeAllocations) { } TEST_F(ArenaAllocatorTest, AllocAlignment) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); for (size_t iterations = 0; iterations <= 10; ++iterations) { for (size_t size = 1; size <= ArenaAllocator::kAlignment + 1; ++size) { @@ -145,15 +147,12 @@ TEST_F(ArenaAllocatorTest, AllocAlignment) { } 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. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = ArenaAllocator::kAlignment * 2; @@ -166,7 +165,7 @@ TEST_F(ArenaAllocatorTest, ReallocReuse) { { // Case 2: small aligned allocation, non-aligned extend inside arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = ArenaAllocator::kAlignment * 2; @@ -179,7 +178,7 @@ TEST_F(ArenaAllocatorTest, ReallocReuse) { { // Case 3: small non-aligned allocation, aligned extend inside arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = ArenaAllocator::kAlignment * 2 + (ArenaAllocator::kAlignment / 2); @@ -192,7 +191,7 @@ TEST_F(ArenaAllocatorTest, ReallocReuse) { { // Case 4: small non-aligned allocation, aligned non-extend inside arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = ArenaAllocator::kAlignment * 2 + (ArenaAllocator::kAlignment / 2); @@ -208,7 +207,7 @@ TEST_F(ArenaAllocatorTest, ReallocReuse) { { // Case 5: large allocation, aligned extend into next arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = arena_allocator::kArenaDefaultSize - @@ -222,7 +221,7 @@ TEST_F(ArenaAllocatorTest, ReallocReuse) { { // Case 6: large allocation, non-aligned extend into next arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = arena_allocator::kArenaDefaultSize - @@ -241,7 +240,7 @@ TEST_F(ArenaAllocatorTest, ReallocReuse) { TEST_F(ArenaAllocatorTest, ReallocAlignment) { { // Case 1: small aligned allocation, aligned extend inside arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = ArenaAllocator::kAlignment * 2; @@ -258,7 +257,7 @@ TEST_F(ArenaAllocatorTest, ReallocAlignment) { { // Case 2: small aligned allocation, non-aligned extend inside arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = ArenaAllocator::kAlignment * 2; @@ -275,7 +274,7 @@ TEST_F(ArenaAllocatorTest, ReallocAlignment) { { // Case 3: small non-aligned allocation, aligned extend inside arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = ArenaAllocator::kAlignment * 2 + (ArenaAllocator::kAlignment / 2); @@ -292,7 +291,7 @@ TEST_F(ArenaAllocatorTest, ReallocAlignment) { { // Case 4: small non-aligned allocation, aligned non-extend inside arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = ArenaAllocator::kAlignment * 2 + (ArenaAllocator::kAlignment / 2); @@ -312,7 +311,7 @@ TEST_F(ArenaAllocatorTest, ReallocAlignment) { { // Case 5: large allocation, aligned extend into next arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = arena_allocator::kArenaDefaultSize - @@ -330,7 +329,7 @@ TEST_F(ArenaAllocatorTest, ReallocAlignment) { { // Case 6: large allocation, non-aligned extend into next arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = arena_allocator::kArenaDefaultSize - diff --git a/runtime/base/arena_bit_vector.cc b/libartbase/base/arena_bit_vector.cc similarity index 98% rename from runtime/base/arena_bit_vector.cc rename to libartbase/base/arena_bit_vector.cc index 1542e9d5f71f31c42c3f3c22573f0b406b16e1ec..01f9013737624d180422bbe8f6b5d0d0f87e0e68 100644 --- a/runtime/base/arena_bit_vector.cc +++ b/libartbase/base/arena_bit_vector.cc @@ -16,8 +16,8 @@ #include "arena_bit_vector.h" -#include "base/allocator.h" -#include "base/arena_allocator.h" +#include "allocator.h" +#include "arena_allocator.h" namespace art { diff --git a/runtime/base/arena_bit_vector.h b/libartbase/base/arena_bit_vector.h similarity index 89% rename from runtime/base/arena_bit_vector.h rename to libartbase/base/arena_bit_vector.h index ca1d5b1d668b99271ef0e49cb9299eea12e4fb08..0fb6bbf775d38192bb40aadd5a67ab6608cf1f4d 100644 --- a/runtime/base/arena_bit_vector.h +++ b/libartbase/base/arena_bit_vector.h @@ -14,11 +14,11 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_ARENA_BIT_VECTOR_H_ -#define ART_RUNTIME_BASE_ARENA_BIT_VECTOR_H_ +#ifndef ART_LIBARTBASE_BASE_ARENA_BIT_VECTOR_H_ +#define ART_LIBARTBASE_BASE_ARENA_BIT_VECTOR_H_ -#include "base/arena_object.h" -#include "base/bit_vector.h" +#include "arena_object.h" +#include "bit_vector.h" namespace art { @@ -55,4 +55,4 @@ class ArenaBitVector : public BitVector, public ArenaObject #include @@ -25,10 +25,10 @@ #include #include "arena_allocator.h" -#include "base/dchecked_vector.h" -#include "base/hash_map.h" -#include "base/hash_set.h" -#include "base/safe_map.h" +#include "dchecked_vector.h" +#include "hash_map.h" +#include "hash_set.h" +#include "safe_map.h" namespace art { @@ -70,15 +70,15 @@ using ArenaSafeMap = template , - typename HashFn = std::hash, - typename Pred = std::equal_to> + typename HashFn = DefaultHashFn, + typename Pred = DefaultPred> using ArenaHashSet = HashSet>; template >, - typename HashFn = std::hash, - typename Pred = std::equal_to> + typename HashFn = DefaultHashFn, + typename Pred = DefaultPred> using ArenaHashMap = HashMap ArenaAllocator::Adapter(ArenaAllocKind kind) } // namespace art -#endif // ART_RUNTIME_BASE_ARENA_CONTAINERS_H_ +#endif // ART_LIBARTBASE_BASE_ARENA_CONTAINERS_H_ diff --git a/runtime/base/arena_object.h b/libartbase/base/arena_object.h similarity index 93% rename from runtime/base/arena_object.h rename to libartbase/base/arena_object.h index d01e346f7ad20845067917d920af292cf29ac59f..ed0922546d7851233ce16286e67e6dc4845ac61a 100644 --- a/runtime/base/arena_object.h +++ b/libartbase/base/arena_object.h @@ -14,13 +14,13 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_ARENA_OBJECT_H_ -#define ART_RUNTIME_BASE_ARENA_OBJECT_H_ +#ifndef ART_LIBARTBASE_BASE_ARENA_OBJECT_H_ +#define ART_LIBARTBASE_BASE_ARENA_OBJECT_H_ #include #include "arena_allocator.h" -#include "base/macros.h" +#include "macros.h" #include "scoped_arena_allocator.h" namespace art { @@ -69,4 +69,4 @@ class DeletableArenaObject { } // namespace art -#endif // ART_RUNTIME_BASE_ARENA_OBJECT_H_ +#endif // ART_LIBARTBASE_BASE_ARENA_OBJECT_H_ diff --git a/libartbase/base/array_slice.h b/libartbase/base/array_slice.h index 1ef2fbad8b0a5d70b735494736e146fe3799093c..fb3da6b476242582cc1e4940435d9c4284626a0f 100644 --- a/libartbase/base/array_slice.h +++ b/libartbase/base/array_slice.h @@ -17,9 +17,9 @@ #ifndef ART_LIBARTBASE_BASE_ARRAY_SLICE_H_ #define ART_LIBARTBASE_BASE_ARRAY_SLICE_H_ -#include "base/bit_utils.h" -#include "base/casts.h" -#include "base/iteration_range.h" +#include "bit_utils.h" +#include "casts.h" +#include "iteration_range.h" #include "stride_iterator.h" namespace art { diff --git a/libartbase/base/atomic.h b/libartbase/base/atomic.h index fd34cc614333ab68abd48804d43c8ae7668a1e7e..9de84cdd20bcf972f81bde24f3e8daa65810b491 100644 --- a/libartbase/base/atomic.h +++ b/libartbase/base/atomic.h @@ -24,10 +24,15 @@ #include -#include "base/macros.h" +#include "macros.h" namespace art { +enum class CASMode { + kStrong, + kWeak, +}; + template class PACKED(sizeof(T)) Atomic : public std::atomic { public: @@ -35,94 +40,28 @@ class PACKED(sizeof(T)) Atomic : public std::atomic { explicit Atomic(T value) : std::atomic(value) { } - // Load from memory without ordering or synchronization constraints. - T LoadRelaxed() const { - return this->load(std::memory_order_relaxed); - } - - // Load from memory with acquire ordering. - T LoadAcquire() const { - return this->load(std::memory_order_acquire); - } - - // Word tearing allowed, but may race. - // TODO: Optimize? - // There has been some discussion of eventually disallowing word - // tearing for Java data loads. + // Load data from an atomic variable with Java data memory order semantics. + // + // Promises memory access semantics of ordinary Java data. + // Does not order other memory accesses. + // Long and double accesses may be performed 32 bits at a time. + // There are no "cache coherence" guarantees; e.g. loads from the same location may be reordered. + // In contrast to normal C++ accesses, racing accesses are allowed. T LoadJavaData() const { return this->load(std::memory_order_relaxed); } - // Load from memory with a total ordering. - // Corresponds exactly to a Java volatile load. - T LoadSequentiallyConsistent() const { - return this->load(std::memory_order_seq_cst); - } - - // Store to memory without ordering or synchronization constraints. - void StoreRelaxed(T desired_value) { - this->store(desired_value, std::memory_order_relaxed); - } - - // Word tearing allowed, but may race. + // Store data in an atomic variable with Java data memory ordering semantics. + // + // Promises memory access semantics of ordinary Java data. + // Does not order other memory accesses. + // Long and double accesses may be performed 32 bits at a time. + // There are no "cache coherence" guarantees; e.g. loads from the same location may be reordered. + // In contrast to normal C++ accesses, racing accesses are allowed. void StoreJavaData(T desired_value) { this->store(desired_value, std::memory_order_relaxed); } - // Store to memory with release ordering. - void StoreRelease(T desired_value) { - this->store(desired_value, std::memory_order_release); - } - - // Store to memory with a total ordering. - void StoreSequentiallyConsistent(T desired_value) { - this->store(desired_value, std::memory_order_seq_cst); - } - - // Atomically replace the value with desired_value. - T ExchangeRelaxed(T desired_value) { - return this->exchange(desired_value, std::memory_order_relaxed); - } - - // Atomically replace the value with desired_value. - T ExchangeSequentiallyConsistent(T desired_value) { - return this->exchange(desired_value, std::memory_order_seq_cst); - } - - // Atomically replace the value with desired_value. - T ExchangeAcquire(T desired_value) { - return this->exchange(desired_value, std::memory_order_acquire); - } - - // Atomically replace the value with desired_value. - T ExchangeRelease(T desired_value) { - return this->exchange(desired_value, std::memory_order_release); - } - - // Atomically replace the value with desired_value if it matches the expected_value. - // Participates in total ordering of atomic operations. Returns true on success, false otherwise. - // If the value does not match, updates the expected_value argument with the value that was - // atomically read for the failed comparison. - bool CompareAndExchangeStrongSequentiallyConsistent(T* expected_value, T desired_value) { - return this->compare_exchange_strong(*expected_value, desired_value, std::memory_order_seq_cst); - } - - // Atomically replace the value with desired_value if it matches the expected_value. - // Participates in total ordering of atomic operations. Returns true on success, false otherwise. - // If the value does not match, updates the expected_value argument with the value that was - // atomically read for the failed comparison. - bool CompareAndExchangeStrongAcquire(T* expected_value, T desired_value) { - return this->compare_exchange_strong(*expected_value, desired_value, std::memory_order_acquire); - } - - // Atomically replace the value with desired_value if it matches the expected_value. - // Participates in total ordering of atomic operations. Returns true on success, false otherwise. - // If the value does not match, updates the expected_value argument with the value that was - // atomically read for the failed comparison. - bool CompareAndExchangeStrongRelease(T* expected_value, T desired_value) { - return this->compare_exchange_strong(*expected_value, desired_value, std::memory_order_release); - } - // Atomically replace the value with desired_value if it matches the expected_value. // Participates in total ordering of atomic operations. bool CompareAndSetStrongSequentiallyConsistent(T expected_value, T desired_value) { @@ -166,66 +105,17 @@ class PACKED(sizeof(T)) Atomic : public std::atomic { return this->compare_exchange_weak(expected_value, desired_value, std::memory_order_release); } - T FetchAndAddSequentiallyConsistent(const T value) { - return this->fetch_add(value, std::memory_order_seq_cst); // Return old_value. - } - - T FetchAndAddRelaxed(const T value) { - return this->fetch_add(value, std::memory_order_relaxed); // Return old_value. - } - - T FetchAndAddAcquire(const T value) { - return this->fetch_add(value, std::memory_order_acquire); // Return old_value. - } - - T FetchAndAddRelease(const T value) { - return this->fetch_add(value, std::memory_order_acquire); // Return old_value. - } - - T FetchAndSubSequentiallyConsistent(const T value) { - return this->fetch_sub(value, std::memory_order_seq_cst); // Return old value. - } - - T FetchAndSubRelaxed(const T value) { - return this->fetch_sub(value, std::memory_order_relaxed); // Return old value. - } - - T FetchAndBitwiseAndSequentiallyConsistent(const T value) { - return this->fetch_and(value, std::memory_order_seq_cst); // Return old_value. - } - - T FetchAndBitwiseAndAcquire(const T value) { - return this->fetch_and(value, std::memory_order_acquire); // Return old_value. - } - - T FetchAndBitwiseAndRelease(const T value) { - return this->fetch_and(value, std::memory_order_release); // Return old_value. - } - - T FetchAndBitwiseOrSequentiallyConsistent(const T value) { - return this->fetch_or(value, std::memory_order_seq_cst); // Return old_value. - } - - T FetchAndBitwiseOrAcquire(const T value) { - return this->fetch_or(value, std::memory_order_acquire); // Return old_value. - } - - T FetchAndBitwiseOrRelease(const T value) { - return this->fetch_or(value, std::memory_order_release); // Return old_value. - } - - T FetchAndBitwiseXorSequentiallyConsistent(const T value) { - return this->fetch_xor(value, std::memory_order_seq_cst); // Return old_value. - } - - T FetchAndBitwiseXorAcquire(const T value) { - return this->fetch_xor(value, std::memory_order_acquire); // Return old_value. - } - - T FetchAndBitwiseXorRelease(const T value) { - return this->fetch_xor(value, std::memory_order_release); // Return old_value. + bool CompareAndSet(T expected_value, + T desired_value, + CASMode mode, + std::memory_order memory_order) { + return mode == CASMode::kStrong + ? this->compare_exchange_strong(expected_value, desired_value, memory_order) + : this->compare_exchange_weak(expected_value, desired_value, memory_order); } + // Returns the address of the current atomic variable. This is only used by futex() which is + // declared to take a volatile address (see base/mutex-inl.h). volatile T* Address() { return reinterpret_cast(this); } diff --git a/libartbase/base/bit_memory_region.h b/libartbase/base/bit_memory_region.h new file mode 100644 index 0000000000000000000000000000000000000000..07c1611c60c1057d260e75cc430f10652c84c15d --- /dev/null +++ b/libartbase/base/bit_memory_region.h @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_LIBARTBASE_BASE_BIT_MEMORY_REGION_H_ +#define ART_LIBARTBASE_BASE_BIT_MEMORY_REGION_H_ + +#include "memory_region.h" + +#include "bit_utils.h" +#include "memory_tool.h" + +namespace art { + +// Bit memory region is a bit offset subregion of a normal memoryregion. This is useful for +// abstracting away the bit start offset to avoid needing passing as an argument everywhere. +class BitMemoryRegion FINAL : public ValueObject { + public: + BitMemoryRegion() = default; + ALWAYS_INLINE explicit BitMemoryRegion(MemoryRegion region) + : data_(reinterpret_cast(AlignDown(region.pointer(), sizeof(uintptr_t)))), + bit_start_(8 * (reinterpret_cast(region.pointer()) % sizeof(uintptr_t))), + bit_size_(region.size_in_bits()) { + } + ALWAYS_INLINE BitMemoryRegion(MemoryRegion region, size_t bit_offset, size_t bit_length) + : BitMemoryRegion(region) { + DCHECK_LE(bit_offset, bit_size_); + DCHECK_LE(bit_length, bit_size_ - bit_offset); + bit_start_ += bit_offset; + bit_size_ = bit_length; + } + + ALWAYS_INLINE bool IsValid() const { return data_ != nullptr; } + + size_t size_in_bits() const { + return bit_size_; + } + + ALWAYS_INLINE BitMemoryRegion Subregion(size_t bit_offset, size_t bit_length) const { + DCHECK_LE(bit_offset, bit_size_); + DCHECK_LE(bit_length, bit_size_ - bit_offset); + BitMemoryRegion result = *this; + result.bit_start_ += bit_offset; + result.bit_size_ = bit_length; + return result; + } + + // Increase the size of the region and return the newly added range (starting at the old end). + ALWAYS_INLINE BitMemoryRegion Extend(size_t bit_length) { + BitMemoryRegion result = *this; + result.bit_start_ += result.bit_size_; + result.bit_size_ = bit_length; + bit_size_ += bit_length; + return result; + } + + // Load a single bit in the region. The bit at offset 0 is the least + // significant bit in the first byte. + ATTRIBUTE_NO_SANITIZE_ADDRESS // We might touch extra bytes due to the alignment. + ALWAYS_INLINE bool LoadBit(uintptr_t bit_offset) const { + DCHECK_LT(bit_offset, bit_size_); + size_t index = (bit_start_ + bit_offset) / kBitsPerIntPtrT; + size_t shift = (bit_start_ + bit_offset) % kBitsPerIntPtrT; + return ((data_[index] >> shift) & 1) != 0; + } + + ALWAYS_INLINE void StoreBit(uintptr_t bit_offset, bool value) { + DCHECK_LT(bit_offset, bit_size_); + uint8_t* data = reinterpret_cast(data_); + size_t index = (bit_start_ + bit_offset) / kBitsPerByte; + size_t shift = (bit_start_ + bit_offset) % kBitsPerByte; + data[index] &= ~(1 << shift); // Clear bit. + data[index] |= (value ? 1 : 0) << shift; // Set bit. + DCHECK_EQ(value, LoadBit(bit_offset)); + } + + // Load `bit_length` bits from `data` starting at given `bit_offset`. + // The least significant bit is stored in the smallest memory offset. + ATTRIBUTE_NO_SANITIZE_ADDRESS // We might touch extra bytes due to the alignment. + ALWAYS_INLINE uint32_t LoadBits(size_t bit_offset, size_t bit_length) const { + DCHECK(IsAligned(data_)); + DCHECK_LE(bit_offset, bit_size_); + DCHECK_LE(bit_length, bit_size_ - bit_offset); + DCHECK_LE(bit_length, BitSizeOf()); + if (bit_length == 0) { + return 0; + } + uintptr_t mask = std::numeric_limits::max() >> (kBitsPerIntPtrT - bit_length); + size_t index = (bit_start_ + bit_offset) / kBitsPerIntPtrT; + size_t shift = (bit_start_ + bit_offset) % kBitsPerIntPtrT; + uintptr_t value = data_[index] >> shift; + size_t finished_bits = kBitsPerIntPtrT - shift; + if (finished_bits < bit_length) { + value |= data_[index + 1] << finished_bits; + } + return value & mask; + } + + // Store `bit_length` bits in `data` starting at given `bit_offset`. + // The least significant bit is stored in the smallest memory offset. + ALWAYS_INLINE void StoreBits(size_t bit_offset, uint32_t value, size_t bit_length) { + DCHECK_LE(bit_offset, bit_size_); + DCHECK_LE(bit_length, bit_size_ - bit_offset); + DCHECK_LE(bit_length, BitSizeOf()); + DCHECK_LE(value, MaxInt(bit_length)); + if (bit_length == 0) { + return; + } + // Write data byte by byte to avoid races with other threads + // on bytes that do not overlap with this region. + uint8_t* data = reinterpret_cast(data_); + uint32_t mask = std::numeric_limits::max() >> (BitSizeOf() - bit_length); + size_t index = (bit_start_ + bit_offset) / kBitsPerByte; + size_t shift = (bit_start_ + bit_offset) % kBitsPerByte; + data[index] &= ~(mask << shift); // Clear bits. + data[index] |= (value << shift); // Set bits. + size_t finished_bits = kBitsPerByte - shift; + for (int i = 1; finished_bits < bit_length; i++, finished_bits += kBitsPerByte) { + data[index + i] &= ~(mask >> finished_bits); // Clear bits. + data[index + i] |= (value >> finished_bits); // Set bits. + } + DCHECK_EQ(value, LoadBits(bit_offset, bit_length)); + } + + // Store bits from other bit region. + ALWAYS_INLINE void StoreBits(size_t bit_offset, const BitMemoryRegion& src, size_t bit_length) { + DCHECK_LE(bit_offset, bit_size_); + DCHECK_LE(bit_length, bit_size_ - bit_offset); + size_t bit = 0; + constexpr size_t kNumBits = BitSizeOf(); + for (; bit + kNumBits <= bit_length; bit += kNumBits) { + StoreBits(bit_offset + bit, src.LoadBits(bit, kNumBits), kNumBits); + } + size_t num_bits = bit_length - bit; + StoreBits(bit_offset + bit, src.LoadBits(bit, num_bits), num_bits); + } + + // Count the number of set bits within the given bit range. + ALWAYS_INLINE size_t PopCount(size_t bit_offset, size_t bit_length) const { + DCHECK_LE(bit_offset, bit_size_); + DCHECK_LE(bit_length, bit_size_ - bit_offset); + size_t count = 0; + size_t bit = 0; + constexpr size_t kNumBits = BitSizeOf(); + for (; bit + kNumBits <= bit_length; bit += kNumBits) { + count += POPCOUNT(LoadBits(bit_offset + bit, kNumBits)); + } + count += POPCOUNT(LoadBits(bit_offset + bit, bit_length - bit)); + return count; + } + + ALWAYS_INLINE bool Equals(const BitMemoryRegion& other) const { + return data_ == other.data_ && + bit_start_ == other.bit_start_ && + bit_size_ == other.bit_size_; + } + + private: + // The data pointer must be naturally aligned. This makes loading code faster. + uintptr_t* data_ = nullptr; + size_t bit_start_ = 0; + size_t bit_size_ = 0; +}; + +class BitMemoryReader { + public: + explicit BitMemoryReader(const uint8_t* data, size_t bit_offset = 0) { + MemoryRegion region(const_cast(data), BitsToBytesRoundUp(bit_offset)); + finished_region_ = BitMemoryRegion(region, 0, bit_offset); + DCHECK_EQ(GetBitOffset(), bit_offset); + } + + size_t GetBitOffset() const { return finished_region_.size_in_bits(); } + + ALWAYS_INLINE BitMemoryRegion Skip(size_t bit_length) { + return finished_region_.Extend(bit_length); + } + + ALWAYS_INLINE uint32_t ReadBits(size_t bit_length) { + return finished_region_.Extend(bit_length).LoadBits(0, bit_length); + } + + private: + // Represents all of the bits which were read so far. There is no upper bound. + // Therefore, by definition, the "cursor" is always at the end of the region. + BitMemoryRegion finished_region_; + + DISALLOW_COPY_AND_ASSIGN(BitMemoryReader); +}; + +template +class BitMemoryWriter { + public: + explicit BitMemoryWriter(Vector* out, size_t bit_offset = 0) + : out_(out), bit_offset_(bit_offset) { + DCHECK_EQ(GetBitOffset(), bit_offset); + } + + const uint8_t* data() const { return out_->data(); } + + size_t GetBitOffset() const { return bit_offset_; } + + ALWAYS_INLINE BitMemoryRegion Allocate(size_t bit_length) { + out_->resize(BitsToBytesRoundUp(bit_offset_ + bit_length)); + BitMemoryRegion region(MemoryRegion(out_->data(), out_->size()), bit_offset_, bit_length); + bit_offset_ += bit_length; + return region; + } + + ALWAYS_INLINE void WriteBits(uint32_t value, size_t bit_length) { + Allocate(bit_length).StoreBits(0, value, bit_length); + } + + private: + Vector* out_; + size_t bit_offset_; + + DISALLOW_COPY_AND_ASSIGN(BitMemoryWriter); +}; + +} // namespace art + +#endif // ART_LIBARTBASE_BASE_BIT_MEMORY_REGION_H_ diff --git a/libartbase/base/bit_memory_region_test.cc b/libartbase/base/bit_memory_region_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..b7546985a9f6b9016da6f0aff57da7b2f815e567 --- /dev/null +++ b/libartbase/base/bit_memory_region_test.cc @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "bit_memory_region.h" + +#include "gtest/gtest.h" + +namespace art { + +static void CheckBits(uint8_t* data, + size_t size, + uint32_t init, + size_t offset, + size_t length, + uint32_t value) { + for (size_t i = 0; i < size * kBitsPerByte; i++) { + uint8_t expected = (offset <= i && i < offset + length) ? value >> (i - offset) : init; + uint8_t actual = data[i / kBitsPerByte] >> (i % kBitsPerByte); + EXPECT_EQ(expected & 1, actual & 1); + } +} + +TEST(BitMemoryRegion, TestBit) { + uint8_t data[sizeof(uint32_t) * 2]; + for (size_t bit_offset = 0; bit_offset < 2 * sizeof(uint32_t) * kBitsPerByte; ++bit_offset) { + for (uint32_t initial_value = 0; initial_value <= 1; initial_value++) { + for (uint32_t value = 0; value <= 1; value++) { + // Check Store and Load with bit_offset set on the region. + std::fill_n(data, sizeof(data), initial_value * 0xFF); + BitMemoryRegion bmr1(MemoryRegion(&data, sizeof(data)), bit_offset, 1); + bmr1.StoreBit(0, value); + EXPECT_EQ(bmr1.LoadBit(0), value); + CheckBits(data, sizeof(data), initial_value, bit_offset, 1, value); + // Check Store and Load with bit_offset set on the methods. + std::fill_n(data, sizeof(data), initial_value * 0xFF); + BitMemoryRegion bmr2(MemoryRegion(&data, sizeof(data))); + bmr2.StoreBit(bit_offset, value); + EXPECT_EQ(bmr2.LoadBit(bit_offset), value); + CheckBits(data, sizeof(data), initial_value, bit_offset, 1, value); + } + } + } +} + +TEST(BitMemoryRegion, TestBits) { + uint8_t data[sizeof(uint32_t) * 4]; + for (size_t bit_offset = 0; bit_offset < 3 * sizeof(uint32_t) * kBitsPerByte; ++bit_offset) { + uint32_t mask = 0; + for (size_t bit_length = 0; bit_length < sizeof(uint32_t) * kBitsPerByte; ++bit_length) { + const uint32_t value = 0xDEADBEEF & mask; + for (uint32_t initial_value = 0; initial_value <= 1; initial_value++) { + // Check Store and Load with bit_offset set on the region. + std::fill_n(data, sizeof(data), initial_value * 0xFF); + BitMemoryRegion bmr1(MemoryRegion(&data, sizeof(data)), bit_offset, bit_length); + bmr1.StoreBits(0, value, bit_length); + EXPECT_EQ(bmr1.LoadBits(0, bit_length), value); + CheckBits(data, sizeof(data), initial_value, bit_offset, bit_length, value); + // Check Store and Load with bit_offset set on the methods. + std::fill_n(data, sizeof(data), initial_value * 0xFF); + BitMemoryRegion bmr2(MemoryRegion(&data, sizeof(data))); + bmr2.StoreBits(bit_offset, value, bit_length); + EXPECT_EQ(bmr2.LoadBits(bit_offset, bit_length), value); + CheckBits(data, sizeof(data), initial_value, bit_offset, bit_length, value); + } + mask = (mask << 1) | 1; + } + } +} + +} // namespace art diff --git a/libartbase/base/bit_string.h b/libartbase/base/bit_string.h index 0e051f358b7d26fb871b0eefbab8ea291271f6ba..d995f8dbb1fc3fe709d718e0928a1420c8ad2c32 100644 --- a/libartbase/base/bit_string.h +++ b/libartbase/base/bit_string.h @@ -17,8 +17,8 @@ #ifndef ART_LIBARTBASE_BASE_BIT_STRING_H_ #define ART_LIBARTBASE_BASE_BIT_STRING_H_ -#include "base/bit_struct.h" -#include "base/bit_utils.h" +#include "bit_struct.h" +#include "bit_utils.h" #include diff --git a/libartbase/base/bit_string_test.cc b/libartbase/base/bit_string_test.cc index 23274e3f2f03d81c4cab2fb399946ca271c87f18..89a71a18949ddb35e1ed3e2035e0dc13fd6c8eb2 100644 --- a/libartbase/base/bit_string_test.cc +++ b/libartbase/base/bit_string_test.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "base/bit_string.h" +#include "bit_string.h" #include "gtest/gtest.h" #include "android-base/logging.h" diff --git a/libartbase/base/bit_struct.h b/libartbase/base/bit_struct.h index 386b8960730facb12bf38960b539ba997462a93b..9814fd4f3a29411a53c24c1f8dbedf2843cd08d6 100644 --- a/libartbase/base/bit_struct.h +++ b/libartbase/base/bit_struct.h @@ -17,8 +17,8 @@ #ifndef ART_LIBARTBASE_BASE_BIT_STRUCT_H_ #define ART_LIBARTBASE_BASE_BIT_STRUCT_H_ -#include "base/bit_utils.h" #include "bit_struct_detail.h" +#include "bit_utils.h" // // Zero-cost, type-safe, well-defined "structs" of bit fields. diff --git a/libartbase/base/bit_struct_detail.h b/libartbase/base/bit_struct_detail.h index facfa61efb579d43692af021df0c0d151b4ad26e..68c2e4461f401ab56ed39d12d7e019691952772a 100644 --- a/libartbase/base/bit_struct_detail.h +++ b/libartbase/base/bit_struct_detail.h @@ -17,7 +17,7 @@ #ifndef ART_LIBARTBASE_BASE_BIT_STRUCT_DETAIL_H_ #define ART_LIBARTBASE_BASE_BIT_STRUCT_DETAIL_H_ -#include "base/bit_utils.h" +#include "bit_utils.h" #include "globals.h" #include diff --git a/libartbase/base/bit_table.h b/libartbase/base/bit_table.h new file mode 100644 index 0000000000000000000000000000000000000000..ee477215e7d8b915a9d9d925b0da9788285b8f99 --- /dev/null +++ b/libartbase/base/bit_table.h @@ -0,0 +1,511 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_LIBARTBASE_BASE_BIT_TABLE_H_ +#define ART_LIBARTBASE_BASE_BIT_TABLE_H_ + +#include +#include +#include +#include +#include +#include + +#include "base/bit_memory_region.h" +#include "base/casts.h" +#include "base/iteration_range.h" +#include "base/memory_region.h" +#include "base/scoped_arena_containers.h" +#include "base/stl_util.h" + +namespace art { + +constexpr uint32_t kVarintHeaderBits = 4; +constexpr uint32_t kVarintSmallValue = 11; // Maximum value which is stored as-is. + +// Load variable-length bit-packed integer from `data` starting at `bit_offset`. +// The first four bits determine the variable length of the encoded integer: +// Values 0..11 represent the result as-is, with no further following bits. +// Values 12..15 mean the result is in the next 8/16/24/32-bits respectively. +ALWAYS_INLINE static inline uint32_t DecodeVarintBits(BitMemoryReader& reader) { + uint32_t x = reader.ReadBits(kVarintHeaderBits); + if (x > kVarintSmallValue) { + x = reader.ReadBits((x - kVarintSmallValue) * kBitsPerByte); + } + return x; +} + +// Store variable-length bit-packed integer from `data` starting at `bit_offset`. +template +ALWAYS_INLINE static inline void EncodeVarintBits(BitMemoryWriter& out, uint32_t value) { + if (value <= kVarintSmallValue) { + out.WriteBits(value, kVarintHeaderBits); + } else { + uint32_t num_bits = RoundUp(MinimumBitsToStore(value), kBitsPerByte); + uint32_t header = kVarintSmallValue + num_bits / kBitsPerByte; + out.WriteBits(header, kVarintHeaderBits); + out.WriteBits(value, num_bits); + } +} + +// Generic purpose table of uint32_t values, which are tightly packed at bit level. +// It has its own header with the number of rows and the bit-widths of all columns. +// The values are accessible by (row, column). The value -1 is stored efficiently. +template +class BitTableBase { + public: + static constexpr uint32_t kNoValue = std::numeric_limits::max(); // == -1. + static constexpr uint32_t kValueBias = kNoValue; // Bias so that -1 is encoded as 0. + + BitTableBase() {} + explicit BitTableBase(BitMemoryReader& reader) { + Decode(reader); + } + + ALWAYS_INLINE void Decode(BitMemoryReader& reader) { + // Decode row count and column sizes from the table header. + size_t initial_bit_offset = reader.GetBitOffset(); + num_rows_ = DecodeVarintBits(reader); + if (num_rows_ != 0) { + column_offset_[0] = 0; + for (uint32_t i = 0; i < kNumColumns; i++) { + size_t column_end = column_offset_[i] + DecodeVarintBits(reader); + column_offset_[i + 1] = dchecked_integral_cast(column_end); + } + } + header_bit_size_ = reader.GetBitOffset() - initial_bit_offset; + + // Record the region which contains the table data and skip past it. + table_data_ = reader.Skip(num_rows_ * NumRowBits()); + } + + ALWAYS_INLINE uint32_t Get(uint32_t row, uint32_t column = 0) const { + DCHECK_LT(row, num_rows_); + DCHECK_LT(column, kNumColumns); + size_t offset = row * NumRowBits() + column_offset_[column]; + return table_data_.LoadBits(offset, NumColumnBits(column)) + kValueBias; + } + + ALWAYS_INLINE BitMemoryRegion GetBitMemoryRegion(uint32_t row, uint32_t column = 0) const { + DCHECK_LT(row, num_rows_); + DCHECK_LT(column, kNumColumns); + size_t offset = row * NumRowBits() + column_offset_[column]; + return table_data_.Subregion(offset, NumColumnBits(column)); + } + + size_t NumRows() const { return num_rows_; } + + uint32_t NumRowBits() const { return column_offset_[kNumColumns]; } + + constexpr size_t NumColumns() const { return kNumColumns; } + + uint32_t NumColumnBits(uint32_t column) const { + return column_offset_[column + 1] - column_offset_[column]; + } + + size_t HeaderBitSize() const { return header_bit_size_; } + + size_t BitSize() const { return header_bit_size_ + table_data_.size_in_bits(); } + + protected: + BitMemoryRegion table_data_; + size_t num_rows_ = 0; + + uint16_t column_offset_[kNumColumns + 1] = {}; + uint16_t header_bit_size_ = 0; +}; + +// Helper class which can be used to create BitTable accessors with named getters. +template +class BitTableAccessor { + public: + static constexpr uint32_t kNumColumns = NumColumns; + static constexpr uint32_t kNoValue = BitTableBase::kNoValue; + + BitTableAccessor(const BitTableBase* table, uint32_t row) + : table_(table), row_(row) { + DCHECK(table_ != nullptr); + } + + ALWAYS_INLINE uint32_t Row() const { return row_; } + + ALWAYS_INLINE bool IsValid() const { return row_ < table_->NumRows(); } + + ALWAYS_INLINE bool Equals(const BitTableAccessor& other) { + return this->table_ == other.table_ && this->row_ == other.row_; + } + +// Helper macro to create constructors and per-table utilities in derived class. +#define BIT_TABLE_HEADER() \ + using BitTableAccessor::BitTableAccessor; /* inherit constructors */ \ + template struct ColumnName; \ + +// Helper macro to create named column accessors in derived class. +#define BIT_TABLE_COLUMN(COLUMN, NAME) \ + static constexpr uint32_t k##NAME = COLUMN; \ + ALWAYS_INLINE uint32_t Get##NAME() const { return table_->Get(row_, COLUMN); } \ + ALWAYS_INLINE bool Has##NAME() const { return Get##NAME() != kNoValue; } \ + template struct ColumnName { \ + static constexpr const char* Value = #NAME; \ + }; \ + + protected: + const BitTableBase* table_ = nullptr; + uint32_t row_ = -1; +}; + +// Template meta-programming helper. +template +static const char* const* GetBitTableColumnNamesImpl(std::index_sequence) { + static const char* names[] = { Accessor::template ColumnName::Value... }; + return names; +} + +// Returns the names of all columns in the given accessor. +template +static const char* const* GetBitTableColumnNames() { + return GetBitTableColumnNamesImpl(std::make_index_sequence()); +} + +// Wrapper which makes it easier to use named accessors for the individual rows. +template +class BitTable : public BitTableBase { + public: + class const_iterator : public std::iterator { + public: + using difference_type = int32_t; + const_iterator() {} + const_iterator(const BitTable* table, uint32_t row) : table_(table), row_(row) {} + const_iterator operator+(difference_type n) { return const_iterator(table_, row_ + n); } + const_iterator operator-(difference_type n) { return const_iterator(table_, row_ - n); } + difference_type operator-(const const_iterator& other) { return row_ - other.row_; } + void operator+=(difference_type rows) { row_ += rows; } + void operator-=(difference_type rows) { row_ -= rows; } + const_iterator operator++() { return const_iterator(table_, ++row_); } + const_iterator operator--() { return const_iterator(table_, --row_); } + const_iterator operator++(int) { return const_iterator(table_, row_++); } + const_iterator operator--(int) { return const_iterator(table_, row_--); } + bool operator==(const_iterator i) const { DCHECK(table_ == i.table_); return row_ == i.row_; } + bool operator!=(const_iterator i) const { DCHECK(table_ == i.table_); return row_ != i.row_; } + bool operator<=(const_iterator i) const { DCHECK(table_ == i.table_); return row_ <= i.row_; } + bool operator>=(const_iterator i) const { DCHECK(table_ == i.table_); return row_ >= i.row_; } + bool operator<(const_iterator i) const { DCHECK(table_ == i.table_); return row_ < i.row_; } + bool operator>(const_iterator i) const { DCHECK(table_ == i.table_); return row_ > i.row_; } + Accessor operator*() { + DCHECK_LT(row_, table_->NumRows()); + return Accessor(table_, row_); + } + Accessor operator->() { + DCHECK_LT(row_, table_->NumRows()); + return Accessor(table_, row_); + } + Accessor operator[](size_t index) { + DCHECK_LT(row_ + index, table_->NumRows()); + return Accessor(table_, row_ + index); + } + private: + const BitTable* table_ = nullptr; + uint32_t row_ = 0; + }; + + using BitTableBase::BitTableBase; // Constructors. + + ALWAYS_INLINE const_iterator begin() const { return const_iterator(this, 0); } + ALWAYS_INLINE const_iterator end() const { return const_iterator(this, this->NumRows()); } + + ALWAYS_INLINE Accessor GetRow(uint32_t row) const { + return Accessor(this, row); + } + + ALWAYS_INLINE Accessor GetInvalidRow() const { + return Accessor(this, static_cast(-1)); + } +}; + +template +typename BitTable::const_iterator operator+( + typename BitTable::const_iterator::difference_type n, + typename BitTable::const_iterator a) { + return a + n; +} + +template +class BitTableRange : public IterationRange::const_iterator> { + public: + typedef typename BitTable::const_iterator const_iterator; + + using IterationRange::IterationRange; + BitTableRange() : IterationRange(const_iterator(), const_iterator()) { } + + bool empty() const { return this->begin() == this->end(); } + size_t size() const { return this->end() - this->begin(); } + + Accessor operator[](size_t index) const { + const_iterator it = this->begin() + index; + DCHECK(it < this->end()); + return *it; + } + + Accessor back() const { + DCHECK(!empty()); + return *(this->end() - 1); + } + + void pop_back() { + DCHECK(!empty()); + --this->last_; + } +}; + +// Helper class for encoding BitTable. It can optionally de-duplicate the inputs. +template +class BitTableBuilderBase { + public: + static constexpr uint32_t kNoValue = BitTableBase::kNoValue; + static constexpr uint32_t kValueBias = BitTableBase::kValueBias; + + class Entry { + public: + Entry() { + // The definition of kNoValue here is for host and target debug builds which complain about + // missing a symbol definition for BitTableBase::kNovValue when optimization is off. + static constexpr uint32_t kNoValue = BitTableBase::kNoValue; + std::fill_n(data_, kNumColumns, kNoValue); + } + + Entry(std::initializer_list values) { + DCHECK_EQ(values.size(), kNumColumns); + std::copy(values.begin(), values.end(), data_); + } + + uint32_t& operator[](size_t column) { + DCHECK_LT(column, kNumColumns); + return data_[column]; + } + + uint32_t operator[](size_t column) const { + DCHECK_LT(column, kNumColumns); + return data_[column]; + } + + private: + uint32_t data_[kNumColumns]; + }; + + explicit BitTableBuilderBase(ScopedArenaAllocator* allocator) + : rows_(allocator->Adapter(kArenaAllocBitTableBuilder)), + dedup_(8, allocator->Adapter(kArenaAllocBitTableBuilder)) { + } + + Entry& operator[](size_t row) { return rows_[row]; } + const Entry& operator[](size_t row) const { return rows_[row]; } + const Entry& back() const { return rows_.back(); } + size_t size() const { return rows_.size(); } + + // Append given value to the vector without de-duplication. + // This will not add the element to the dedup map to avoid its associated costs. + void Add(Entry value) { + rows_.push_back(value); + } + + // Append given list of values and return the index of the first value. + // If the exact same set of values was already added, return the old index. + uint32_t Dedup(Entry* values, size_t count = 1) { + FNVHash hasher; + uint32_t hash = hasher(MemoryRegion(values, sizeof(Entry) * count)); + + // Check if we have already added identical set of values. + auto range = dedup_.equal_range(hash); + for (auto it = range.first; it != range.second; ++it) { + uint32_t index = it->second; + if (count <= size() - index && + std::equal(values, + values + count, + rows_.begin() + index, + [](const Entry& lhs, const Entry& rhs) { + return memcmp(&lhs, &rhs, sizeof(Entry)) == 0; + })) { + return index; + } + } + + // Add the set of values and add the index to the dedup map. + uint32_t index = size(); + rows_.insert(rows_.end(), values, values + count); + dedup_.emplace(hash, index); + return index; + } + + uint32_t Dedup(Entry value) { + return Dedup(&value, /* count */ 1); + } + + // Calculate the column bit widths based on the current data. + void Measure(/*out*/ std::array* column_bits) const { + uint32_t max_column_value[kNumColumns]; + std::fill_n(max_column_value, kNumColumns, 0); + for (uint32_t r = 0; r < size(); r++) { + for (uint32_t c = 0; c < kNumColumns; c++) { + max_column_value[c] |= rows_[r][c] - kValueBias; + } + } + for (uint32_t c = 0; c < kNumColumns; c++) { + (*column_bits)[c] = MinimumBitsToStore(max_column_value[c]); + } + } + + // Encode the stored data into a BitTable. + template + void Encode(BitMemoryWriter& out) const { + size_t initial_bit_offset = out.GetBitOffset(); + + std::array column_bits; + Measure(&column_bits); + EncodeVarintBits(out, size()); + if (size() != 0) { + // Write table header. + for (uint32_t c = 0; c < kNumColumns; c++) { + EncodeVarintBits(out, column_bits[c]); + } + + // Write table data. + for (uint32_t r = 0; r < size(); r++) { + for (uint32_t c = 0; c < kNumColumns; c++) { + out.WriteBits(rows_[r][c] - kValueBias, column_bits[c]); + } + } + } + + // Verify the written data. + if (kIsDebugBuild) { + BitTableBase table; + BitMemoryReader reader(out.data(), initial_bit_offset); + table.Decode(reader); + DCHECK_EQ(size(), table.NumRows()); + for (uint32_t c = 0; c < kNumColumns; c++) { + DCHECK_EQ(column_bits[c], table.NumColumnBits(c)); + } + for (uint32_t r = 0; r < size(); r++) { + for (uint32_t c = 0; c < kNumColumns; c++) { + DCHECK_EQ(rows_[r][c], table.Get(r, c)) << " (" << r << ", " << c << ")"; + } + } + } + } + + protected: + ScopedArenaDeque rows_; + ScopedArenaUnorderedMultimap dedup_; // Hash -> row index. +}; + +template +class BitTableBuilder : public BitTableBuilderBase { + public: + using BitTableBuilderBase::BitTableBuilderBase; // Constructors. +}; + +// Helper class for encoding single-column BitTable of bitmaps (allows more than 32 bits). +class BitmapTableBuilder { + public: + explicit BitmapTableBuilder(ScopedArenaAllocator* const allocator) + : allocator_(allocator), + rows_(allocator->Adapter(kArenaAllocBitTableBuilder)), + dedup_(8, allocator_->Adapter(kArenaAllocBitTableBuilder)) { + } + + MemoryRegion operator[](size_t row) { return rows_[row]; } + const MemoryRegion operator[](size_t row) const { return rows_[row]; } + size_t size() const { return rows_.size(); } + + // Add the given bitmap to the table and return its index. + // If the bitmap was already added it will be deduplicated. + // The last bit must be set and any padding bits in the last byte must be zero. + uint32_t Dedup(const void* bitmap, size_t num_bits) { + MemoryRegion region(const_cast(bitmap), BitsToBytesRoundUp(num_bits)); + DCHECK(num_bits == 0 || BitMemoryRegion(region).LoadBit(num_bits - 1) == 1); + DCHECK_EQ(BitMemoryRegion(region).LoadBits(num_bits, region.size_in_bits() - num_bits), 0u); + FNVHash hasher; + uint32_t hash = hasher(region); + + // Check if we have already added identical bitmap. + auto range = dedup_.equal_range(hash); + for (auto it = range.first; it != range.second; ++it) { + if (MemoryRegion::ContentEquals()(region, rows_[it->second])) { + return it->second; + } + } + + // Add the bitmap and add the index to the dedup map. + uint32_t index = size(); + void* copy = allocator_->Alloc(region.size(), kArenaAllocBitTableBuilder); + memcpy(copy, region.pointer(), region.size()); + rows_.push_back(MemoryRegion(copy, region.size())); + dedup_.emplace(hash, index); + max_num_bits_ = std::max(max_num_bits_, num_bits); + return index; + } + + // Encode the stored data into a BitTable. + template + void Encode(BitMemoryWriter& out) const { + size_t initial_bit_offset = out.GetBitOffset(); + + EncodeVarintBits(out, size()); + if (size() != 0) { + EncodeVarintBits(out, max_num_bits_); + + // Write table data. + for (MemoryRegion row : rows_) { + BitMemoryRegion src(row); + BitMemoryRegion dst = out.Allocate(max_num_bits_); + dst.StoreBits(/* bit_offset */ 0, src, std::min(max_num_bits_, src.size_in_bits())); + } + } + + // Verify the written data. + if (kIsDebugBuild) { + BitTableBase<1> table; + BitMemoryReader reader(out.data(), initial_bit_offset); + table.Decode(reader); + DCHECK_EQ(size(), table.NumRows()); + DCHECK_EQ(max_num_bits_, table.NumColumnBits(0)); + for (uint32_t r = 0; r < size(); r++) { + BitMemoryRegion expected(rows_[r]); + BitMemoryRegion seen = table.GetBitMemoryRegion(r); + size_t num_bits = std::max(expected.size_in_bits(), seen.size_in_bits()); + for (size_t b = 0; b < num_bits; b++) { + bool e = b < expected.size_in_bits() && expected.LoadBit(b); + bool s = b < seen.size_in_bits() && seen.LoadBit(b); + DCHECK_EQ(e, s) << " (" << r << ")[" << b << "]"; + } + } + } + } + + private: + ScopedArenaAllocator* const allocator_; + ScopedArenaDeque rows_; + ScopedArenaUnorderedMultimap dedup_; // Hash -> row index. + size_t max_num_bits_ = 0u; +}; + +} // namespace art + +#endif // ART_LIBARTBASE_BASE_BIT_TABLE_H_ diff --git a/libartbase/base/bit_table_test.cc b/libartbase/base/bit_table_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..2fd9052516b7d0840930e90eaf1c258eba2476c7 --- /dev/null +++ b/libartbase/base/bit_table_test.cc @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "bit_table.h" + +#include + +#include "gtest/gtest.h" + +#include "base/arena_allocator.h" +#include "base/bit_utils.h" +#include "base/malloc_arena_pool.h" + +namespace art { + +TEST(BitTableTest, TestVarint) { + for (size_t start_bit_offset = 0; start_bit_offset <= 32; start_bit_offset++) { + uint32_t values[] = { 0, 1, 11, 12, 15, 16, 255, 256, ~1u, ~0u }; + for (uint32_t value : values) { + std::vector buffer; + BitMemoryWriter> writer(&buffer, start_bit_offset); + EncodeVarintBits(writer, value); + + BitMemoryReader reader(buffer.data(), start_bit_offset); + uint32_t result = DecodeVarintBits(reader); + EXPECT_EQ(writer.GetBitOffset(), reader.GetBitOffset()); + EXPECT_EQ(value, result); + } + } +} + +TEST(BitTableTest, TestEmptyTable) { + MallocArenaPool pool; + ArenaStack arena_stack(&pool); + ScopedArenaAllocator allocator(&arena_stack); + + std::vector buffer; + BitMemoryWriter> writer(&buffer); + BitTableBuilderBase<1> builder(&allocator); + builder.Encode(writer); + + BitMemoryReader reader(buffer.data()); + BitTableBase<1> table(reader); + EXPECT_EQ(writer.GetBitOffset(), reader.GetBitOffset()); + EXPECT_EQ(0u, table.NumRows()); +} + +TEST(BitTableTest, TestSingleColumnTable) { + MallocArenaPool pool; + ArenaStack arena_stack(&pool); + ScopedArenaAllocator allocator(&arena_stack); + + constexpr uint32_t kNoValue = -1; + std::vector buffer; + BitMemoryWriter> writer(&buffer); + BitTableBuilderBase<1> builder(&allocator); + builder.Add({42u}); + builder.Add({kNoValue}); + builder.Add({1000u}); + builder.Add({kNoValue}); + builder.Encode(writer); + + BitMemoryReader reader(buffer.data()); + BitTableBase<1> table(reader); + EXPECT_EQ(writer.GetBitOffset(), reader.GetBitOffset()); + EXPECT_EQ(4u, table.NumRows()); + EXPECT_EQ(42u, table.Get(0)); + EXPECT_EQ(kNoValue, table.Get(1)); + EXPECT_EQ(1000u, table.Get(2)); + EXPECT_EQ(kNoValue, table.Get(3)); + EXPECT_EQ(10u, table.NumColumnBits(0)); +} + +TEST(BitTableTest, TestUnalignedTable) { + MallocArenaPool pool; + ArenaStack arena_stack(&pool); + ScopedArenaAllocator allocator(&arena_stack); + + for (size_t start_bit_offset = 0; start_bit_offset <= 32; start_bit_offset++) { + std::vector buffer; + BitMemoryWriter> writer(&buffer, start_bit_offset); + BitTableBuilderBase<1> builder(&allocator); + builder.Add({42u}); + builder.Encode(writer); + + BitMemoryReader reader(buffer.data(), start_bit_offset); + BitTableBase<1> table(reader); + EXPECT_EQ(writer.GetBitOffset(), reader.GetBitOffset()); + EXPECT_EQ(1u, table.NumRows()); + EXPECT_EQ(42u, table.Get(0)); + } +} + +TEST(BitTableTest, TestBigTable) { + MallocArenaPool pool; + ArenaStack arena_stack(&pool); + ScopedArenaAllocator allocator(&arena_stack); + + constexpr uint32_t kNoValue = -1; + std::vector buffer; + BitMemoryWriter> writer(&buffer); + BitTableBuilderBase<4> builder(&allocator); + builder.Add({42u, kNoValue, 0u, static_cast(-2)}); + builder.Add({62u, kNoValue, 63u, static_cast(-3)}); + builder.Encode(writer); + + BitMemoryReader reader(buffer.data()); + BitTableBase<4> table(reader); + EXPECT_EQ(writer.GetBitOffset(), reader.GetBitOffset()); + EXPECT_EQ(2u, table.NumRows()); + EXPECT_EQ(42u, table.Get(0, 0)); + EXPECT_EQ(kNoValue, table.Get(0, 1)); + EXPECT_EQ(0u, table.Get(0, 2)); + EXPECT_EQ(static_cast(-2), table.Get(0, 3)); + EXPECT_EQ(62u, table.Get(1, 0)); + EXPECT_EQ(kNoValue, table.Get(1, 1)); + EXPECT_EQ(63u, table.Get(1, 2)); + EXPECT_EQ(static_cast(-3), table.Get(1, 3)); + EXPECT_EQ(6u, table.NumColumnBits(0)); + EXPECT_EQ(0u, table.NumColumnBits(1)); + EXPECT_EQ(7u, table.NumColumnBits(2)); + EXPECT_EQ(32u, table.NumColumnBits(3)); +} + +TEST(BitTableTest, TestDedup) { + MallocArenaPool pool; + ArenaStack arena_stack(&pool); + ScopedArenaAllocator allocator(&arena_stack); + + BitTableBuilderBase<2> builder(&allocator); + BitTableBuilderBase<2>::Entry value0{1, 2}; + BitTableBuilderBase<2>::Entry value1{3, 4}; + EXPECT_EQ(0u, builder.Dedup(&value0)); + EXPECT_EQ(1u, builder.Dedup(&value1)); + EXPECT_EQ(0u, builder.Dedup(&value0)); + EXPECT_EQ(1u, builder.Dedup(&value1)); + EXPECT_EQ(2u, builder.size()); +} + +TEST(BitTableTest, TestBitmapTable) { + MallocArenaPool pool; + ArenaStack arena_stack(&pool); + ScopedArenaAllocator allocator(&arena_stack); + + std::vector buffer; + BitMemoryWriter> writer(&buffer); + const uint64_t value = 0xDEADBEEF0BADF00Dull; + BitmapTableBuilder builder(&allocator); + std::multimap indicies; // bitmap -> row. + for (size_t bit_length = 0; bit_length <= BitSizeOf(); ++bit_length) { + uint64_t bitmap = value & MaxInt(bit_length); + indicies.emplace(bitmap, builder.Dedup(&bitmap, MinimumBitsToStore(bitmap))); + } + builder.Encode(writer); + EXPECT_EQ(1 + static_cast(POPCOUNT(value)), builder.size()); + + BitMemoryReader reader(buffer.data()); + BitTableBase<1> table(reader); + EXPECT_EQ(writer.GetBitOffset(), reader.GetBitOffset()); + for (auto it : indicies) { + uint64_t expected = it.first; + BitMemoryRegion actual = table.GetBitMemoryRegion(it.second); + EXPECT_GE(actual.size_in_bits(), MinimumBitsToStore(expected)); + for (size_t b = 0; b < actual.size_in_bits(); b++, expected >>= 1) { + EXPECT_EQ(expected & 1, actual.LoadBit(b)) << "b=" << b; + } + } +} + +TEST(BitTableTest, TestCollisions) { + MallocArenaPool pool; + ArenaStack arena_stack(&pool); + ScopedArenaAllocator allocator(&arena_stack); + FNVHash hasher; + + BitTableBuilderBase<2>::Entry value0{56948505, 0}; + BitTableBuilderBase<2>::Entry value1{67108869, 0}; + + BitTableBuilderBase<2> builder(&allocator); + EXPECT_EQ(hasher(MemoryRegion(&value0, sizeof(value0))), + hasher(MemoryRegion(&value1, sizeof(value1)))); + EXPECT_EQ(0u, builder.Dedup(&value0)); + EXPECT_EQ(1u, builder.Dedup(&value1)); + EXPECT_EQ(0u, builder.Dedup(&value0)); + EXPECT_EQ(1u, builder.Dedup(&value1)); + EXPECT_EQ(2u, builder.size()); + + BitmapTableBuilder builder2(&allocator); + EXPECT_EQ(hasher(MemoryRegion(&value0, BitsToBytesRoundUp(MinimumBitsToStore(value0[0])))), + hasher(MemoryRegion(&value1, BitsToBytesRoundUp(MinimumBitsToStore(value1[0]))))); + EXPECT_EQ(0u, builder2.Dedup(&value0[0], MinimumBitsToStore(value0[0]))); + EXPECT_EQ(1u, builder2.Dedup(&value1[0], MinimumBitsToStore(value1[0]))); + EXPECT_EQ(0u, builder2.Dedup(&value0[0], MinimumBitsToStore(value0[0]))); + EXPECT_EQ(1u, builder2.Dedup(&value1[0], MinimumBitsToStore(value1[0]))); + EXPECT_EQ(2u, builder2.size()); +} + +} // namespace art diff --git a/libartbase/base/bit_utils.h b/libartbase/base/bit_utils.h index ff6c16068073b7e5fcbe86888ba7093e0a98cf29..58cc78ccca99ff8d1a411d9f346256a25e4e6783 100644 --- a/libartbase/base/bit_utils.h +++ b/libartbase/base/bit_utils.h @@ -22,7 +22,8 @@ #include -#include "base/stl_util_identity.h" +#include "globals.h" +#include "stl_util_identity.h" namespace art { @@ -499,6 +500,10 @@ inline static constexpr T BitFieldExtract(T value, size_t lsb, size_t width) { return bitfield_unsigned; } +inline static constexpr size_t BitsToBytesRoundUp(size_t num_bits) { + return RoundUp(num_bits, kBitsPerByte) / kBitsPerByte; +} + } // namespace art #endif // ART_LIBARTBASE_BASE_BIT_UTILS_H_ diff --git a/libartbase/base/bit_utils_iterator.h b/libartbase/base/bit_utils_iterator.h index 3fab15a3db7d8be87d42c62163d81c471c7406cb..4975ebfb44696e87d90657984a972213d8edcf05 100644 --- a/libartbase/base/bit_utils_iterator.h +++ b/libartbase/base/bit_utils_iterator.h @@ -23,9 +23,9 @@ #include -#include "base/bit_utils.h" -#include "base/iteration_range.h" -#include "base/stl_util.h" +#include "bit_utils.h" +#include "iteration_range.h" +#include "stl_util.h" namespace art { diff --git a/libartbase/base/bit_vector-inl.h b/libartbase/base/bit_vector-inl.h index 7a9f4650da13546ac9bf7452a4b1f58f3caeb7c7..2bdc14ebe9b04d70f8f734183b4852db33e2a254 100644 --- a/libartbase/base/bit_vector-inl.h +++ b/libartbase/base/bit_vector-inl.h @@ -21,7 +21,7 @@ #include -#include "base/bit_utils.h" +#include "bit_utils.h" namespace art { diff --git a/libartbase/base/bit_vector.h b/libartbase/base/bit_vector.h index 2ffa2aa9c9553e1c1415d9d6d39a69e02d2bfa97..a930f4e556901c6b8ff37e3aa1a77b74e830f428 100644 --- a/libartbase/base/bit_vector.h +++ b/libartbase/base/bit_vector.h @@ -20,7 +20,7 @@ #include #include -#include "base/bit_utils.h" +#include "bit_utils.h" #include "globals.h" namespace art { diff --git a/libartbase/base/bounded_fifo.h b/libartbase/base/bounded_fifo.h index 444f31a55bd8a7a9b7c6fc995d71d835ff7f8be0..43d14f4cefeeece3eaa010134c5b39caa465bf2a 100644 --- a/libartbase/base/bounded_fifo.h +++ b/libartbase/base/bounded_fifo.h @@ -19,7 +19,7 @@ #include -#include "base/bit_utils.h" +#include "bit_utils.h" namespace art { diff --git a/libartbase/base/common_art_test.cc b/libartbase/base/common_art_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..e24b073142631ee3467a5536a37941dd3f89bfc6 --- /dev/null +++ b/libartbase/base/common_art_test.cc @@ -0,0 +1,506 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common_art_test.h" + +#include +#include +#include +#include +#include +#include "nativehelper/scoped_local_ref.h" + +#include "android-base/stringprintf.h" +#include "android-base/unique_fd.h" +#include + +#include "art_field-inl.h" +#include "base/file_utils.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/mem_map.h" +#include "base/mutex.h" +#include "base/os.h" +#include "base/runtime_debug.h" +#include "base/stl_util.h" +#include "base/unix_file/fd_file.h" +#include "dex/art_dex_file_loader.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_loader.h" +#include "dex/primitive.h" +#include "gtest/gtest.h" + +namespace art { + +using android::base::StringPrintf; + +ScratchFile::ScratchFile() { + // ANDROID_DATA needs to be set + CHECK_NE(static_cast(nullptr), getenv("ANDROID_DATA")) << + "Are you subclassing RuntimeTest?"; + filename_ = getenv("ANDROID_DATA"); + filename_ += "/TmpFile-XXXXXX"; + int fd = mkstemp(&filename_[0]); + CHECK_NE(-1, fd) << strerror(errno) << " for " << filename_; + file_.reset(new File(fd, GetFilename(), true)); +} + +ScratchFile::ScratchFile(const ScratchFile& other, const char* suffix) + : ScratchFile(other.GetFilename() + suffix) {} + +ScratchFile::ScratchFile(const std::string& filename) : filename_(filename) { + int fd = open(filename_.c_str(), O_RDWR | O_CREAT, 0666); + CHECK_NE(-1, fd); + file_.reset(new File(fd, GetFilename(), true)); +} + +ScratchFile::ScratchFile(File* file) { + CHECK(file != nullptr); + filename_ = file->GetPath(); + file_.reset(file); +} + +ScratchFile::ScratchFile(ScratchFile&& other) { + *this = std::move(other); +} + +ScratchFile& ScratchFile::operator=(ScratchFile&& other) { + if (GetFile() != other.GetFile()) { + std::swap(filename_, other.filename_); + std::swap(file_, other.file_); + } + return *this; +} + +ScratchFile::~ScratchFile() { + Unlink(); +} + +int ScratchFile::GetFd() const { + return file_->Fd(); +} + +void ScratchFile::Close() { + if (file_.get() != nullptr) { + if (file_->FlushCloseOrErase() != 0) { + PLOG(WARNING) << "Error closing scratch file."; + } + } +} + +void ScratchFile::Unlink() { + if (!OS::FileExists(filename_.c_str())) { + return; + } + Close(); + int unlink_result = unlink(filename_.c_str()); + CHECK_EQ(0, unlink_result); +} + +void CommonArtTestImpl::SetUpAndroidRoot() { + if (IsHost()) { + // $ANDROID_ROOT is set on the device, but not necessarily on the host. + // But it needs to be set so that icu4c can find its locale data. + const char* android_root_from_env = getenv("ANDROID_ROOT"); + if (android_root_from_env == nullptr) { + // Use ANDROID_HOST_OUT for ANDROID_ROOT if it is set. + const char* android_host_out = getenv("ANDROID_HOST_OUT"); + if (android_host_out != nullptr) { + setenv("ANDROID_ROOT", android_host_out, 1); + } else { + // Build it from ANDROID_BUILD_TOP or cwd + std::string root; + const char* android_build_top = getenv("ANDROID_BUILD_TOP"); + if (android_build_top != nullptr) { + root += android_build_top; + } else { + // Not set by build server, so default to current directory + char* cwd = getcwd(nullptr, 0); + setenv("ANDROID_BUILD_TOP", cwd, 1); + root += cwd; + free(cwd); + } +#if defined(__linux__) + root += "/out/host/linux-x86"; +#elif defined(__APPLE__) + root += "/out/host/darwin-x86"; +#else +#error unsupported OS +#endif + setenv("ANDROID_ROOT", root.c_str(), 1); + } + } + setenv("LD_LIBRARY_PATH", ":", 0); // Required by java.lang.System.. + + // Not set by build server, so default + if (getenv("ANDROID_HOST_OUT") == nullptr) { + setenv("ANDROID_HOST_OUT", getenv("ANDROID_ROOT"), 1); + } + } +} + +void CommonArtTestImpl::SetUpAndroidData(std::string& android_data) { + // On target, Cannot use /mnt/sdcard because it is mounted noexec, so use subdir of dalvik-cache + if (IsHost()) { + const char* tmpdir = getenv("TMPDIR"); + if (tmpdir != nullptr && tmpdir[0] != 0) { + android_data = tmpdir; + } else { + android_data = "/tmp"; + } + } else { + android_data = "/data/dalvik-cache"; + } + android_data += "/art-data-XXXXXX"; + if (mkdtemp(&android_data[0]) == nullptr) { + PLOG(FATAL) << "mkdtemp(\"" << &android_data[0] << "\") failed"; + } + setenv("ANDROID_DATA", android_data.c_str(), 1); +} + +void CommonArtTestImpl::SetUp() { + SetUpAndroidRoot(); + SetUpAndroidData(android_data_); + dalvik_cache_.append(android_data_.c_str()); + dalvik_cache_.append("/dalvik-cache"); + int mkdir_result = mkdir(dalvik_cache_.c_str(), 0700); + ASSERT_EQ(mkdir_result, 0); +} + +void CommonArtTestImpl::TearDownAndroidData(const std::string& android_data, bool fail_on_error) { + if (fail_on_error) { + ASSERT_EQ(rmdir(android_data.c_str()), 0); + } else { + rmdir(android_data.c_str()); + } +} + +// Helper - find directory with the following format: +// ${ANDROID_BUILD_TOP}/${subdir1}/${subdir2}-${version}/${subdir3}/bin/ +std::string CommonArtTestImpl::GetAndroidToolsDir(const std::string& subdir1, + const std::string& subdir2, + const std::string& subdir3) { + std::string root; + const char* android_build_top = getenv("ANDROID_BUILD_TOP"); + if (android_build_top != nullptr) { + root = android_build_top; + } else { + // Not set by build server, so default to current directory + char* cwd = getcwd(nullptr, 0); + setenv("ANDROID_BUILD_TOP", cwd, 1); + root = cwd; + free(cwd); + } + + std::string toolsdir = root + "/" + subdir1; + std::string founddir; + DIR* dir; + if ((dir = opendir(toolsdir.c_str())) != nullptr) { + float maxversion = 0; + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + std::string format = subdir2 + "-%f"; + float version; + if (std::sscanf(entry->d_name, format.c_str(), &version) == 1) { + if (version > maxversion) { + maxversion = version; + founddir = toolsdir + "/" + entry->d_name + "/" + subdir3 + "/bin/"; + } + } + } + closedir(dir); + } + + if (founddir.empty()) { + ADD_FAILURE() << "Cannot find Android tools directory."; + } + return founddir; +} + +std::string CommonArtTestImpl::GetAndroidHostToolsDir() { + return GetAndroidToolsDir("prebuilts/gcc/linux-x86/host", + "x86_64-linux-glibc2.15", + "x86_64-linux"); +} + +std::string CommonArtTestImpl::GetCoreArtLocation() { + return GetCoreFileLocation("art"); +} + +std::string CommonArtTestImpl::GetCoreOatLocation() { + return GetCoreFileLocation("oat"); +} + +std::unique_ptr CommonArtTestImpl::LoadExpectSingleDexFile(const char* location) { + std::vector> dex_files; + std::string error_msg; + MemMap::Init(); + static constexpr bool kVerifyChecksum = true; + const ArtDexFileLoader dex_file_loader; + if (!dex_file_loader.Open( + location, location, /* verify */ true, kVerifyChecksum, &error_msg, &dex_files)) { + LOG(FATAL) << "Could not open .dex file '" << location << "': " << error_msg << "\n"; + UNREACHABLE(); + } else { + CHECK_EQ(1U, dex_files.size()) << "Expected only one dex file in " << location; + return std::move(dex_files[0]); + } +} + +void CommonArtTestImpl::ClearDirectory(const char* dirpath, bool recursive) { + ASSERT_TRUE(dirpath != nullptr); + DIR* dir = opendir(dirpath); + ASSERT_TRUE(dir != nullptr); + dirent* e; + struct stat s; + while ((e = readdir(dir)) != nullptr) { + if ((strcmp(e->d_name, ".") == 0) || (strcmp(e->d_name, "..") == 0)) { + continue; + } + std::string filename(dirpath); + filename.push_back('/'); + filename.append(e->d_name); + int stat_result = lstat(filename.c_str(), &s); + ASSERT_EQ(0, stat_result) << "unable to stat " << filename; + if (S_ISDIR(s.st_mode)) { + if (recursive) { + ClearDirectory(filename.c_str()); + int rmdir_result = rmdir(filename.c_str()); + ASSERT_EQ(0, rmdir_result) << filename; + } + } else { + int unlink_result = unlink(filename.c_str()); + ASSERT_EQ(0, unlink_result) << filename; + } + } + closedir(dir); +} + +void CommonArtTestImpl::TearDown() { + const char* android_data = getenv("ANDROID_DATA"); + ASSERT_TRUE(android_data != nullptr); + ClearDirectory(dalvik_cache_.c_str()); + int rmdir_cache_result = rmdir(dalvik_cache_.c_str()); + ASSERT_EQ(0, rmdir_cache_result); + TearDownAndroidData(android_data_, true); + dalvik_cache_.clear(); +} + +static std::string GetDexFileName(const std::string& jar_prefix, bool host) { + std::string path; + if (host) { + const char* host_dir = getenv("ANDROID_HOST_OUT"); + CHECK(host_dir != nullptr); + path = host_dir; + } else { + path = GetAndroidRoot(); + } + + std::string suffix = host + ? "-hostdex" // The host version. + : "-testdex"; // The unstripped target version. + + return StringPrintf("%s/framework/%s%s.jar", path.c_str(), jar_prefix.c_str(), suffix.c_str()); +} + +std::vector CommonArtTestImpl::GetLibCoreDexFileNames() { + return std::vector({GetDexFileName("core-oj", IsHost()), + GetDexFileName("core-libart", IsHost())}); +} + +std::string CommonArtTestImpl::GetTestAndroidRoot() { + if (IsHost()) { + const char* host_dir = getenv("ANDROID_HOST_OUT"); + CHECK(host_dir != nullptr); + return host_dir; + } + return GetAndroidRoot(); +} + +// Check that for target builds we have ART_TARGET_NATIVETEST_DIR set. +#ifdef ART_TARGET +#ifndef ART_TARGET_NATIVETEST_DIR +#error "ART_TARGET_NATIVETEST_DIR not set." +#endif +// Wrap it as a string literal. +#define ART_TARGET_NATIVETEST_DIR_STRING STRINGIFY(ART_TARGET_NATIVETEST_DIR) "/" +#else +#define ART_TARGET_NATIVETEST_DIR_STRING "" +#endif + +std::string CommonArtTestImpl::GetTestDexFileName(const char* name) const { + CHECK(name != nullptr); + std::string filename; + if (IsHost()) { + filename += getenv("ANDROID_HOST_OUT"); + filename += "/framework/"; + } else { + filename += ART_TARGET_NATIVETEST_DIR_STRING; + } + filename += "art-gtest-"; + filename += name; + filename += ".jar"; + return filename; +} + +std::vector> CommonArtTestImpl::OpenDexFiles(const char* filename) { + static constexpr bool kVerify = true; + static constexpr bool kVerifyChecksum = true; + std::string error_msg; + const ArtDexFileLoader dex_file_loader; + std::vector> dex_files; + bool success = dex_file_loader.Open(filename, + filename, + kVerify, + kVerifyChecksum, + &error_msg, + &dex_files); + CHECK(success) << "Failed to open '" << filename << "': " << error_msg; + for (auto& dex_file : dex_files) { + CHECK_EQ(PROT_READ, dex_file->GetPermissions()); + CHECK(dex_file->IsReadOnly()); + } + return dex_files; +} + +std::vector> CommonArtTestImpl::OpenTestDexFiles( + const char* name) { + return OpenDexFiles(GetTestDexFileName(name).c_str()); +} + +std::unique_ptr CommonArtTestImpl::OpenTestDexFile(const char* name) { + std::vector> vector = OpenTestDexFiles(name); + EXPECT_EQ(1U, vector.size()); + return std::move(vector[0]); +} + +std::string CommonArtTestImpl::GetCoreFileLocation(const char* suffix) { + CHECK(suffix != nullptr); + + std::string location; + if (IsHost()) { + const char* host_dir = getenv("ANDROID_HOST_OUT"); + CHECK(host_dir != nullptr); + location = StringPrintf("%s/framework/core.%s", host_dir, suffix); + } else { + location = StringPrintf("/data/art-test/core.%s", suffix); + } + + return location; +} + +std::string CommonArtTestImpl::CreateClassPath( + const std::vector>& dex_files) { + CHECK(!dex_files.empty()); + std::string classpath = dex_files[0]->GetLocation(); + for (size_t i = 1; i < dex_files.size(); i++) { + classpath += ":" + dex_files[i]->GetLocation(); + } + return classpath; +} + +std::string CommonArtTestImpl::CreateClassPathWithChecksums( + const std::vector>& dex_files) { + CHECK(!dex_files.empty()); + std::string classpath = dex_files[0]->GetLocation() + "*" + + std::to_string(dex_files[0]->GetLocationChecksum()); + for (size_t i = 1; i < dex_files.size(); i++) { + classpath += ":" + dex_files[i]->GetLocation() + "*" + + std::to_string(dex_files[i]->GetLocationChecksum()); + } + return classpath; +} + +CommonArtTestImpl::ForkAndExecResult CommonArtTestImpl::ForkAndExec( + const std::vector& argv, + const PostForkFn& post_fork, + const OutputHandlerFn& handler) { + ForkAndExecResult result; + result.status_code = 0; + result.stage = ForkAndExecResult::kLink; + + std::vector c_args; + for (const std::string& str : argv) { + c_args.push_back(str.c_str()); + } + c_args.push_back(nullptr); + + android::base::unique_fd link[2]; + { + int link_fd[2]; + + if (pipe(link_fd) == -1) { + return result; + } + link[0].reset(link_fd[0]); + link[1].reset(link_fd[1]); + } + + result.stage = ForkAndExecResult::kFork; + + pid_t pid = fork(); + if (pid == -1) { + return result; + } + + if (pid == 0) { + if (!post_fork()) { + LOG(ERROR) << "Failed post-fork function"; + exit(1); + UNREACHABLE(); + } + + // Redirect stdout and stderr. + dup2(link[1].get(), STDOUT_FILENO); + dup2(link[1].get(), STDERR_FILENO); + + link[0].reset(); + link[1].reset(); + + execv(c_args[0], const_cast(c_args.data())); + exit(1); + UNREACHABLE(); + } + + result.stage = ForkAndExecResult::kWaitpid; + link[1].reset(); + + char buffer[128] = { 0 }; + ssize_t bytes_read = 0; + while (TEMP_FAILURE_RETRY(bytes_read = read(link[0].get(), buffer, 128)) > 0) { + handler(buffer, bytes_read); + } + handler(buffer, 0u); // End with a virtual write of zero length to simplify clients. + + link[0].reset(); + + if (waitpid(pid, &result.status_code, 0) == -1) { + return result; + } + + result.stage = ForkAndExecResult::kFinished; + return result; +} + +CommonArtTestImpl::ForkAndExecResult CommonArtTestImpl::ForkAndExec( + const std::vector& argv, const PostForkFn& post_fork, std::string* output) { + auto string_collect_fn = [output](char* buf, size_t len) { + *output += std::string(buf, len); + }; + return ForkAndExec(argv, post_fork, string_collect_fn); +} + +} // namespace art diff --git a/libartbase/base/common_art_test.h b/libartbase/base/common_art_test.h new file mode 100644 index 0000000000000000000000000000000000000000..62834c7d355321df9e87c1be504a1462fd59120f --- /dev/null +++ b/libartbase/base/common_art_test.h @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_LIBARTBASE_BASE_COMMON_ART_TEST_H_ +#define ART_LIBARTBASE_BASE_COMMON_ART_TEST_H_ + +#include + +#include +#include + +#include + +#include + +#include "base/globals.h" +#include "base/mutex.h" +#include "base/os.h" +#include "base/unix_file/fd_file.h" +#include "dex/art_dex_file_loader.h" +#include "dex/compact_dex_level.h" + +namespace art { + +using LogSeverity = android::base::LogSeverity; +using ScopedLogSeverity = android::base::ScopedLogSeverity; + +// OBJ pointer helpers to avoid needing .Decode everywhere. +#define EXPECT_OBJ_PTR_EQ(a, b) EXPECT_EQ(MakeObjPtr(a).Ptr(), MakeObjPtr(b).Ptr()); +#define ASSERT_OBJ_PTR_EQ(a, b) ASSERT_EQ(MakeObjPtr(a).Ptr(), MakeObjPtr(b).Ptr()); +#define EXPECT_OBJ_PTR_NE(a, b) EXPECT_NE(MakeObjPtr(a).Ptr(), MakeObjPtr(b).Ptr()); +#define ASSERT_OBJ_PTR_NE(a, b) ASSERT_NE(MakeObjPtr(a).Ptr(), MakeObjPtr(b).Ptr()); + +class DexFile; + +class ScratchFile { + public: + ScratchFile(); + + explicit ScratchFile(const std::string& filename); + + ScratchFile(const ScratchFile& other, const char* suffix); + + ScratchFile(ScratchFile&& other); + + ScratchFile& operator=(ScratchFile&& other); + + explicit ScratchFile(File* file); + + ~ScratchFile(); + + const std::string& GetFilename() const { + return filename_; + } + + File* GetFile() const { + return file_.get(); + } + + int GetFd() const; + + void Close(); + void Unlink(); + + private: + std::string filename_; + std::unique_ptr file_; +}; + +class CommonArtTestImpl { + public: + CommonArtTestImpl() = default; + virtual ~CommonArtTestImpl() = default; + + static void SetUpAndroidRoot(); + + // Note: setting up ANDROID_DATA may create a temporary directory. If this is used in a + // non-derived class, be sure to also call the corresponding tear-down below. + static void SetUpAndroidData(std::string& android_data); + + static void TearDownAndroidData(const std::string& android_data, bool fail_on_error); + + // Gets the paths of the libcore dex files. + static std::vector GetLibCoreDexFileNames(); + + // Returns bin directory which contains host's prebuild tools. + static std::string GetAndroidHostToolsDir(); + + // Retuerns the filename for a test dex (i.e. XandY or ManyMethods). + std::string GetTestDexFileName(const char* name) const; + + template + bool MutateDexFile(File* output_dex, const std::string& input_jar, const Mutator& mutator) { + std::vector> dex_files; + std::string error_msg; + const ArtDexFileLoader dex_file_loader; + CHECK(dex_file_loader.Open(input_jar.c_str(), + input_jar.c_str(), + /*verify*/ true, + /*verify_checksum*/ true, + &error_msg, + &dex_files)) << error_msg; + EXPECT_EQ(dex_files.size(), 1u) << "Only one input dex is supported"; + const std::unique_ptr& dex = dex_files[0]; + CHECK(dex->EnableWrite()) << "Failed to enable write"; + DexFile* dex_file = const_cast(dex.get()); + mutator(dex_file); + const_cast(dex_file->GetHeader()).checksum_ = dex_file->CalculateChecksum(); + if (!output_dex->WriteFully(dex->Begin(), dex->Size())) { + return false; + } + if (output_dex->Flush() != 0) { + PLOG(FATAL) << "Could not flush the output file."; + } + return true; + } + + struct ForkAndExecResult { + enum Stage { + kLink, + kFork, + kWaitpid, + kFinished, + }; + Stage stage; + int status_code; + + bool StandardSuccess() { + return stage == kFinished && WIFEXITED(status_code) && WEXITSTATUS(status_code) == 0; + } + }; + using OutputHandlerFn = std::function; + using PostForkFn = std::function; + static ForkAndExecResult ForkAndExec(const std::vector& argv, + const PostForkFn& post_fork, + const OutputHandlerFn& handler); + static ForkAndExecResult ForkAndExec(const std::vector& argv, + const PostForkFn& post_fork, + std::string* output); + + protected: + static bool IsHost() { + return !kIsTargetBuild; + } + + // Helper - find directory with the following format: + // ${ANDROID_BUILD_TOP}/${subdir1}/${subdir2}-${version}/${subdir3}/bin/ + static std::string GetAndroidToolsDir(const std::string& subdir1, + const std::string& subdir2, + const std::string& subdir3); + + // File location to core.art, e.g. $ANDROID_HOST_OUT/system/framework/core.art + static std::string GetCoreArtLocation(); + + // File location to core.oat, e.g. $ANDROID_HOST_OUT/system/framework/core.oat + static std::string GetCoreOatLocation(); + + std::unique_ptr LoadExpectSingleDexFile(const char* location); + + void ClearDirectory(const char* dirpath, bool recursive = true); + + std::string GetTestAndroidRoot(); + + // Open a file (allows reading of framework jars). + std::vector> OpenDexFiles(const char* filename); + // Open a test file (art-gtest-*.jar). + std::vector> OpenTestDexFiles(const char* name); + + std::unique_ptr OpenTestDexFile(const char* name); + + + std::string android_data_; + std::string dalvik_cache_; + + virtual void SetUp(); + + virtual void TearDown(); + + // Creates the class path string for the given dex files (the list of dex file locations + // separated by ':'). + std::string CreateClassPath(const std::vector>& dex_files); + // Same as CreateClassPath but add the dex file checksum after each location. The separator + // is '*'. + std::string CreateClassPathWithChecksums( + const std::vector>& dex_files); + + static std::string GetCoreFileLocation(const char* suffix); + + std::vector> loaded_dex_files_; +}; + +template +class CommonArtTestBase : public TestType, public CommonArtTestImpl { + public: + CommonArtTestBase() {} + virtual ~CommonArtTestBase() {} + + protected: + virtual void SetUp() OVERRIDE { + CommonArtTestImpl::SetUp(); + } + + virtual void TearDown() OVERRIDE { + CommonArtTestImpl::TearDown(); + } +}; + +using CommonArtTest = CommonArtTestBase; + +template +using CommonArtTestWithParam = CommonArtTestBase>; + +#define TEST_DISABLED_FOR_TARGET() \ + if (kIsTargetBuild) { \ + printf("WARNING: TEST DISABLED FOR TARGET\n"); \ + return; \ + } + +#define TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS() \ + if (!kHostStaticBuildEnabled) { \ + printf("WARNING: TEST DISABLED FOR NON-STATIC HOST BUILDS\n"); \ + return; \ + } + +#define TEST_DISABLED_FOR_MEMORY_TOOL() \ + if (kRunningOnMemoryTool) { \ + printf("WARNING: TEST DISABLED FOR MEMORY TOOL\n"); \ + return; \ + } + +#define TEST_DISABLED_FOR_HEAP_POISONING() \ + if (kPoisonHeapReferences) { \ + printf("WARNING: TEST DISABLED FOR HEAP POISONING\n"); \ + return; \ + } +} // namespace art + +#define TEST_DISABLED_FOR_MEMORY_TOOL_WITH_HEAP_POISONING() \ + if (kRunningOnMemoryTool && kPoisonHeapReferences) { \ + printf("WARNING: TEST DISABLED FOR MEMORY TOOL WITH HEAP POISONING\n"); \ + return; \ + } + +#endif // ART_LIBARTBASE_BASE_COMMON_ART_TEST_H_ diff --git a/libartbase/base/data_hash.h b/libartbase/base/data_hash.h new file mode 100644 index 0000000000000000000000000000000000000000..5ad7779b805d2b922255ad6d771f06f365be997f --- /dev/null +++ b/libartbase/base/data_hash.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_LIBARTBASE_BASE_DATA_HASH_H_ +#define ART_LIBARTBASE_BASE_DATA_HASH_H_ + +#include "base/macros.h" + +namespace art { + +// Hash bytes using a relatively fast hash. +static inline size_t HashBytes(const uint8_t* data, size_t len) { + size_t hash = 0x811c9dc5; + for (uint32_t i = 0; i < len; ++i) { + hash = (hash * 16777619) ^ data[i]; + } + hash += hash << 13; + hash ^= hash >> 7; + hash += hash << 3; + hash ^= hash >> 17; + hash += hash << 5; + return hash; +} + +class DataHash { + private: + static constexpr bool kUseMurmur3Hash = true; + + public: + template + size_t operator()(const Container& array) const { + // Containers that provide the data() function use contiguous storage. + const uint8_t* data = reinterpret_cast(array.data()); + uint32_t len = sizeof(typename Container::value_type) * array.size(); + if (kUseMurmur3Hash) { + static constexpr uint32_t c1 = 0xcc9e2d51; + static constexpr uint32_t c2 = 0x1b873593; + static constexpr uint32_t r1 = 15; + static constexpr uint32_t r2 = 13; + static constexpr uint32_t m = 5; + static constexpr uint32_t n = 0xe6546b64; + + uint32_t hash = 0; + + const int nblocks = len / 4; + typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t; + const unaligned_uint32_t *blocks = reinterpret_cast(data); + int i; + for (i = 0; i < nblocks; i++) { + uint32_t k = blocks[i]; + k *= c1; + k = (k << r1) | (k >> (32 - r1)); + k *= c2; + + hash ^= k; + hash = ((hash << r2) | (hash >> (32 - r2))) * m + n; + } + + const uint8_t *tail = reinterpret_cast(data + nblocks * 4); + uint32_t k1 = 0; + + switch (len & 3) { + case 3: + k1 ^= tail[2] << 16; + FALLTHROUGH_INTENDED; + case 2: + k1 ^= tail[1] << 8; + FALLTHROUGH_INTENDED; + case 1: + k1 ^= tail[0]; + + k1 *= c1; + k1 = (k1 << r1) | (k1 >> (32 - r1)); + k1 *= c2; + hash ^= k1; + } + + hash ^= len; + hash ^= (hash >> 16); + hash *= 0x85ebca6b; + hash ^= (hash >> 13); + hash *= 0xc2b2ae35; + hash ^= (hash >> 16); + + return hash; + } else { + return HashBytes(data, len); + } + } +}; + +} // namespace art + +#endif // ART_LIBARTBASE_BASE_DATA_HASH_H_ diff --git a/runtime/base/dumpable.h b/libartbase/base/dumpable.h similarity index 62% rename from runtime/base/dumpable.h rename to libartbase/base/dumpable.h index 9ef8d69b48984592ba620325d025ac472418b5a7..0c00505461b290aaf1a122e41f0d829f011387bd 100644 --- a/runtime/base/dumpable.h +++ b/libartbase/base/dumpable.h @@ -14,13 +14,12 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_DUMPABLE_H_ -#define ART_RUNTIME_BASE_DUMPABLE_H_ +#ifndef ART_LIBARTBASE_BASE_DUMPABLE_H_ +#define ART_LIBARTBASE_BASE_DUMPABLE_H_ #include -#include "base/macros.h" -#include "base/mutex.h" +#include "macros.h" namespace art { @@ -51,27 +50,6 @@ std::ostream& operator<<(std::ostream& os, const Dumpable& rhs) { return os; } -template -class MutatorLockedDumpable { - public: - explicit MutatorLockedDumpable(T& value) REQUIRES_SHARED(Locks::mutator_lock_) : value_(value) {} - - void Dump(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_) { - value_.Dump(os); - } - - private: - const T& value_; - - DISALLOW_COPY_AND_ASSIGN(MutatorLockedDumpable); -}; - -template -std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable& rhs) - // TODO: should be REQUIRES_SHARED(Locks::mutator_lock_) however annotalysis - // currently fails for this. - NO_THREAD_SAFETY_ANALYSIS; - } // namespace art -#endif // ART_RUNTIME_BASE_DUMPABLE_H_ +#endif // ART_LIBARTBASE_BASE_DUMPABLE_H_ diff --git a/libartbase/base/file_magic.cc b/libartbase/base/file_magic.cc index 2b9bed0397385ce4851c1a18919e16222047885c..d8d843beeb6396c8dfbb55cdf2bcc414f02d150a 100644 --- a/libartbase/base/file_magic.cc +++ b/libartbase/base/file_magic.cc @@ -23,7 +23,7 @@ #include #include -#include "base/unix_file/fd_file.h" +#include "unix_file/fd_file.h" namespace art { diff --git a/libartbase/base/file_magic.h b/libartbase/base/file_magic.h index 53f551cb3a5488ce2acc55865a849494858f9823..0d0322c470adc213f292da82f1b309ee57e77583 100644 --- a/libartbase/base/file_magic.h +++ b/libartbase/base/file_magic.h @@ -20,7 +20,7 @@ #include #include -#include "base/os.h" +#include "os.h" namespace art { diff --git a/runtime/base/file_utils.cc b/libartbase/base/file_utils.cc similarity index 83% rename from runtime/base/file_utils.cc rename to libartbase/base/file_utils.cc index c3ba27080ab717c2b06e374a5f55cc1d13147f17..56934aca1f3a92f7eed829e41560bc187be95fd1 100644 --- a/runtime/base/file_utils.cc +++ b/libartbase/base/file_utils.cc @@ -43,11 +43,10 @@ #include "android-base/strings.h" #include "base/bit_utils.h" -#include "base/stl_util.h" +#include "base/globals.h" #include "base/os.h" +#include "base/stl_util.h" #include "base/unix_file/fd_file.h" -#include "dex/dex_file_loader.h" -#include "globals.h" #if defined(__APPLE__) #include @@ -64,6 +63,8 @@ namespace art { using android::base::StringAppendF; using android::base::StringPrintf; +static constexpr const char* kClassesDex = "classes.dex"; + bool ReadFileToString(const std::string& file_name, std::string* result) { File file(file_name, O_RDONLY, false); if (!file.IsOpened()) { @@ -83,59 +84,6 @@ bool ReadFileToString(const std::string& file_name, std::string* result) { } } -bool PrintFileToLog(const std::string& file_name, android::base::LogSeverity level) { - File file(file_name, O_RDONLY, false); - if (!file.IsOpened()) { - return false; - } - - constexpr size_t kBufSize = 256; // Small buffer. Avoid stack overflow and stack size warnings. - char buf[kBufSize + 1]; // +1 for terminator. - size_t filled_to = 0; - while (true) { - DCHECK_LT(filled_to, kBufSize); - int64_t n = TEMP_FAILURE_RETRY(read(file.Fd(), &buf[filled_to], kBufSize - filled_to)); - if (n <= 0) { - // Print the rest of the buffer, if it exists. - if (filled_to > 0) { - buf[filled_to] = 0; - LOG(level) << buf; - } - return n == 0; - } - // Scan for '\n'. - size_t i = filled_to; - bool found_newline = false; - for (; i < filled_to + n; ++i) { - if (buf[i] == '\n') { - // Found a line break, that's something to print now. - buf[i] = 0; - LOG(level) << buf; - // Copy the rest to the front. - if (i + 1 < filled_to + n) { - memmove(&buf[0], &buf[i + 1], filled_to + n - i - 1); - filled_to = filled_to + n - i - 1; - } else { - filled_to = 0; - } - found_newline = true; - break; - } - } - if (found_newline) { - continue; - } else { - filled_to += n; - // Check if we must flush now. - if (filled_to == kBufSize) { - buf[kBufSize] = 0; - LOG(level) << buf; - filled_to = 0; - } - } - } -} - std::string GetAndroidRootSafe(std::string* error_msg) { // Prefer ANDROID_ROOT if it's set. const char* android_dir = getenv("ANDROID_ROOT"); @@ -277,7 +225,7 @@ bool GetDalvikCacheFilename(const char* location, const char* cache_location, !android::base::EndsWith(location, ".art") && !android::base::EndsWith(location, ".oat")) { cache_file += "/"; - cache_file += DexFileLoader::kClassesDex; + cache_file += kClassesDex; } std::replace(cache_file.begin(), cache_file.end(), '/', '@'); *filename = StringPrintf("%s/%s", cache_location, cache_file.c_str()); @@ -306,8 +254,8 @@ std::string GetSystemImageFilename(const char* location, const InstructionSet is } std::string ReplaceFileExtension(const std::string& filename, const std::string& new_extension) { - const size_t last_ext = filename.find_last_of('.'); - if (last_ext == std::string::npos) { + const size_t last_ext = filename.find_last_of("./"); + if (last_ext == std::string::npos || filename[last_ext] != '.') { return filename + "." + new_extension; } else { return filename.substr(0, last_ext + 1) + new_extension; diff --git a/runtime/base/file_utils.h b/libartbase/base/file_utils.h similarity index 94% rename from runtime/base/file_utils.h rename to libartbase/base/file_utils.h index 8adb4f7bf8d669b4a2d98b78fc965351b464444c..063393bd3bda74c0f4d90cc60f0b2b31e8d93122 100644 --- a/runtime/base/file_utils.h +++ b/libartbase/base/file_utils.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_FILE_UTILS_H_ -#define ART_RUNTIME_BASE_FILE_UTILS_H_ +#ifndef ART_LIBARTBASE_BASE_FILE_UTILS_H_ +#define ART_LIBARTBASE_BASE_FILE_UTILS_H_ #include @@ -28,7 +28,6 @@ namespace art { bool ReadFileToString(const std::string& file_name, std::string* result); -bool PrintFileToLog(const std::string& file_name, android::base::LogSeverity level); // Find $ANDROID_ROOT, /system, or abort. std::string GetAndroidRoot(); @@ -47,6 +46,7 @@ std::string GetDefaultBootImageLocation(std::string* error_msg); // Returns the dalvik-cache location, with subdir appended. Returns the empty string if the cache // could not be found. std::string GetDalvikCache(const char* subdir); + // Return true if we found the dalvik cache and stored it in the dalvik_cache argument. // have_android_data will be set to true if we have an ANDROID_DATA that exists, // dalvik_cache_exists will be true if there is a dalvik-cache directory that is present. @@ -80,4 +80,4 @@ bool LocationIsOnSystemFramework(const char* location); } // namespace art -#endif // ART_RUNTIME_BASE_FILE_UTILS_H_ +#endif // ART_LIBARTBASE_BASE_FILE_UTILS_H_ diff --git a/runtime/base/file_utils_test.cc b/libartbase/base/file_utils_test.cc similarity index 83% rename from runtime/base/file_utils_test.cc rename to libartbase/base/file_utils_test.cc index cf6e34d1eabace69f41945525cd7a8f941465f09..2a7273b85e1cbbe245ac7fdd21948227428beef4 100644 --- a/runtime/base/file_utils_test.cc +++ b/libartbase/base/file_utils_test.cc @@ -20,11 +20,11 @@ #include #include "base/stl_util.h" -#include "common_runtime_test.h" +#include "common_art_test.h" namespace art { -class FileUtilsTest : public CommonRuntimeTest {}; +class FileUtilsTest : public CommonArtTest {}; TEST_F(FileUtilsTest, GetDalvikCacheFilename) { std::string name; @@ -63,22 +63,21 @@ TEST_F(FileUtilsTest, GetAndroidRootSafe) { // We don't expect null returns for most cases, so don't check and let std::string crash. - // CommonRuntimeTest sets ANDROID_ROOT, so expect this to be the same. + // CommonArtTest sets ANDROID_ROOT, so expect this to be the same. std::string android_root = GetAndroidRootSafe(&error_msg); std::string android_root_env = getenv("ANDROID_ROOT"); EXPECT_EQ(android_root, android_root_env); // Set ANDROID_ROOT to something else (but the directory must exist). So use dirname. - char* root_dup = strdup(android_root_env.c_str()); - char* dir = dirname(root_dup); + UniqueCPtr root_dup(strdup(android_root_env.c_str())); + char* dir = dirname(root_dup.get()); ASSERT_EQ(0, setenv("ANDROID_ROOT", dir, 1 /* overwrite */)); std::string android_root2 = GetAndroidRootSafe(&error_msg); EXPECT_STREQ(dir, android_root2.c_str()); - free(root_dup); // Set a bogus value for ANDROID_ROOT. This should be an error. ASSERT_EQ(0, setenv("ANDROID_ROOT", "/this/is/obviously/bogus", 1 /* overwrite */)); - EXPECT_TRUE(GetAndroidRootSafe(&error_msg) == nullptr); + EXPECT_EQ(GetAndroidRootSafe(&error_msg), ""); // Unset ANDROID_ROOT and see that it still returns something (as libart code is running). ASSERT_EQ(0, unsetenv("ANDROID_ROOT")); @@ -94,4 +93,11 @@ TEST_F(FileUtilsTest, GetAndroidRootSafe) { ASSERT_EQ(0, setenv("ANDROID_ROOT", android_root_env.c_str(), 1 /* overwrite */)); } +TEST_F(FileUtilsTest, ReplaceFileExtension) { + EXPECT_EQ("/directory/file.vdex", ReplaceFileExtension("/directory/file.oat", "vdex")); + EXPECT_EQ("/.directory/file.vdex", ReplaceFileExtension("/.directory/file.oat", "vdex")); + EXPECT_EQ("/directory/file.vdex", ReplaceFileExtension("/directory/file", "vdex")); + EXPECT_EQ("/.directory/file.vdex", ReplaceFileExtension("/.directory/file", "vdex")); +} + } // namespace art diff --git a/libartbase/base/fuchsia_compat.h b/libartbase/base/fuchsia_compat.h new file mode 100644 index 0000000000000000000000000000000000000000..018bac0528fbcad33dbcc09cf3c02963fbf703d1 --- /dev/null +++ b/libartbase/base/fuchsia_compat.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_LIBARTBASE_BASE_FUCHSIA_COMPAT_H_ +#define ART_LIBARTBASE_BASE_FUCHSIA_COMPAT_H_ + +// stubs for features lacking in Fuchsia + +struct rlimit { + int rlim_cur; +}; + +#define RLIMIT_FSIZE (1) +#define RLIM_INFINITY (-1) +static int getrlimit(int resource, struct rlimit *rlim) { + LOG(FATAL) << "getrlimit not available for Fuchsia"; +} + +static int ashmem_create_region(const char *name, size_t size) { + LOG(FATAL) << "ashmem_create_region not available for Fuchsia"; +} + +#endif // ART_LIBARTBASE_BASE_FUCHSIA_COMPAT_H_ diff --git a/libartbase/base/globals.h b/libartbase/base/globals.h index 69d1a64a3b8b2c7ab7dfe8328ee4f4131dcde2ec..cd0bf8fafc9371c1f2a9e42274cb13d3cd216bed 100644 --- a/libartbase/base/globals.h +++ b/libartbase/base/globals.h @@ -38,6 +38,9 @@ static constexpr size_t kStackAlignment = 16; // compile-time constant so the compiler can generate better code. static constexpr int kPageSize = 4096; +// Size of Dex virtual registers. +static constexpr size_t kVRegSize = 4; + // Returns whether the given memory offset can be used for generating // an implicit null check. static inline bool CanDoImplicitNullCheckOn(uintptr_t offset) { @@ -71,7 +74,9 @@ static constexpr bool kIsPGOInstrumentation = false; // ART_TARGET - Defined for target builds of ART. // ART_TARGET_LINUX - Defined for target Linux builds of ART. // ART_TARGET_ANDROID - Defined for target Android builds of ART. -// Note: Either ART_TARGET_LINUX or ART_TARGET_ANDROID need to be set when ART_TARGET is set. +// ART_TARGET_FUCHSIA - Defined for Fuchsia builds of ART. +// Note: Either ART_TARGET_LINUX, ART_TARGET_ANDROID or ART_TARGET_FUCHSIA +// need to be set when ART_TARGET is set. // Note: When ART_TARGET_LINUX is defined mem_map.h will not be using Ashmem for memory mappings // (usually only available on Android kernels). #if defined(ART_TARGET) @@ -79,10 +84,16 @@ static constexpr bool kIsPGOInstrumentation = false; static constexpr bool kIsTargetBuild = true; # if defined(ART_TARGET_LINUX) static constexpr bool kIsTargetLinux = true; +static constexpr bool kIsTargetFuchsia = false; # elif defined(ART_TARGET_ANDROID) static constexpr bool kIsTargetLinux = false; +static constexpr bool kIsTargetFuchsia = false; +# elif defined(ART_TARGET_FUCHSIA) +static constexpr bool kIsTargetLinux = false; +static constexpr bool kIsTargetFuchsia = true; # else -# error "Either ART_TARGET_LINUX or ART_TARGET_ANDROID needs to be defined for target builds." +# error "Either ART_TARGET_LINUX, ART_TARGET_ANDROID or ART_TARGET_FUCHSIA " \ + "needs to be defined for target builds." # endif #else static constexpr bool kIsTargetBuild = false; @@ -90,8 +101,11 @@ static constexpr bool kIsTargetBuild = false; # error "ART_TARGET_LINUX defined for host build." # elif defined(ART_TARGET_ANDROID) # error "ART_TARGET_ANDROID defined for host build." +# elif defined(ART_TARGET_FUCHSIA) +# error "ART_TARGET_FUCHSIA defined for host build." # else static constexpr bool kIsTargetLinux = false; +static constexpr bool kIsTargetFuchsia = false; # endif #endif diff --git a/libartbase/base/hash_map.h b/libartbase/base/hash_map.h index 0d7198c0f7711113d6d9103e6ef7bedfd30382b9..a3bb5b5550bf7f93eb2f72670f9f0d755377b60f 100644 --- a/libartbase/base/hash_map.h +++ b/libartbase/base/hash_map.h @@ -48,9 +48,12 @@ class HashMapWrapper { Fn fn_; }; -template , class Pred = std::equal_to, - class Alloc = std::allocator>> +template , + class Pred = DefaultPred, + class Alloc = std::allocator>> class HashMap : public HashSet, EmptyFn, HashMapWrapper, diff --git a/libartbase/base/hash_set.h b/libartbase/base/hash_set.h index 2f810eaadecde136dd009f347a75eeb364f746e7..2b1a5eb9474171feab3767719bbc865aac8ea82f 100644 --- a/libartbase/base/hash_set.h +++ b/libartbase/base/hash_set.h @@ -22,16 +22,94 @@ #include #include #include +#include #include #include #include +#include "base/data_hash.h" #include "bit_utils.h" #include "macros.h" namespace art { +template +class HashSetIterator : std::iterator { + public: + HashSetIterator(const HashSetIterator&) = default; + HashSetIterator(HashSetIterator&&) = default; + HashSetIterator(HashSetType* hash_set, size_t index) : index_(index), hash_set_(hash_set) {} + + // Conversion from iterator to const_iterator. + template ::value && + std::is_same::value>::type> + HashSetIterator(const HashSetIterator& other) + : index_(other.index_), hash_set_(other.hash_set_) {} + + HashSetIterator& operator=(const HashSetIterator&) = default; + HashSetIterator& operator=(HashSetIterator&&) = default; + + bool operator==(const HashSetIterator& other) const { + return hash_set_ == other.hash_set_ && this->index_ == other.index_; + } + + bool operator!=(const HashSetIterator& other) const { + return !(*this == other); + } + + HashSetIterator operator++() { // Value after modification. + this->index_ = hash_set_->NextNonEmptySlot(index_); + return *this; + } + + HashSetIterator operator++(int) { + HashSetIterator temp = *this; + ++*this; + return temp; + } + + Elem& operator*() const { + DCHECK(!hash_set_->IsFreeSlot(this->index_)); + return hash_set_->ElementForIndex(this->index_); + } + + Elem* operator->() const { + return &**this; + } + + private: + size_t index_; + HashSetType* hash_set_; + + template + friend bool operator==(const HashSetIterator& lhs, + const HashSetIterator& rhs); + template friend class HashSet; + template friend class HashSetIterator; +}; + +template +bool operator==(const HashSetIterator& lhs, + const HashSetIterator& rhs) { + static_assert( + std::is_convertible, + HashSetIterator>::value || + std::is_convertible, + HashSetIterator>::value, "Bad iterator types."); + DCHECK_EQ(lhs.hash_set_, rhs.hash_set_); + return lhs.index_ == rhs.index_; +} + +template +bool operator!=(const HashSetIterator& lhs, + const HashSetIterator& rhs) { + return !(lhs == rhs); +} + // Returns true if an item is empty. template class DefaultEmptyFn { @@ -55,70 +133,35 @@ class DefaultEmptyFn { } }; -// Low memory version of a hash set, uses less memory than std::unordered_set since elements aren't -// boxed. Uses linear probing to resolve collisions. +template +using DefaultHashFn = typename std::conditional::value, + DataHash, + std::hash>::type; + +struct DefaultStringEquals { + // Allow comparison with anything that can be compared to std::string, for example StringPiece. + template + bool operator()(const std::string& lhs, const T& rhs) const { + return lhs == rhs; + } +}; + +template +using DefaultPred = typename std::conditional::value, + DefaultStringEquals, + std::equal_to>::type; + +// Low memory version of a hash set, uses less memory than std::unordered_multiset since elements +// aren't boxed. Uses linear probing to resolve collisions. // EmptyFn needs to implement two functions MakeEmpty(T& item) and IsEmpty(const T& item). // TODO: We could get rid of this requirement by using a bitmap, though maybe this would be slower // and more complicated. -template , class HashFn = std::hash, - class Pred = std::equal_to, class Alloc = std::allocator> +template , + class HashFn = DefaultHashFn, + class Pred = DefaultPred, + class Alloc = std::allocator> class HashSet { - template - class BaseIterator : std::iterator { - public: - BaseIterator(const BaseIterator&) = default; - BaseIterator(BaseIterator&&) = default; - BaseIterator(HashSetType* hash_set, size_t index) : index_(index), hash_set_(hash_set) { - } - BaseIterator& operator=(const BaseIterator&) = default; - BaseIterator& operator=(BaseIterator&&) = default; - - bool operator==(const BaseIterator& other) const { - return hash_set_ == other.hash_set_ && this->index_ == other.index_; - } - - bool operator!=(const BaseIterator& other) const { - return !(*this == other); - } - - BaseIterator operator++() { // Value after modification. - this->index_ = this->NextNonEmptySlot(this->index_, hash_set_); - return *this; - } - - BaseIterator operator++(int) { - BaseIterator temp = *this; - this->index_ = this->NextNonEmptySlot(this->index_, hash_set_); - return temp; - } - - Elem& operator*() const { - DCHECK(!hash_set_->IsFreeSlot(this->index_)); - return hash_set_->ElementForIndex(this->index_); - } - - Elem* operator->() const { - return &**this; - } - - // TODO: Operator -- --(int) (and use std::bidirectional_iterator_tag) - - private: - size_t index_; - HashSetType* hash_set_; - - size_t NextNonEmptySlot(size_t index, const HashSet* hash_set) const { - const size_t num_buckets = hash_set->NumBuckets(); - DCHECK_LT(index, num_buckets); - do { - ++index; - } while (index < num_buckets && hash_set->IsFreeSlot(index)); - return index; - } - - friend class HashSet; - }; - public: using value_type = T; using allocator_type = Alloc; @@ -126,8 +169,8 @@ class HashSet { using const_reference = const T&; using pointer = T*; using const_pointer = const T*; - using iterator = BaseIterator; - using const_iterator = BaseIterator; + using iterator = HashSetIterator; + using const_iterator = HashSetIterator; using size_type = size_t; using difference_type = ptrdiff_t; @@ -136,7 +179,7 @@ class HashSet { static constexpr size_t kMinBuckets = 1000; // If we don't own the data, this will create a new array which owns the data. - void Clear() { + void clear() { DeallocateStorage(); num_elements_ = 0; elements_until_expand_ = 0; @@ -300,13 +343,12 @@ class HashSet { return const_iterator(this, NumBuckets()); } - bool Empty() const { - return Size() == 0; + size_t size() const { + return num_elements_; } - // Return true if the hash set has ownership of the underlying data. - bool OwnsData() const { - return owns_data_; + bool empty() const { + return size() == 0; } // Erase algorithm: @@ -317,7 +359,7 @@ class HashSet { // and set the empty slot to be the location we just moved from. // Relies on maintaining the invariant that there's no empty slots from the 'ideal' index of an // element to its actual location/index. - iterator Erase(iterator it) { + iterator erase(iterator it) { // empty_index is the index that will become empty. size_t empty_index = it.index_; DCHECK(!IsFreeSlot(empty_index)); @@ -368,12 +410,12 @@ class HashSet { // Set of Class* sorted by name, want to find a class with a name but can't allocate a dummy // object in the heap for performance solution. template - iterator Find(const K& key) { + iterator find(const K& key) { return FindWithHash(key, hashfn_(key)); } template - const_iterator Find(const K& key) const { + const_iterator find(const K& key) const { return FindWithHash(key, hashfn_(key)); } @@ -387,14 +429,26 @@ class HashSet { return const_iterator(this, FindIndex(key, hash)); } + // Insert an element with hint, allows duplicates. + // Note: The hint is not very useful for a HashSet<> unless there are many hash conflicts + // and in that case the use of HashSet<> itself should be reconsidered. + iterator insert(const_iterator hint ATTRIBUTE_UNUSED, const T& element) { + return insert(element); + } + iterator insert(const_iterator hint ATTRIBUTE_UNUSED, T&& element) { + return insert(std::move(element)); + } + // Insert an element, allows duplicates. - template ::value>::type> - void Insert(U&& element) { - InsertWithHash(std::forward(element), hashfn_(element)); + iterator insert(const T& element) { + return InsertWithHash(element, hashfn_(element)); + } + iterator insert(T&& element) { + return InsertWithHash(std::move(element), hashfn_(element)); } template ::value>::type> - void InsertWithHash(U&& element, size_t hash) { + iterator InsertWithHash(U&& element, size_t hash) { DCHECK_EQ(hash, hashfn_(element)); if (num_elements_ >= elements_until_expand_) { Expand(); @@ -403,10 +457,7 @@ class HashSet { const size_t index = FirstAvailableSlot(IndexForHash(hash)); data_[index] = std::forward(element); ++num_elements_; - } - - size_t Size() const { - return num_elements_; + return iterator(this, index); } void swap(HashSet& other) { @@ -430,12 +481,12 @@ class HashSet { } void ShrinkToMaximumLoad() { - Resize(Size() / max_load_factor_); + Resize(size() / max_load_factor_); } // Reserve enough room to insert until Size() == num_elements without requiring to grow the hash // set. No-op if the hash set is already large enough to do this. - void Reserve(size_t num_elements) { + void reserve(size_t num_elements) { size_t num_buckets = num_elements / max_load_factor_; // Deal with rounding errors. Add one for rounding. while (static_cast(num_buckets * max_load_factor_) <= num_elements + 1u) { @@ -466,7 +517,7 @@ class HashSet { // Calculate the current load factor and return it. double CalculateLoadFactor() const { - return static_cast(Size()) / static_cast(NumBuckets()); + return static_cast(size()) / static_cast(NumBuckets()); } // Make sure that everything reinserts in the right spot. Returns the number of errors. @@ -510,7 +561,7 @@ class HashSet { // maximum load factor. const double load_factor = CalculateLoadFactor(); if (load_factor > max_load_factor_) { - Resize(Size() / ((min_load_factor_ + max_load_factor_) * 0.5)); + Resize(size() / ((min_load_factor_ + max_load_factor_) * 0.5)); } } @@ -605,7 +656,7 @@ class HashSet { // Expand the set based on the load factors. void Expand() { - size_t min_index = static_cast(Size() / min_load_factor_); + size_t min_index = static_cast(size() / min_load_factor_); // Resize based on the minimum load factor. Resize(min_index); } @@ -615,7 +666,7 @@ class HashSet { if (new_size < kMinBuckets) { new_size = kMinBuckets; } - DCHECK_GE(new_size, Size()); + DCHECK_GE(new_size, size()); T* const old_data = data_; size_t old_num_buckets = num_buckets_; // Reinsert all of the old elements. @@ -649,6 +700,15 @@ class HashSet { return index; } + size_t NextNonEmptySlot(size_t index) const { + const size_t num_buckets = NumBuckets(); + DCHECK_LT(index, num_buckets); + do { + ++index; + } while (index < num_buckets && IsFreeSlot(index)); + return index; + } + // Return new offset. template static size_t WriteToBytes(uint8_t* ptr, size_t offset, Elem n) { @@ -679,6 +739,9 @@ class HashSet { double min_load_factor_; double max_load_factor_; + template + friend class HashSetIterator; + ART_FRIEND_TEST(InternTableTest, CrossHash); }; diff --git a/libartbase/base/hash_set_test.cc b/libartbase/base/hash_set_test.cc index ff745b4be57053672ea7fa2f20db3affeef031b1..782a68b5d5b634e5f5bf2854ebd5fa5c463cd5bb 100644 --- a/libartbase/base/hash_set_test.cc +++ b/libartbase/base/hash_set_test.cc @@ -24,6 +24,8 @@ #include #include + +#include "base/stringpiece.h" #include "hash_map.h" namespace art { @@ -66,16 +68,16 @@ class HashSetTest : public testing::Test { TEST_F(HashSetTest, TestSmoke) { HashSet hash_set; const std::string test_string = "hello world 1234"; - ASSERT_TRUE(hash_set.Empty()); - ASSERT_EQ(hash_set.Size(), 0U); - hash_set.Insert(test_string); - auto it = hash_set.Find(test_string); + ASSERT_TRUE(hash_set.empty()); + ASSERT_EQ(hash_set.size(), 0U); + hash_set.insert(test_string); + auto it = hash_set.find(test_string); ASSERT_EQ(*it, test_string); - auto after_it = hash_set.Erase(it); + auto after_it = hash_set.erase(it); ASSERT_TRUE(after_it == hash_set.end()); - ASSERT_TRUE(hash_set.Empty()); - ASSERT_EQ(hash_set.Size(), 0U); - it = hash_set.Find(test_string); + ASSERT_TRUE(hash_set.empty()); + ASSERT_EQ(hash_set.size(), 0U); + it = hash_set.find(test_string); ASSERT_TRUE(it == hash_set.end()); } @@ -86,26 +88,26 @@ TEST_F(HashSetTest, TestInsertAndErase) { for (size_t i = 0; i < count; ++i) { // Insert a bunch of elements and make sure we can find them. strings.push_back(RandomString(10)); - hash_set.Insert(strings[i]); - auto it = hash_set.Find(strings[i]); + hash_set.insert(strings[i]); + auto it = hash_set.find(strings[i]); ASSERT_TRUE(it != hash_set.end()); ASSERT_EQ(*it, strings[i]); } - ASSERT_EQ(strings.size(), hash_set.Size()); + ASSERT_EQ(strings.size(), hash_set.size()); // Try to erase the odd strings. for (size_t i = 1; i < count; i += 2) { - auto it = hash_set.Find(strings[i]); + auto it = hash_set.find(strings[i]); ASSERT_TRUE(it != hash_set.end()); ASSERT_EQ(*it, strings[i]); - hash_set.Erase(it); + hash_set.erase(it); } // Test removed. for (size_t i = 1; i < count; i += 2) { - auto it = hash_set.Find(strings[i]); + auto it = hash_set.find(strings[i]); ASSERT_TRUE(it == hash_set.end()); } for (size_t i = 0; i < count; i += 2) { - auto it = hash_set.Find(strings[i]); + auto it = hash_set.find(strings[i]); ASSERT_TRUE(it != hash_set.end()); ASSERT_EQ(*it, strings[i]); } @@ -119,7 +121,7 @@ TEST_F(HashSetTest, TestIterator) { for (size_t i = 0; i < count; ++i) { // Insert a bunch of elements and make sure we can find them. strings.push_back(RandomString(10)); - hash_set.Insert(strings[i]); + hash_set.insert(strings[i]); } // Make sure we visit each string exactly once. std::map found_count; @@ -133,7 +135,7 @@ TEST_F(HashSetTest, TestIterator) { // Remove all the elements with iterator erase. for (auto it = hash_set.begin(); it != hash_set.end();) { ++found_count[*it]; - it = hash_set.Erase(it); + it = hash_set.erase(it); ASSERT_EQ(hash_set.Verify(), 0U); } for (size_t i = 0; i < count; ++i) { @@ -147,14 +149,14 @@ TEST_F(HashSetTest, TestSwap) { static constexpr size_t count = 1000; for (size_t i = 0; i < count; ++i) { strings.push_back(RandomString(10)); - hash_seta.Insert(strings[i]); + hash_seta.insert(strings[i]); } std::swap(hash_seta, hash_setb); - hash_seta.Insert("TEST"); - hash_setb.Insert("TEST2"); + hash_seta.insert("TEST"); + hash_setb.insert("TEST2"); for (size_t i = 0; i < count; ++i) { strings.push_back(RandomString(10)); - hash_seta.Insert(strings[i]); + hash_seta.insert(strings[i]); } } @@ -163,7 +165,7 @@ TEST_F(HashSetTest, TestShrink) { std::vector strings = {"a", "b", "c", "d", "e", "f", "g"}; for (size_t i = 0; i < strings.size(); ++i) { // Insert some strings into the beginning of our hash set to establish an initial size - hash_set.Insert(strings[i]); + hash_set.insert(strings[i]); } hash_set.ShrinkToMaximumLoad(); @@ -174,12 +176,12 @@ TEST_F(HashSetTest, TestShrink) { static constexpr size_t count = 1000; for (size_t i = 0; i < count; ++i) { random_strings.push_back(RandomString(10)); - hash_set.Insert(random_strings[i]); + hash_set.insert(random_strings[i]); } // Erase all the extra strings which guarantees that our load factor will be really bad. for (size_t i = 0; i < count; ++i) { - hash_set.Erase(hash_set.Find(random_strings[i])); + hash_set.erase(hash_set.find(random_strings[i])); } const double bad_load = hash_set.CalculateLoadFactor(); @@ -191,7 +193,7 @@ TEST_F(HashSetTest, TestShrink) { // Make sure all the initial elements we had are still there for (const std::string& initial_string : strings) { - EXPECT_NE(hash_set.end(), hash_set.Find(initial_string)) + EXPECT_NE(hash_set.end(), hash_set.find(initial_string)) << "expected to find " << initial_string; } } @@ -201,7 +203,7 @@ TEST_F(HashSetTest, TestLoadFactor) { static constexpr size_t kStringCount = 1000; static constexpr double kEpsilon = 0.01; for (size_t i = 0; i < kStringCount; ++i) { - hash_set.Insert(RandomString(i % 10 + 1)); + hash_set.insert(RandomString(i % 10 + 1)); } // Check that changing the load factor resizes the table to be within the target range. EXPECT_GE(hash_set.CalculateLoadFactor() + kEpsilon, hash_set.GetMinLoadFactor()); @@ -228,29 +230,29 @@ TEST_F(HashSetTest, TestStress) { SetSeed(seed); LOG(INFO) << "Starting stress test with seed " << seed; for (size_t i = 0; i < operations; ++i) { - ASSERT_EQ(hash_set.Size(), std_set.size()); + ASSERT_EQ(hash_set.size(), std_set.size()); size_t delta = std::abs(static_cast(target_size) - - static_cast(hash_set.Size())); + static_cast(hash_set.size())); size_t n = PRand(); if (n % target_size == 0) { - hash_set.Clear(); + hash_set.clear(); std_set.clear(); - ASSERT_TRUE(hash_set.Empty()); + ASSERT_TRUE(hash_set.empty()); ASSERT_TRUE(std_set.empty()); } else if (n % target_size < delta) { // Skew towards adding elements until we are at the desired size. const std::string& s = strings[PRand() % string_count]; - hash_set.Insert(s); + hash_set.insert(s); std_set.insert(s); - ASSERT_EQ(*hash_set.Find(s), *std_set.find(s)); + ASSERT_EQ(*hash_set.find(s), *std_set.find(s)); } else { const std::string& s = strings[PRand() % string_count]; - auto it1 = hash_set.Find(s); + auto it1 = hash_set.find(s); auto it2 = std_set.find(s); ASSERT_EQ(it1 == hash_set.end(), it2 == std_set.end()); if (it1 != hash_set.end()) { ASSERT_EQ(*it1, *it2); - hash_set.Erase(it1); + hash_set.erase(it1); std_set.erase(it2); } } @@ -268,13 +270,13 @@ struct IsEmptyStringPair { TEST_F(HashSetTest, TestHashMap) { HashMap hash_map; - hash_map.Insert(std::make_pair(std::string("abcd"), 123)); - hash_map.Insert(std::make_pair(std::string("abcd"), 124)); - hash_map.Insert(std::make_pair(std::string("bags"), 444)); - auto it = hash_map.Find(std::string("abcd")); + hash_map.insert(std::make_pair(std::string("abcd"), 123)); + hash_map.insert(std::make_pair(std::string("abcd"), 124)); + hash_map.insert(std::make_pair(std::string("bags"), 444)); + auto it = hash_map.find(std::string("abcd")); ASSERT_EQ(it->second, 123); - hash_map.Erase(it); - it = hash_map.Find(std::string("abcd")); + hash_map.erase(it); + it = hash_map.find(std::string("abcd")); ASSERT_EQ(it->second, 124); } @@ -325,33 +327,50 @@ struct VectorIntHashEquals { TEST_F(HashSetTest, TestLookupByAlternateKeyType) { HashSet, IsEmptyFnVectorInt, VectorIntHashEquals, VectorIntHashEquals> hash_set; - hash_set.Insert(std::vector({1, 2, 3, 4})); - hash_set.Insert(std::vector({4, 2})); - ASSERT_EQ(hash_set.end(), hash_set.Find(std::vector({1, 1, 1, 1}))); - ASSERT_NE(hash_set.end(), hash_set.Find(std::vector({1, 2, 3, 4}))); - ASSERT_EQ(hash_set.end(), hash_set.Find(std::forward_list({1, 1, 1, 1}))); - ASSERT_NE(hash_set.end(), hash_set.Find(std::forward_list({1, 2, 3, 4}))); + hash_set.insert(std::vector({1, 2, 3, 4})); + hash_set.insert(std::vector({4, 2})); + ASSERT_EQ(hash_set.end(), hash_set.find(std::vector({1, 1, 1, 1}))); + ASSERT_NE(hash_set.end(), hash_set.find(std::vector({1, 2, 3, 4}))); + ASSERT_EQ(hash_set.end(), hash_set.find(std::forward_list({1, 1, 1, 1}))); + ASSERT_NE(hash_set.end(), hash_set.find(std::forward_list({1, 2, 3, 4}))); } TEST_F(HashSetTest, TestReserve) { HashSet hash_set; std::vector sizes = {1, 10, 25, 55, 128, 1024, 4096}; for (size_t size : sizes) { - hash_set.Reserve(size); + hash_set.reserve(size); const size_t buckets_before = hash_set.NumBuckets(); // Check that we expanded enough. CHECK_GE(hash_set.ElementsUntilExpand(), size); // Try inserting elements until we are at our reserve size and ensure the hash set did not // expand. - while (hash_set.Size() < size) { - hash_set.Insert(std::to_string(hash_set.Size())); + while (hash_set.size() < size) { + hash_set.insert(std::to_string(hash_set.size())); } CHECK_EQ(hash_set.NumBuckets(), buckets_before); } // Check the behaviour for shrinking, it does not necessarily resize down. constexpr size_t size = 100; - hash_set.Reserve(size); + hash_set.reserve(size); CHECK_GE(hash_set.ElementsUntilExpand(), size); } +TEST_F(HashSetTest, IteratorConversion) { + const char* test_string = "dummy"; + HashSet hash_set; + HashSet::iterator it = hash_set.insert(test_string); + HashSet::const_iterator cit = it; + ASSERT_TRUE(it == cit); + ASSERT_EQ(*it, *cit); +} + +TEST_F(HashSetTest, StringSearchyStringPiece) { + const char* test_string = "dummy"; + HashSet hash_set; + HashSet::iterator insert_pos = hash_set.insert(test_string); + HashSet::iterator it = hash_set.find(StringPiece(test_string)); + ASSERT_TRUE(it == insert_pos); +} + } // namespace art diff --git a/libartbase/base/hex_dump.h b/libartbase/base/hex_dump.h index 55f4d53c2e93c13a52ad70cf2636c5cab00a3093..d13595dc0260f46aff4459925a3070b4cc9d3320 100644 --- a/libartbase/base/hex_dump.h +++ b/libartbase/base/hex_dump.h @@ -17,7 +17,7 @@ #ifndef ART_LIBARTBASE_BASE_HEX_DUMP_H_ #define ART_LIBARTBASE_BASE_HEX_DUMP_H_ -#include "base/macros.h" +#include "macros.h" #include diff --git a/libartbase/base/histogram-inl.h b/libartbase/base/histogram-inl.h index 26d01b2883a482de2d663a1235a1929bade772a2..9832f03d90657e8796d9e82bca4b7878f97c47ad 100644 --- a/libartbase/base/histogram-inl.h +++ b/libartbase/base/histogram-inl.h @@ -26,9 +26,9 @@ #include -#include "base/bit_utils.h" -#include "base/time_utils.h" -#include "base/utils.h" +#include "bit_utils.h" +#include "time_utils.h" +#include "utils.h" namespace art { diff --git a/runtime/indenter.h b/libartbase/base/indenter.h similarity index 94% rename from runtime/indenter.h rename to libartbase/base/indenter.h index 6361dd2092c71fa2e9f28a6dfa62bef5ab121af5..a479b7d6509d34b2fc4efaff8a9a1500c24a7fd0 100644 --- a/runtime/indenter.h +++ b/libartbase/base/indenter.h @@ -14,15 +14,15 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_INDENTER_H_ -#define ART_RUNTIME_INDENTER_H_ +#ifndef ART_LIBARTBASE_BASE_INDENTER_H_ +#define ART_LIBARTBASE_BASE_INDENTER_H_ #include #include #include -#include "base/macros.h" +#include "macros.h" namespace art { @@ -122,6 +122,10 @@ class VariableIndentationOutputStream { return indented_os_; } + size_t GetIndentation() const { + return indenter_.count_; + } + void IncreaseIndentation(size_t adjustment) { indenter_.count_ += adjustment; } @@ -160,4 +164,4 @@ class ScopedIndentation { } // namespace art -#endif // ART_RUNTIME_INDENTER_H_ +#endif // ART_LIBARTBASE_BASE_INDENTER_H_ diff --git a/runtime/indenter_test.cc b/libartbase/base/indenter_test.cc similarity index 100% rename from runtime/indenter_test.cc rename to libartbase/base/indenter_test.cc diff --git a/libartbase/base/iteration_range.h b/libartbase/base/iteration_range.h index 76049a7e4d513805e7ff372f990f464c870dc295..cd87d85f686958773908017e55ba6617f0ad2dca 100644 --- a/libartbase/base/iteration_range.h +++ b/libartbase/base/iteration_range.h @@ -39,9 +39,9 @@ class IterationRange { iterator cbegin() const { return first_; } iterator cend() const { return last_; } - private: - const iterator first_; - const iterator last_; + protected: + iterator first_; + iterator last_; }; template diff --git a/libartbase/base/leb128.h b/libartbase/base/leb128.h index ab19daa108ddc5574a8d531381869393e3933958..d5847fd6c6fd5e9a5cc21f3b79b6b54c4fca655c 100644 --- a/libartbase/base/leb128.h +++ b/libartbase/base/leb128.h @@ -21,9 +21,9 @@ #include -#include "base/bit_utils.h" -#include "base/globals.h" -#include "base/macros.h" +#include "bit_utils.h" +#include "globals.h" +#include "macros.h" namespace art { diff --git a/libartbase/base/leb128_test.cc b/libartbase/base/leb128_test.cc index 747fc19f5da264be38755a79b88dbeb6444ee6e9..58b0d077864196d3fc4e2c07b000401f5e6da9ca 100644 --- a/libartbase/base/leb128_test.cc +++ b/libartbase/base/leb128_test.cc @@ -17,9 +17,8 @@ #include "leb128.h" #include "gtest/gtest.h" - -#include "base/histogram-inl.h" -#include "base/time_utils.h" +#include "histogram-inl.h" +#include "time_utils.h" namespace art { diff --git a/libartbase/base/length_prefixed_array.h b/libartbase/base/length_prefixed_array.h index 7c09bddd554bc409140e7b8462021fa98e6f92e1..9238e81566057410c525fea8b730752902accd58 100644 --- a/libartbase/base/length_prefixed_array.h +++ b/libartbase/base/length_prefixed_array.h @@ -20,10 +20,10 @@ #include // for offsetof() #include // for memset() -#include "base/bit_utils.h" -#include "base/casts.h" -#include "base/iteration_range.h" -#include "base/stride_iterator.h" +#include "bit_utils.h" +#include "casts.h" +#include "iteration_range.h" +#include "stride_iterator.h" namespace art { diff --git a/libartbase/base/logging.cc b/libartbase/base/logging.cc index 37b1f8264a7a2b0caed4b8ab3e069e4e15b058e8..a66a7e363546fc889fd7a4d3eded02520e180518 100644 --- a/libartbase/base/logging.cc +++ b/libartbase/base/logging.cc @@ -21,6 +21,8 @@ #include #include "aborting.h" +#include "os.h" +#include "unix_file/fd_file.h" // Headers for LogMessage::LogLine. #ifdef ART_TARGET_ANDROID @@ -140,4 +142,57 @@ void LogHelper::LogLineLowStack(const char* file, #endif // ART_TARGET_ANDROID } +bool PrintFileToLog(const std::string& file_name, android::base::LogSeverity level) { + File file(file_name, O_RDONLY, false); + if (!file.IsOpened()) { + return false; + } + + constexpr size_t kBufSize = 256; // Small buffer. Avoid stack overflow and stack size warnings. + char buf[kBufSize + 1]; // +1 for terminator. + size_t filled_to = 0; + while (true) { + DCHECK_LT(filled_to, kBufSize); + int64_t n = TEMP_FAILURE_RETRY(read(file.Fd(), &buf[filled_to], kBufSize - filled_to)); + if (n <= 0) { + // Print the rest of the buffer, if it exists. + if (filled_to > 0) { + buf[filled_to] = 0; + LOG(level) << buf; + } + return n == 0; + } + // Scan for '\n'. + size_t i = filled_to; + bool found_newline = false; + for (; i < filled_to + n; ++i) { + if (buf[i] == '\n') { + // Found a line break, that's something to print now. + buf[i] = 0; + LOG(level) << buf; + // Copy the rest to the front. + if (i + 1 < filled_to + n) { + memmove(&buf[0], &buf[i + 1], filled_to + n - i - 1); + filled_to = filled_to + n - i - 1; + } else { + filled_to = 0; + } + found_newline = true; + break; + } + } + if (found_newline) { + continue; + } else { + filled_to += n; + // Check if we must flush now. + if (filled_to == kBufSize) { + buf[kBufSize] = 0; + LOG(level) << buf; + filled_to = 0; + } + } + } +} + } // namespace art diff --git a/libartbase/base/logging.h b/libartbase/base/logging.h index fd5fc743832cf954ecb152f045bf128f31f5fe65..d2c0a0239987f302507b7cf1d0d9771aa3e8df77 100644 --- a/libartbase/base/logging.h +++ b/libartbase/base/logging.h @@ -21,7 +21,7 @@ #include #include "android-base/logging.h" -#include "base/macros.h" +#include "macros.h" namespace art { @@ -98,6 +98,9 @@ class LogHelper { DISALLOW_COPY_AND_ASSIGN(LogHelper); }; +// Copy the contents of file_name to the log stream for level. +bool PrintFileToLog(const std::string& file_name, android::base::LogSeverity level); + // Is verbose logging enabled for the given module? Where the module is defined in LogVerbosity. #define VLOG_IS_ON(module) UNLIKELY(::art::gLogVerbosity.module) diff --git a/libartbase/base/logging_test.cc b/libartbase/base/logging_test.cc index 404e080b03cb8822b4d3aac06b9db1b5a202e73a..46ba41bb1f184d7565e7b0726d1355b9c234033e 100644 --- a/libartbase/base/logging_test.cc +++ b/libartbase/base/logging_test.cc @@ -19,9 +19,9 @@ #include #include "android-base/logging.h" -#include "base/bit_utils.h" -#include "base/macros.h" -#include "common_runtime_test.h" +#include "bit_utils.h" +#include "gtest/gtest.h" +#include "macros.h" #include "runtime_debug.h" namespace art { @@ -31,9 +31,9 @@ static void SimpleAborter(const char* msg) { _exit(1); } -class LoggingTest : public CommonRuntimeTest { +class LoggingTest : public testing::Test { protected: - void PostRuntimeCreate() OVERRIDE { + LoggingTest() { // In our abort tests we really don't want the runtime to create a real dump. android::base::SetAborter(SimpleAborter); } diff --git a/libartbase/base/macros.h b/libartbase/base/macros.h index 6dd981dc9ef73e12d1a4c4cd6f1bc48c1fffe636..f26cf0708b29d33abef4d631c11beb794d1304e7 100644 --- a/libartbase/base/macros.h +++ b/libartbase/base/macros.h @@ -45,8 +45,6 @@ template ART_FRIEND_TEST(test_set_name, individual_test) private: \ void* operator new(size_t) = delete // NOLINT -#define SIZEOF_MEMBER(t, f) sizeof((reinterpret_cast(4096))->f) // NOLINT - #define OFFSETOF_MEMBER(t, f) \ (reinterpret_cast(&reinterpret_cast(16)->f) - static_cast(16u)) // NOLINT diff --git a/libartbase/base/malloc_arena_pool.cc b/libartbase/base/malloc_arena_pool.cc new file mode 100644 index 0000000000000000000000000000000000000000..15a5d71a6b51fbf5232569016cfc858d6c1e607a --- /dev/null +++ b/libartbase/base/malloc_arena_pool.cc @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "malloc_arena_pool.h" + +#include + +#include +#include +#include +#include + +#include +#include "arena_allocator-inl.h" + +namespace art { + +class MallocArena FINAL : public Arena { + public: + explicit MallocArena(size_t size = arena_allocator::kArenaDefaultSize); + virtual ~MallocArena(); + private: + static constexpr size_t RequiredOverallocation() { + return (alignof(std::max_align_t) < ArenaAllocator::kArenaAlignment) + ? ArenaAllocator::kArenaAlignment - alignof(std::max_align_t) + : 0u; + } + + uint8_t* unaligned_memory_; +}; + +MallocArena::MallocArena(size_t size) { + // We need to guarantee kArenaAlignment aligned allocation for the new arena. + // TODO: Use std::aligned_alloc() when it becomes available with C++17. + constexpr size_t overallocation = RequiredOverallocation(); + unaligned_memory_ = reinterpret_cast(calloc(1, size + overallocation)); + CHECK(unaligned_memory_ != nullptr); // Abort on OOM. + DCHECK_ALIGNED(unaligned_memory_, alignof(std::max_align_t)); + if (overallocation == 0u) { + memory_ = unaligned_memory_; + } else { + memory_ = AlignUp(unaligned_memory_, ArenaAllocator::kArenaAlignment); + if (kRunningOnMemoryTool) { + size_t head = memory_ - unaligned_memory_; + size_t tail = overallocation - head; + MEMORY_TOOL_MAKE_NOACCESS(unaligned_memory_, head); + MEMORY_TOOL_MAKE_NOACCESS(memory_ + size, tail); + } + } + DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment); + size_ = size; +} + +MallocArena::~MallocArena() { + constexpr size_t overallocation = RequiredOverallocation(); + if (overallocation != 0u && kRunningOnMemoryTool) { + size_t head = memory_ - unaligned_memory_; + size_t tail = overallocation - head; + MEMORY_TOOL_MAKE_UNDEFINED(unaligned_memory_, head); + MEMORY_TOOL_MAKE_UNDEFINED(memory_ + size_, tail); + } + free(reinterpret_cast(unaligned_memory_)); +} + +void Arena::Reset() { + if (bytes_allocated_ > 0) { + memset(Begin(), 0, bytes_allocated_); + bytes_allocated_ = 0; + } +} + +MallocArenaPool::MallocArenaPool() : free_arenas_(nullptr) { +} + +MallocArenaPool::~MallocArenaPool() { + ReclaimMemory(); +} + +void MallocArenaPool::ReclaimMemory() { + while (free_arenas_ != nullptr) { + Arena* arena = free_arenas_; + free_arenas_ = free_arenas_->next_; + delete arena; + } +} + +void MallocArenaPool::LockReclaimMemory() { + std::lock_guard lock(lock_); + ReclaimMemory(); +} + +Arena* MallocArenaPool::AllocArena(size_t size) { + Arena* ret = nullptr; + { + std::lock_guard lock(lock_); + if (free_arenas_ != nullptr && LIKELY(free_arenas_->Size() >= size)) { + ret = free_arenas_; + free_arenas_ = free_arenas_->next_; + } + } + if (ret == nullptr) { + ret = new MallocArena(size); + } + ret->Reset(); + return ret; +} + +void MallocArenaPool::TrimMaps() { + // Nop, because there is no way to do madvise here. +} + +size_t MallocArenaPool::GetBytesAllocated() const { + size_t total = 0; + std::lock_guard lock(lock_); + for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) { + total += arena->GetBytesAllocated(); + } + return total; +} + +void MallocArenaPool::FreeArenaChain(Arena* first) { + if (kRunningOnMemoryTool) { + for (Arena* arena = first; arena != nullptr; arena = arena->next_) { + MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_); + } + } + + if (arena_allocator::kArenaAllocatorPreciseTracking) { + // Do not reuse arenas when tracking. + while (first != nullptr) { + Arena* next = first->next_; + delete first; + first = next; + } + return; + } + + if (first != nullptr) { + Arena* last = first; + while (last->next_ != nullptr) { + last = last->next_; + } + std::lock_guard lock(lock_); + last->next_ = free_arenas_; + free_arenas_ = first; + } +} + +} // namespace art diff --git a/libartbase/base/malloc_arena_pool.h b/libartbase/base/malloc_arena_pool.h new file mode 100644 index 0000000000000000000000000000000000000000..c48be59eb5ad72f2af30a69557c1006ca3598cf4 --- /dev/null +++ b/libartbase/base/malloc_arena_pool.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_LIBARTBASE_BASE_MALLOC_ARENA_POOL_H_ +#define ART_LIBARTBASE_BASE_MALLOC_ARENA_POOL_H_ + +#include + +#include "arena_allocator.h" + +namespace art { + +class MallocArenaPool FINAL : public ArenaPool { + public: + MallocArenaPool(); + ~MallocArenaPool(); + Arena* AllocArena(size_t size) OVERRIDE; + void FreeArenaChain(Arena* first) OVERRIDE; + size_t GetBytesAllocated() const OVERRIDE; + void ReclaimMemory() OVERRIDE; + void LockReclaimMemory() OVERRIDE; + // Is a nop for malloc pools. + void TrimMaps() OVERRIDE; + + private: + Arena* free_arenas_; + // Use a std::mutex here as Arenas are at the bottom of the lock hierarchy when malloc is used. + mutable std::mutex lock_; + + DISALLOW_COPY_AND_ASSIGN(MallocArenaPool); +}; + +} // namespace art + +#endif // ART_LIBARTBASE_BASE_MALLOC_ARENA_POOL_H_ diff --git a/runtime/mem_map.cc b/libartbase/base/mem_map.cc similarity index 87% rename from runtime/mem_map.cc rename to libartbase/base/mem_map.cc index b9d51c112528685bb4f90c51d676a93eea73f42a..5cea86951907c10f8f71c15cc30b09998ee0de1b 100644 --- a/runtime/mem_map.cc +++ b/libartbase/base/mem_map.cc @@ -19,7 +19,7 @@ #include #include #include // For the PROT_* and MAP_* constants. -#ifndef ANDROID_OS +#if !defined(ANDROID_OS) && !defined(__Fuchsia__) #include #endif @@ -29,16 +29,19 @@ #include "android-base/stringprintf.h" #include "android-base/unique_fd.h" -#include "backtrace/BacktraceMap.h" + +#if !defined(__Fuchsia__) #include "cutils/ashmem.h" +#else +#include "fuchsia_compat.h" +#endif -#include "base/allocator.h" -#include "base/bit_utils.h" -#include "base/file_utils.h" -#include "base/globals.h" -#include "base/logging.h" // For VLOG_IS_ON. -#include "base/memory_tool.h" -#include "base/utils.h" +#include "allocator.h" +#include "bit_utils.h" +#include "globals.h" +#include "logging.h" // For VLOG_IS_ON. +#include "memory_tool.h" +#include "utils.h" #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON @@ -58,21 +61,6 @@ using Maps = AllocationTrackingMultiMap; // All the non-empty MemMaps. Use a multimap as we do a reserve-and-divide (eg ElfMap::Load()). static Maps* gMaps GUARDED_BY(MemMap::GetMemMapsLock()) = nullptr; -static std::ostream& operator<<( - std::ostream& os, - std::pair iters) { - for (BacktraceMap::iterator it = iters.first; it != iters.second; ++it) { - const backtrace_map_t* entry = *it; - os << StringPrintf("0x%08x-0x%08x %c%c%c %s\n", - static_cast(entry->start), - static_cast(entry->end), - (entry->flags & PROT_READ) ? 'r' : '-', - (entry->flags & PROT_WRITE) ? 'w' : '-', - (entry->flags & PROT_EXEC) ? 'x' : '-', entry->name.c_str()); - } - return os; -} - std::ostream& operator<<(std::ostream& os, const Maps& mem_maps) { os << "MemMap:" << std::endl; for (auto it = mem_maps.begin(); it != mem_maps.end(); ++it) { @@ -150,8 +138,6 @@ bool MemMap::ContainedWithinExistingMap(uint8_t* ptr, size_t size, std::string* uintptr_t begin = reinterpret_cast(ptr); uintptr_t end = begin + size; - // There is a suspicion that BacktraceMap::Create is occasionally missing maps. TODO: Investigate - // further. { std::lock_guard mu(*mem_maps_lock_); for (auto& pair : *gMaps) { @@ -163,22 +149,6 @@ bool MemMap::ContainedWithinExistingMap(uint8_t* ptr, size_t size, std::string* } } - std::unique_ptr map(BacktraceMap::Create(getpid(), true)); - if (map == nullptr) { - if (error_msg != nullptr) { - *error_msg = StringPrintf("Failed to build process map"); - } - return false; - } - - ScopedBacktraceMapIteratorLock lock(map.get()); - for (BacktraceMap::iterator it = map->begin(); it != map->end(); ++it) { - const backtrace_map_t* entry = *it; - if ((begin >= entry->start && begin < entry->end) // start of new within old - && (end > entry->start && end <= entry->end)) { // end of new within old - return true; - } - } if (error_msg != nullptr) { PrintFileToLog("/proc/self/maps", LogSeverity::ERROR); *error_msg = StringPrintf("Requested region 0x%08" PRIxPTR "-0x%08" PRIxPTR " does not overlap " @@ -187,35 +157,6 @@ bool MemMap::ContainedWithinExistingMap(uint8_t* ptr, size_t size, std::string* return false; } -// Return true if the address range does not conflict with any /proc/self/maps entry. -static bool CheckNonOverlapping(uintptr_t begin, - uintptr_t end, - std::string* error_msg) { - std::unique_ptr map(BacktraceMap::Create(getpid(), true)); - if (map.get() == nullptr) { - *error_msg = StringPrintf("Failed to build process map"); - return false; - } - ScopedBacktraceMapIteratorLock lock(map.get()); - for (BacktraceMap::iterator it = map->begin(); it != map->end(); ++it) { - const backtrace_map_t* entry = *it; - if ((begin >= entry->start && begin < entry->end) // start of new within old - || (end > entry->start && end < entry->end) // end of new within old - || (begin <= entry->start && end > entry->end)) { // start/end of new includes all of old - std::ostringstream map_info; - map_info << std::make_pair(it, map->end()); - *error_msg = StringPrintf("Requested region 0x%08" PRIxPTR "-0x%08" PRIxPTR " overlaps with " - "existing map 0x%08" PRIxPTR "-0x%08" PRIxPTR " (%s)\n%s", - begin, end, - static_cast(entry->start), static_cast(entry->end), - entry->name.c_str(), - map_info.str().c_str()); - return false; - } - } - return true; -} - // CheckMapRequest to validate a non-MAP_FAILED mmap result based on // the expected value, calling munmap if validation fails, giving the // reason in error_msg. @@ -225,7 +166,7 @@ static bool CheckNonOverlapping(uintptr_t begin, // non-null, we check that pointer is the actual_ptr == expected_ptr, // and if not, report in error_msg what the conflict mapping was if // found, or a generic error in other cases. -static bool CheckMapRequest(uint8_t* expected_ptr, void* actual_ptr, size_t byte_count, +bool MemMap::CheckMapRequest(uint8_t* expected_ptr, void* actual_ptr, size_t byte_count, std::string* error_msg) { // Handled first by caller for more specific error messages. CHECK(actual_ptr != MAP_FAILED); @@ -236,14 +177,13 @@ static bool CheckMapRequest(uint8_t* expected_ptr, void* actual_ptr, size_t byte uintptr_t actual = reinterpret_cast(actual_ptr); uintptr_t expected = reinterpret_cast(expected_ptr); - uintptr_t limit = expected + byte_count; if (expected_ptr == actual_ptr) { return true; } // We asked for an address but didn't get what we wanted, all paths below here should fail. - int result = munmap(actual_ptr, byte_count); + int result = TargetMUnmap(actual_ptr, byte_count); if (result == -1) { PLOG(WARNING) << StringPrintf("munmap(%p, %zd) failed", actual_ptr, byte_count); } @@ -256,33 +196,34 @@ static bool CheckMapRequest(uint8_t* expected_ptr, void* actual_ptr, size_t byte // true, even if there is no overlap // - There might have been an overlap at the point of mmap, but the // overlapping region has since been unmapped. - std::string error_detail; - CheckNonOverlapping(expected, limit, &error_detail); + + // Tell the client the mappings that were in place at the time. + if (kIsDebugBuild) { + PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); + } + std::ostringstream os; os << StringPrintf("Failed to mmap at expected address, mapped at " "0x%08" PRIxPTR " instead of 0x%08" PRIxPTR, actual, expected); - if (!error_detail.empty()) { - os << " : " << error_detail; - } *error_msg = os.str(); } return false; } #if USE_ART_LOW_4G_ALLOCATOR -static inline void* TryMemMapLow4GB(void* ptr, +void* MemMap::TryMemMapLow4GB(void* ptr, size_t page_aligned_byte_count, int prot, int flags, int fd, off_t offset) { - void* actual = mmap(ptr, page_aligned_byte_count, prot, flags, fd, offset); + void* actual = TargetMMap(ptr, page_aligned_byte_count, prot, flags, fd, offset); if (actual != MAP_FAILED) { // Since we didn't use MAP_FIXED the kernel may have mapped it somewhere not in the low // 4GB. If this is the case, unmap and retry. if (reinterpret_cast(actual) + page_aligned_byte_count >= 4 * GB) { - munmap(actual, page_aligned_byte_count); + TargetMUnmap(actual, page_aligned_byte_count); actual = MAP_FAILED; } } @@ -301,7 +242,7 @@ MemMap* MemMap::MapAnonymous(const char* name, #ifndef __LP64__ UNUSED(low_4gb); #endif - use_ashmem = use_ashmem && !kIsTargetLinux; + use_ashmem = use_ashmem && !kIsTargetLinux && !kIsTargetFuchsia; if (byte_count == 0) { return new MemMap(name, nullptr, 0, nullptr, 0, prot, false); } @@ -524,7 +465,7 @@ MemMap* MemMap::MapFileAtAddress(uint8_t* expected_ptr, (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; } @@ -585,7 +526,7 @@ MemMap::~MemMap() { if (!reuse_) { MEMORY_TOOL_MAKE_UNDEFINED(base_begin_, base_size_); if (!already_unmapped_) { - int result = munmap(base_begin_, base_size_); + int result = TargetMUnmap(base_begin_, base_size_); if (result == -1) { PLOG(FATAL) << "munmap failed"; } @@ -629,7 +570,7 @@ MemMap::MemMap(const std::string& name, uint8_t* begin, size_t size, void* base_ MemMap* MemMap::RemapAtEnd(uint8_t* new_end, const char* tail_name, int tail_prot, std::string* error_msg, bool use_ashmem) { - use_ashmem = use_ashmem && !kIsTargetLinux; + use_ashmem = use_ashmem && !kIsTargetLinux && !kIsTargetFuchsia; DCHECK_GE(new_end, Begin()); DCHECK_LE(new_end, End()); DCHECK_LE(begin_ + size_, reinterpret_cast(base_begin_) + base_size_); @@ -671,7 +612,7 @@ MemMap* MemMap::RemapAtEnd(uint8_t* new_end, const char* tail_name, int tail_pro MEMORY_TOOL_MAKE_UNDEFINED(tail_base_begin, tail_base_size); // Unmap/map the tail region. - int result = munmap(tail_base_begin, tail_base_size); + int result = TargetMUnmap(tail_base_begin, tail_base_size); if (result == -1) { PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); *error_msg = StringPrintf("munmap(%p, %zd) failed for '%s'. See process maps in the log.", @@ -682,12 +623,12 @@ MemMap* MemMap::RemapAtEnd(uint8_t* new_end, const char* tail_name, int tail_pro // calls. Otherwise, libc (or something else) might take this memory // region. Note this isn't perfect as there's no way to prevent // other threads to try to take this memory region here. - uint8_t* actual = reinterpret_cast(mmap(tail_base_begin, - tail_base_size, - tail_prot, - flags, - fd.get(), - 0)); + uint8_t* actual = reinterpret_cast(TargetMMap(tail_base_begin, + tail_base_size, + tail_prot, + flags, + fd.get(), + 0)); if (actual == MAP_FAILED) { PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); *error_msg = StringPrintf("anonymous mmap(%p, %zd, 0x%x, 0x%x, %d, 0) failed. See process " @@ -711,19 +652,11 @@ void MemMap::MadviseDontNeedAndZero() { } 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. - uint8_t* base_begin = reinterpret_cast(base_begin_); - MEMORY_TOOL_MAKE_DEFINED(base_begin, begin_ - base_begin); - result = msync(BaseBegin(), End() - base_begin, MS_SYNC) == 0; - MEMORY_TOOL_MAKE_NOACCESS(base_begin, begin_ - base_begin); - } else { - result = msync(BaseBegin(), BaseSize(), MS_SYNC) == 0; - } - return result; + // Historical note: To avoid Valgrind errors, we temporarily lifted the lower-end noaccess + // protection before passing it to msync() when `redzone_size_` was non-null, as Valgrind + // only accepts page-aligned base address, and excludes the higher-end noaccess protection + // from the msync range. b/27552451. + return msync(BaseBegin(), BaseSize(), MS_SYNC) == 0; } bool MemMap::Protect(int prot) { @@ -862,6 +795,8 @@ void MemMap::Init() { std::lock_guard mu(*mem_maps_lock_); DCHECK(gMaps == nullptr); gMaps = new Maps; + + TargetMMapInit(); } void MemMap::Shutdown() { @@ -893,8 +828,10 @@ void MemMap::SetSize(size_t new_size) { reinterpret_cast(reinterpret_cast(BaseBegin()) + new_base_size), base_size_ - new_base_size); - CHECK_EQ(munmap(reinterpret_cast(reinterpret_cast(BaseBegin()) + new_base_size), - base_size_ - new_base_size), 0) << new_base_size << " " << base_size_; + CHECK_EQ(TargetMUnmap(reinterpret_cast( + reinterpret_cast(BaseBegin()) + new_base_size), + base_size_ - new_base_size), 0) + << new_base_size << " " << base_size_; base_size_ = new_base_size; size_ = new_size; } @@ -1040,7 +977,7 @@ void* MemMap::MapInternal(void* addr, if (orig_prot != prot_non_exec) { if (mprotect(actual, length, orig_prot) != 0) { PLOG(ERROR) << "Could not protect to requested prot: " << orig_prot; - munmap(actual, length); + TargetMUnmap(actual, length); errno = ENOMEM; return MAP_FAILED; } @@ -1048,14 +985,14 @@ void* MemMap::MapInternal(void* addr, return actual; } - actual = mmap(addr, length, prot, flags, fd, offset); + actual = TargetMMap(addr, length, prot, flags, fd, offset); #else #if defined(__LP64__) if (low_4gb && addr == nullptr) { flags |= MAP_32BIT; } #endif - actual = mmap(addr, length, prot, flags, fd, offset); + actual = TargetMMap(addr, length, prot, flags, fd, offset); #endif return actual; } @@ -1131,13 +1068,13 @@ void MemMap::AlignBy(size_t size) { // Unmap the unaligned parts. if (base_begin < aligned_base_begin) { MEMORY_TOOL_MAKE_UNDEFINED(base_begin, aligned_base_begin - base_begin); - CHECK_EQ(munmap(base_begin, aligned_base_begin - base_begin), 0) + CHECK_EQ(TargetMUnmap(base_begin, aligned_base_begin - base_begin), 0) << "base_begin=" << reinterpret_cast(base_begin) << " aligned_base_begin=" << reinterpret_cast(aligned_base_begin); } if (aligned_base_end < base_end) { MEMORY_TOOL_MAKE_UNDEFINED(aligned_base_end, base_end - aligned_base_end); - CHECK_EQ(munmap(aligned_base_end, base_end - aligned_base_end), 0) + CHECK_EQ(TargetMUnmap(aligned_base_end, base_end - aligned_base_end), 0) << "base_end=" << reinterpret_cast(base_end) << " aligned_base_end=" << reinterpret_cast(aligned_base_end); } diff --git a/runtime/mem_map.h b/libartbase/base/mem_map.h similarity index 91% rename from runtime/mem_map.h rename to libartbase/base/mem_map.h index 0ecb414614937f3af6fbefbf25c50982fd4d963c..1979357714e9501598d925fdcc03235b7bc9ee23 100644 --- a/runtime/mem_map.h +++ b/libartbase/base/mem_map.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_MEM_MAP_H_ -#define ART_RUNTIME_MEM_MAP_H_ +#ifndef ART_LIBARTBASE_BASE_MEM_MAP_H_ +#define ART_LIBARTBASE_BASE_MEM_MAP_H_ #include #include @@ -25,13 +25,15 @@ #include #include "android-base/thread_annotations.h" +#include "macros.h" namespace art { -#if defined(__LP64__) && (defined(__aarch64__) || defined(__mips__) || defined(__APPLE__)) +#if defined(__LP64__) && !defined(__Fuchsia__) && \ + (defined(__aarch64__) || defined(__mips__) || defined(__APPLE__)) #define USE_ART_LOW_4G_ALLOCATOR 1 #else -#if defined(__LP64__) && !defined(__x86_64__) +#if defined(__LP64__) && !defined(__Fuchsia__) && !defined(__x86_64__) #error "Unrecognized 64-bit architecture." #endif #define USE_ART_LOW_4G_ALLOCATOR 0 @@ -263,6 +265,12 @@ class MemMap { off_t offset) REQUIRES(!MemMap::mem_maps_lock_); + // member function to access real_munmap + static bool CheckMapRequest(uint8_t* expected_ptr, + void* actual_ptr, + size_t byte_count, + std::string* error_msg); + const std::string name_; uint8_t* begin_; // Start of data. May be changed by AlignBy. size_t size_; // Length of data. @@ -283,8 +291,19 @@ class MemMap { #if USE_ART_LOW_4G_ALLOCATOR static uintptr_t next_mem_pos_; // Next memory location to check for low_4g extent. + + static void* TryMemMapLow4GB(void* ptr, + size_t page_aligned_byte_count, + int prot, + int flags, + int fd, + off_t offset); #endif + static void TargetMMapInit(); + static void* TargetMMap(void* start, size_t len, int prot, int flags, int fd, off_t fd_off); + static int TargetMUnmap(void* start, size_t len); + static std::mutex* mem_maps_lock_; friend class MemMapTest; // To allow access to base_begin_ and base_size_. @@ -297,4 +316,4 @@ void ZeroAndReleasePages(void* address, size_t length); } // namespace art -#endif // ART_RUNTIME_MEM_MAP_H_ +#endif // ART_LIBARTBASE_BASE_MEM_MAP_H_ diff --git a/libartbase/base/mem_map_fuchsia.cc b/libartbase/base/mem_map_fuchsia.cc new file mode 100644 index 0000000000000000000000000000000000000000..db31efb1c0fc3e9db4a8c0475a5b876a72ada0db --- /dev/null +++ b/libartbase/base/mem_map_fuchsia.cc @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mem_map.h" +#include +#include "logging.h" + +#include +#include + +namespace art { + +static zx_handle_t fuchsia_lowmem_vmar = ZX_HANDLE_INVALID; +static zx_vaddr_t fuchsia_lowmem_base = 0; +static size_t fuchsia_lowmem_size = 0; + +static const char map_name[] = "mmap-android"; +static constexpr uintptr_t FUCHSIA_LOWER_MEM_START = 0x80000000; +static constexpr uintptr_t FUCHSIA_LOWER_MEM_SIZE = 0x60000000; + +void MemMap::TargetMMapInit() { + if (fuchsia_lowmem_vmar != ZX_HANDLE_INVALID) { + return; + } + + zx_info_vmar_t vmarinfo; + CHECK_EQ(zx_object_get_info(zx_vmar_root_self(), + ZX_INFO_VMAR, + &vmarinfo, + sizeof(vmarinfo), + NULL, + NULL), ZX_OK) << "could not find info from root vmar"; + + uintptr_t lower_mem_start = FUCHSIA_LOWER_MEM_START - vmarinfo.base; + fuchsia_lowmem_size = FUCHSIA_LOWER_MEM_SIZE; + uint32_t allocflags = ZX_VM_FLAG_CAN_MAP_READ | + ZX_VM_FLAG_CAN_MAP_WRITE | + ZX_VM_FLAG_CAN_MAP_EXECUTE | + ZX_VM_FLAG_SPECIFIC; + CHECK_EQ(zx_vmar_allocate(zx_vmar_root_self(), + lower_mem_start, + fuchsia_lowmem_size, + allocflags, + &fuchsia_lowmem_vmar, + &fuchsia_lowmem_base), ZX_OK) << "could not allocate lowmem vmar"; +} + +void* MemMap::TargetMMap(void* start, size_t len, int prot, int flags, int fd, off_t fd_off) { + zx_status_t status; + uintptr_t mem = 0; + + bool mmap_lower = (flags & MAP_32BIT) != 0; + + // for file-based mapping use system library + if ((flags & MAP_ANONYMOUS) == 0) { + if (start != nullptr) { + flags |= MAP_FIXED; + } + CHECK(!mmap_lower) << "cannot map files into low memory for Fuchsia"; + return mmap(start, len, prot, flags, fd, fd_off); + } + + uint32_t vmarflags = 0; + if ((prot & PROT_READ) != 0) { + vmarflags |= ZX_VM_FLAG_PERM_READ; + } + if ((prot & PROT_WRITE) != 0) { + vmarflags |= ZX_VM_FLAG_PERM_WRITE; + } + if ((prot & PROT_EXEC) != 0) { + vmarflags |= ZX_VM_FLAG_PERM_EXECUTE; + } + + if (len == 0) { + errno = EINVAL; + return MAP_FAILED; + } + + zx_info_vmar_t vmarinfo; + size_t vmaroffset = 0; + if (start != nullptr) { + vmarflags |= ZX_VM_FLAG_SPECIFIC; + status = zx_object_get_info((mmap_lower ? fuchsia_lowmem_vmar : zx_vmar_root_self()), + ZX_INFO_VMAR, + &vmarinfo, + sizeof(vmarinfo), + NULL, + NULL); + if (status < 0 || reinterpret_cast(start) < vmarinfo.base) { + errno = EINVAL; + return MAP_FAILED; + } + vmaroffset = reinterpret_cast(start) - vmarinfo.base; + } + + zx_handle_t vmo; + if (zx_vmo_create(len, 0, &vmo) < 0) { + errno = ENOMEM; + return MAP_FAILED; + } + zx_vmo_get_size(vmo, &len); + zx_object_set_property(vmo, ZX_PROP_NAME, map_name, strlen(map_name)); + + if (mmap_lower) { + status = zx_vmar_map(fuchsia_lowmem_vmar, vmaroffset, vmo, fd_off, len, vmarflags, &mem); + } else { + status = zx_vmar_map(zx_vmar_root_self(), vmaroffset, vmo, fd_off, len, vmarflags, &mem); + } + zx_handle_close(vmo); + if (status != ZX_OK) { + return MAP_FAILED; + } + + return reinterpret_cast(mem); +} + +int MemMap::TargetMUnmap(void* start, size_t len) { + uintptr_t addr = reinterpret_cast(start); + zx_handle_t alloc_vmar = zx_vmar_root_self(); + if (addr >= fuchsia_lowmem_base && addr < fuchsia_lowmem_base + fuchsia_lowmem_size) { + alloc_vmar = fuchsia_lowmem_vmar; + } + zx_status_t status = zx_vmar_unmap(alloc_vmar, addr, len); + if (status < 0) { + errno = EINVAL; + return -1; + } + return 0; +} + +} // namespace art diff --git a/runtime/mem_map_test.cc b/libartbase/base/mem_map_test.cc similarity index 95% rename from runtime/mem_map_test.cc rename to libartbase/base/mem_map_test.cc index 3adbf18a7a6acde7f93b2ca32f94592274aee9cd..c575c7a31fc2dfe08d3c9309c61ebcd7520866ac 100644 --- a/runtime/mem_map_test.cc +++ b/libartbase/base/mem_map_test.cc @@ -21,13 +21,14 @@ #include #include -#include "base/memory_tool.h" -#include "base/unix_file/fd_file.h" -#include "common_runtime_test.h" +#include "base/common_art_test.h" +#include "common_runtime_test.h" // For TEST_DISABLED_FOR_MIPS +#include "memory_tool.h" +#include "unix_file/fd_file.h" namespace art { -class MemMapTest : public CommonRuntimeTest { +class MemMapTest : public CommonArtTest { public: static uint8_t* BaseBegin(MemMap* mem_map) { return reinterpret_cast(mem_map->base_begin_); @@ -470,31 +471,32 @@ TEST_F(MemMapTest, MapAnonymousExactAddr32bitHighAddr) { // cannot allocate in the 2GB-4GB region. TEST_DISABLED_FOR_MIPS(); + // This test does not work under AddressSanitizer. + // Historical note: This test did not work under Valgrind either. + 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 map; - for (; start_addr <= std::numeric_limits::max() - size; start_addr += size) { - map.reset(MemMap::MapAnonymous("MapAnonymousExactAddr32bitHighAddr", - reinterpret_cast(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 map; + for (; start_addr <= std::numeric_limits::max() - size; start_addr += size) { + map.reset(MemMap::MapAnonymous("MapAnonymousExactAddr32bitHighAddr", + reinterpret_cast(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(map->End()), 2u * GB); - ASSERT_TRUE(error_msg.empty()); - ASSERT_EQ(BaseBegin(map.get()), reinterpret_cast(start_addr)); } + ASSERT_TRUE(map.get() != nullptr) << error_msg; + ASSERT_GE(reinterpret_cast(map->End()), 2u * GB); + ASSERT_TRUE(error_msg.empty()); + ASSERT_EQ(BaseBegin(map.get()), reinterpret_cast(start_addr)); } TEST_F(MemMapTest, MapAnonymousOverflow) { diff --git a/runtime/gc/reference_processor-inl.h b/libartbase/base/mem_map_unix.cc similarity index 58% rename from runtime/gc/reference_processor-inl.h rename to libartbase/base/mem_map_unix.cc index 0f47d3dc9f182b42eee4a621a8be042bc7309a4f..601b049525362fb2b941f5748fb96de2cd77545e 100644 --- a/runtime/gc/reference_processor-inl.h +++ b/libartbase/base/mem_map_unix.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,21 +14,22 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_GC_REFERENCE_PROCESSOR_INL_H_ -#define ART_RUNTIME_GC_REFERENCE_PROCESSOR_INL_H_ +#include "mem_map.h" -#include "reference_processor.h" - -#include "mirror/reference-inl.h" +#include namespace art { -namespace gc { -inline bool ReferenceProcessor::SlowPathEnabled() { - return mirror::Reference::GetJavaLangRefReference()->GetSlowPathEnabled(); +void MemMap::TargetMMapInit() { + // no-op for unix } -} // namespace gc -} // namespace art +void* MemMap::TargetMMap(void* start, size_t len, int prot, int flags, int fd, off_t fd_off) { + return mmap(start, len, prot, flags, fd, fd_off); +} -#endif // ART_RUNTIME_GC_REFERENCE_PROCESSOR_INL_H_ +int MemMap::TargetMUnmap(void* start, size_t len) { + return munmap(start, len); +} + +} // namespace art diff --git a/runtime/base/dumpable-inl.h b/libartbase/base/memory_region.cc similarity index 56% rename from runtime/base/dumpable-inl.h rename to libartbase/base/memory_region.cc index 9d7fc39093bf19c33b4c178da5febc1d91074f85..d2078724874633814a8c0236f9d79fe7f2c47e68 100644 --- a/runtime/base/dumpable-inl.h +++ b/libartbase/base/memory_region.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,22 +14,19 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_DUMPABLE_INL_H_ -#define ART_RUNTIME_BASE_DUMPABLE_INL_H_ +#include "memory_region.h" -#include "base/dumpable.h" -#include "base/mutex.h" -#include "thread-current-inl.h" +#include +#include namespace art { -template -inline std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable& rhs) { - Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); - rhs.Dump(os); - return os; +void MemoryRegion::CopyFrom(size_t offset, const MemoryRegion& from) const { + CHECK(from.pointer() != nullptr); + CHECK_GT(from.size(), 0U); + CHECK_GE(this->size(), from.size()); + CHECK_LE(offset, this->size() - from.size()); + memmove(reinterpret_cast(begin() + offset), from.pointer(), from.size()); } } // namespace art - -#endif // ART_RUNTIME_BASE_DUMPABLE_INL_H_ diff --git a/runtime/memory_region.h b/libartbase/base/memory_region.h similarity index 65% rename from runtime/memory_region.h rename to libartbase/base/memory_region.h index 23e0aecbda5f83c8b44a23d4526d0c9b8dab61fd..206032923f9d2a5f6f856b83c30e23b1369bb5f1 100644 --- a/runtime/memory_region.h +++ b/libartbase/base/memory_region.h @@ -14,20 +14,20 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_MEMORY_REGION_H_ -#define ART_RUNTIME_MEMORY_REGION_H_ +#ifndef ART_LIBARTBASE_BASE_MEMORY_REGION_H_ +#define ART_LIBARTBASE_BASE_MEMORY_REGION_H_ #include #include #include -#include "arch/instruction_set.h" -#include "base/bit_utils.h" -#include "base/casts.h" -#include "base/macros.h" -#include "base/value_object.h" +#include "bit_utils.h" +#include "casts.h" +#include "enums.h" #include "globals.h" +#include "macros.h" +#include "value_object.h" namespace art { @@ -109,67 +109,6 @@ class MemoryRegion FINAL : public ValueObject { return ComputeInternalPointer(offset); } - // Load a single bit in the region. The bit at offset 0 is the least - // significant bit in the first byte. - ALWAYS_INLINE bool LoadBit(uintptr_t bit_offset) const { - uint8_t bit_mask; - uint8_t byte = *ComputeBitPointer(bit_offset, &bit_mask); - return byte & bit_mask; - } - - ALWAYS_INLINE void StoreBit(uintptr_t bit_offset, bool value) const { - uint8_t bit_mask; - uint8_t* byte = ComputeBitPointer(bit_offset, &bit_mask); - if (value) { - *byte |= bit_mask; - } else { - *byte &= ~bit_mask; - } - } - - // Load `length` bits from the region starting at bit offset `bit_offset`. - // The bit at the smallest offset is the least significant bit in the - // loaded value. `length` must not be larger than the number of bits - // contained in the return value (32). - ALWAYS_INLINE uint32_t LoadBits(uintptr_t bit_offset, size_t length) const { - DCHECK_LE(length, BitSizeOf()); - DCHECK_LE(bit_offset + length, size_in_bits()); - if (UNLIKELY(length == 0)) { - // Do not touch any memory if the range is empty. - return 0; - } - const uint8_t* address = begin() + bit_offset / kBitsPerByte; - const uint32_t shift = bit_offset & (kBitsPerByte - 1); - // Load the value (reading only the strictly needed bytes). - const uint32_t load_bit_count = shift + length; - uint32_t value = address[0] >> shift; - if (load_bit_count > 8) { - value |= static_cast(address[1]) << (8 - shift); - if (load_bit_count > 16) { - value |= static_cast(address[2]) << (16 - shift); - if (load_bit_count > 24) { - value |= static_cast(address[3]) << (24 - shift); - if (load_bit_count > 32) { - value |= static_cast(address[4]) << (32 - shift); - } - } - } - } - // Clear unwanted most significant bits. - uint32_t clear_bit_count = BitSizeOf(value) - length; - value = (value << clear_bit_count) >> clear_bit_count; - for (size_t i = 0; i < length; ++i) { - DCHECK_EQ((value >> i) & 1, LoadBit(bit_offset + i)); - } - return value; - } - - // Store `value` on `length` bits in the region starting at bit offset - // `bit_offset`. The bit at the smallest offset is the least significant - // bit of the stored `value`. `value` must not be larger than `length` - // bits. - void StoreBits(uintptr_t bit_offset, uint32_t value, size_t length); - void CopyFrom(size_t offset, const MemoryRegion& from) const; template @@ -211,9 +150,8 @@ class MemoryRegion FINAL : public ValueObject { // Is `address` aligned on a machine word? template static constexpr bool IsWordAligned(const T* address) { - // Word alignment in bytes. - size_t kWordAlignment = static_cast(GetInstructionSetPointerSize(kRuntimeISA)); - return IsAlignedParam(address, kWordAlignment); + // Word alignment in bytes. Determined from pointer size. + return IsAligned(address); } void* pointer_; @@ -222,4 +160,4 @@ class MemoryRegion FINAL : public ValueObject { } // namespace art -#endif // ART_RUNTIME_MEMORY_REGION_H_ +#endif // ART_LIBARTBASE_BASE_MEMORY_REGION_H_ diff --git a/runtime/memory_region_test.cc b/libartbase/base/memory_region_test.cc similarity index 54% rename from runtime/memory_region_test.cc rename to libartbase/base/memory_region_test.cc index e3aead47faaa197977b88b97fc4746160e158e15..72e03a485a30d7a8dc78a7bc75d145cf3ce630d6 100644 --- a/runtime/memory_region_test.cc +++ b/libartbase/base/memory_region_test.cc @@ -18,8 +18,6 @@ #include "gtest/gtest.h" -#include "bit_memory_region.h" - namespace art { TEST(MemoryRegion, LoadUnaligned) { @@ -57,35 +55,4 @@ TEST(MemoryRegion, StoreUnaligned) { } } -TEST(MemoryRegion, TestBits) { - const size_t n = 8; - uint8_t data[n] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - MemoryRegion region(&data, n); - uint32_t value = 0xDEADBEEF; - // Try various offsets and lengths. - for (size_t bit_offset = 0; bit_offset < 2 * kBitsPerByte; ++bit_offset) { - for (size_t length = 0; length < 2 * kBitsPerByte; ++length) { - const uint32_t length_mask = (1 << length) - 1; - uint32_t masked_value = value & length_mask; - BitMemoryRegion bmr(region, bit_offset, length); - region.StoreBits(bit_offset, masked_value, length); - EXPECT_EQ(region.LoadBits(bit_offset, length), masked_value); - EXPECT_EQ(bmr.LoadBits(0, length), masked_value); - // Check adjacent bits to make sure they were not incorrectly cleared. - EXPECT_EQ(region.LoadBits(0, bit_offset), (1u << bit_offset) - 1); - EXPECT_EQ(region.LoadBits(bit_offset + length, length), length_mask); - region.StoreBits(bit_offset, length_mask, length); - // Store with bit memory region. - bmr.StoreBits(0, masked_value, length); - EXPECT_EQ(bmr.LoadBits(0, length), masked_value); - // Check adjacent bits to make sure they were not incorrectly cleared. - EXPECT_EQ(region.LoadBits(0, bit_offset), (1u << bit_offset) - 1); - EXPECT_EQ(region.LoadBits(bit_offset + length, length), length_mask); - region.StoreBits(bit_offset, length_mask, length); - // Flip the value to try different edge bit combinations. - value = ~value; - } - } -} - } // namespace art diff --git a/libartbase/base/memory_tool.h b/libartbase/base/memory_tool.h index e1df99fed4c9b29fff285dfec37a625652b0dcc0..d381f010f56623241ce3588a01d6de0cd9132a10 100644 --- a/libartbase/base/memory_tool.h +++ b/libartbase/base/memory_tool.h @@ -19,53 +19,53 @@ #include +namespace art { + #if !defined(__has_feature) -#define __has_feature(x) 0 +# define __has_feature(x) 0 #endif #if __has_feature(address_sanitizer) -#include -#define ADDRESS_SANITIZER +# include +# 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 -#include -#define MEMORY_TOOL_MAKE_NOACCESS(p, s) VALGRIND_MAKE_MEM_NOACCESS(p, s) -#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) VALGRIND_MAKE_MEM_UNDEFINED(p, s) -#define MEMORY_TOOL_MAKE_DEFINED(p, s) VALGRIND_MAKE_MEM_DEFINED(p, s) -#define ATTRIBUTE_NO_SANITIZE_ADDRESS -#define MEMORY_TOOL_HANDLE_NO_RETURN do { } while (0) -#define RUNNING_ON_MEMORY_TOOL RUNNING_ON_VALGRIND -constexpr bool kMemoryToolIsAvailable = true; -constexpr bool kMemoryToolIsValgrind = true; -constexpr bool kMemoryToolDetectsLeaks = true; -constexpr bool kMemoryToolAddsRedzones = true; +# define MEMORY_TOOL_MAKE_NOACCESS(p, s) do { (void)(p); (void)(s); } while (0) +# define MEMORY_TOOL_MAKE_UNDEFINED(p, s) do { (void)(p); (void)(s); } while (0) +# define MEMORY_TOOL_MAKE_DEFINED(p, s) do { (void)(p); (void)(s); } while (0) +# define ATTRIBUTE_NO_SANITIZE_ADDRESS +# define MEMORY_TOOL_HANDLE_NO_RETURN do { } while (0) +constexpr bool kRunningOnMemoryTool = false; +constexpr bool kMemoryToolIsAvailable = false; +constexpr bool kMemoryToolDetectsLeaks = false; +constexpr bool kMemoryToolAddsRedzones = false; constexpr size_t kMemoryToolStackGuardSizeScale = 1; #endif +} // namespace art + #endif // ART_LIBARTBASE_BASE_MEMORY_TOOL_H_ diff --git a/libartbase/base/os_linux.cc b/libartbase/base/os_linux.cc index cb228bd8537bcef1d1a5a12d7a950df378456356..f8b31cf0d81d6cf79abdfa3a76a91c85ff3ed145 100644 --- a/libartbase/base/os_linux.cc +++ b/libartbase/base/os_linux.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "base/os.h" +#include "os.h" #include #include @@ -25,7 +25,7 @@ #include -#include "base/unix_file/fd_file.h" +#include "unix_file/fd_file.h" namespace art { diff --git a/libartbase/base/safe_copy.cc b/libartbase/base/safe_copy.cc index 7ba5cbd3e6686366410d49c3cecedfdb68c4d474..b46b921307669b4a82d6924bfb7470a6fa3f178d 100644 --- a/libartbase/base/safe_copy.cc +++ b/libartbase/base/safe_copy.cc @@ -24,7 +24,7 @@ #include -#include "base/bit_utils.h" +#include "bit_utils.h" namespace art { diff --git a/libartbase/base/safe_copy_test.cc b/libartbase/base/safe_copy_test.cc index a9ec9528a188a8324138049b944222f857987cd2..c23651f7a7150bbe28d3f185219d6ba7b7469f07 100644 --- a/libartbase/base/safe_copy_test.cc +++ b/libartbase/base/safe_copy_test.cc @@ -16,14 +16,15 @@ #include "safe_copy.h" -#include "common_runtime_test.h" - #include #include #include #include +#include "android-base/logging.h" #include "globals.h" +#include "gtest/gtest.h" + namespace art { diff --git a/libartbase/base/safe_map.h b/libartbase/base/safe_map.h index e08394ea86cf3de655d4075d4b9234e3e98d7247..a4d845996d6f4355244a4191b104b36b7b9a127e 100644 --- a/libartbase/base/safe_map.h +++ b/libartbase/base/safe_map.h @@ -129,7 +129,7 @@ class SafeMap { } template - V GetOrCreate(const K& k, CreateFn create) { + V& GetOrCreate(const K& k, CreateFn create) { static_assert(std::is_same::type>::value, "Argument `create` should return a value of type V."); auto lb = lower_bound(k); diff --git a/runtime/base/scoped_arena_allocator.cc b/libartbase/base/scoped_arena_allocator.cc similarity index 99% rename from runtime/base/scoped_arena_allocator.cc rename to libartbase/base/scoped_arena_allocator.cc index 7240842d552070b727519d5351963aea5346b54d..ab05c6041ac711a5b4f954206f22a07d54a66ed7 100644 --- a/runtime/base/scoped_arena_allocator.cc +++ b/libartbase/base/scoped_arena_allocator.cc @@ -17,7 +17,7 @@ #include "scoped_arena_allocator.h" #include "arena_allocator-inl.h" -#include "base/memory_tool.h" +#include "memory_tool.h" namespace art { diff --git a/runtime/base/scoped_arena_allocator.h b/libartbase/base/scoped_arena_allocator.h similarity index 96% rename from runtime/base/scoped_arena_allocator.h rename to libartbase/base/scoped_arena_allocator.h index a253e2f53597dba299d090f45c9977403067201c..7eaec5e3c3b28bb3f315281da2de22c7bb9c3a6b 100644 --- a/runtime/base/scoped_arena_allocator.h +++ b/libartbase/base/scoped_arena_allocator.h @@ -14,15 +14,15 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_SCOPED_ARENA_ALLOCATOR_H_ -#define ART_RUNTIME_BASE_SCOPED_ARENA_ALLOCATOR_H_ +#ifndef ART_LIBARTBASE_BASE_SCOPED_ARENA_ALLOCATOR_H_ +#define ART_LIBARTBASE_BASE_SCOPED_ARENA_ALLOCATOR_H_ #include #include "arena_allocator.h" -#include "base/debug_stack.h" -#include "base/globals.h" -#include "base/macros.h" +#include "debug_stack.h" +#include "globals.h" +#include "macros.h" namespace art { @@ -185,4 +185,4 @@ class ScopedArenaAllocator } // namespace art -#endif // ART_RUNTIME_BASE_SCOPED_ARENA_ALLOCATOR_H_ +#endif // ART_LIBARTBASE_BASE_SCOPED_ARENA_ALLOCATOR_H_ diff --git a/runtime/base/scoped_arena_containers.h b/libartbase/base/scoped_arena_containers.h similarity index 92% rename from runtime/base/scoped_arena_containers.h rename to libartbase/base/scoped_arena_containers.h index f8ee3f33af8afb9bc598dfed7dce0819e748b0b0..80144d2c09b21c86f85ccd73cc20eba5663f2cf0 100644 --- a/runtime/base/scoped_arena_containers.h +++ b/libartbase/base/scoped_arena_containers.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_SCOPED_ARENA_CONTAINERS_H_ -#define ART_RUNTIME_BASE_SCOPED_ARENA_CONTAINERS_H_ +#ifndef ART_LIBARTBASE_BASE_SCOPED_ARENA_CONTAINERS_H_ +#define ART_LIBARTBASE_BASE_SCOPED_ARENA_CONTAINERS_H_ #include #include @@ -25,8 +25,8 @@ #include #include "arena_containers.h" // For ArenaAllocatorAdapterKind. -#include "base/dchecked_vector.h" -#include "base/safe_map.h" +#include "dchecked_vector.h" +#include "safe_map.h" #include "scoped_arena_allocator.h" namespace art { @@ -66,15 +66,15 @@ using ScopedArenaSafeMap = template , - typename HashFn = std::hash, - typename Pred = std::equal_to> + typename HashFn = DefaultHashFn, + typename Pred = DefaultPred> using ScopedArenaHashSet = HashSet>; template >, - typename HashFn = std::hash, - typename Pred = std::equal_to> + typename HashFn = DefaultHashFn, + typename Pred = DefaultPred> using ScopedArenaHashMap = HashMap, class KeyEqual = st using ScopedArenaUnorderedMap = std::unordered_map>>; +template , class KeyEqual = std::equal_to> +using ScopedArenaUnorderedMultimap = + std::unordered_multimap>>; + // Implementation details below. template <> @@ -228,7 +236,7 @@ class ArenaDelete { 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); @@ -272,4 +280,4 @@ using ArenaUniquePtr = std::unique_ptr>; } // namespace art -#endif // ART_RUNTIME_BASE_SCOPED_ARENA_CONTAINERS_H_ +#endif // ART_LIBARTBASE_BASE_SCOPED_ARENA_CONTAINERS_H_ diff --git a/libartbase/base/scoped_flock.cc b/libartbase/base/scoped_flock.cc index 514b97bfb157055f0c36f136d9727f7dbb334615..d679328cef998c2166600f57460d7a202ca469f0 100644 --- a/libartbase/base/scoped_flock.cc +++ b/libartbase/base/scoped_flock.cc @@ -22,7 +22,7 @@ #include #include -#include "base/unix_file/fd_file.h" +#include "unix_file/fd_file.h" namespace art { diff --git a/libartbase/base/scoped_flock.h b/libartbase/base/scoped_flock.h index 476b25748b13011032a60779173fcbea0165b549..39b36b4fdd3d043b9d496a9b57f83802f9998be2 100644 --- a/libartbase/base/scoped_flock.h +++ b/libartbase/base/scoped_flock.h @@ -22,9 +22,9 @@ #include -#include "base/macros.h" -#include "base/os.h" -#include "base/unix_file/fd_file.h" +#include "macros.h" +#include "os.h" +#include "unix_file/fd_file.h" namespace art { diff --git a/libartbase/base/scoped_flock_test.cc b/libartbase/base/scoped_flock_test.cc index 1b6caaf747c3d84fefb6b1e9d4061b448b7e8b3d..f9ac1e0230063ec15d5f8806e6afdf250a5c89ac 100644 --- a/libartbase/base/scoped_flock_test.cc +++ b/libartbase/base/scoped_flock_test.cc @@ -16,11 +16,11 @@ #include "scoped_flock.h" -#include "common_runtime_test.h" +#include "base/common_art_test.h" namespace art { -class ScopedFlockTest : public CommonRuntimeTest {}; +class ScopedFlockTest : public CommonArtTest {}; TEST_F(ScopedFlockTest, TestLocking) { ScratchFile scratch_file; diff --git a/libartbase/base/stats.h b/libartbase/base/stats.h new file mode 100644 index 0000000000000000000000000000000000000000..4dcbfe81c67b1ae09b70b3682b8c0f3f67f7ba16 --- /dev/null +++ b/libartbase/base/stats.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_LIBARTBASE_BASE_STATS_H_ +#define ART_LIBARTBASE_BASE_STATS_H_ + +#include + +#include "globals.h" + +namespace art { + +// Simple structure to record tree of statistical values. +class Stats { + public: + double Value() const { return value_; } + size_t Count() const { return count_; } + Stats* Child(const char* name) { return &children_[name]; } + const std::unordered_map& Children() const { return children_; } + + void AddBytes(double bytes, size_t count = 1) { Add(bytes, count); } + void AddBits(double bits, size_t count = 1) { Add(bits / kBitsPerByte, count); } + void AddSeconds(double s, size_t count = 1) { Add(s, count); } + void AddNanoSeconds(double ns, size_t count = 1) { Add(ns / 1000000000.0, count); } + + double SumChildrenValues() const { + double sum = 0.0; + for (auto it : children_) { + sum += it.second.Value(); + } + return sum; + } + + private: + void Add(double value, size_t count = 1) { + value_ += value; + count_ += count; + } + + double value_ = 0.0; // Commutative sum of the collected statistic in basic units. + size_t count_ = 0; // The number of samples for this node. + std::unordered_map children_; +}; + +} // namespace art + +#endif // ART_LIBARTBASE_BASE_STATS_H_ diff --git a/libartbase/base/systrace.h b/libartbase/base/systrace.h index 2ff33e8c841f52b898523fd6a3ec62f87a00f521..d995dce285fdb2e29051d74a2f4c0c4c0dc9a894 100644 --- a/libartbase/base/systrace.h +++ b/libartbase/base/systrace.h @@ -24,6 +24,7 @@ #include #include "android-base/stringprintf.h" +#include "macros.h" namespace art { @@ -75,7 +76,7 @@ class ScopedTraceNoStart { }; #define SCOPED_TRACE \ - ::art::ScopedTraceNoStart trace ## __LINE__; \ + ::art::ScopedTraceNoStart APPEND_TOKENS_AFTER_EVAL(trace, __LINE__) ; \ (ATRACE_ENABLED()) && ::art::ScopedTraceNoStart::ScopedTraceMessageHelper().stream() } // namespace art diff --git a/libartbase/base/time_utils.cc b/libartbase/base/time_utils.cc index 3c09d5a36f20e4b8ecffa51ffd6dd1e25f4157e4..89a1109a7e294a3006419c861ac7d5b71782816c 100644 --- a/libartbase/base/time_utils.cc +++ b/libartbase/base/time_utils.cc @@ -22,7 +22,7 @@ #include "android-base/stringprintf.h" -#include "base/logging.h" +#include "logging.h" #if defined(__APPLE__) #include diff --git a/libartbase/base/time_utils.h b/libartbase/base/time_utils.h index 811af5d68255088e917cfa1526783495998d7e95..431d3e19acdb296882d36be2c00b9e368a5f33c7 100644 --- a/libartbase/base/time_utils.h +++ b/libartbase/base/time_utils.h @@ -22,7 +22,7 @@ #include -#include "base/macros.h" +#include "macros.h" namespace art { diff --git a/libartbase/base/tracking_safe_map.h b/libartbase/base/tracking_safe_map.h index 2750de1b947f174c6a84d248762bc355f55265bb..9b015c4ea5b9bfb3d06faaa99e95b776a8d9e9fe 100644 --- a/libartbase/base/tracking_safe_map.h +++ b/libartbase/base/tracking_safe_map.h @@ -17,8 +17,8 @@ #ifndef ART_LIBARTBASE_BASE_TRACKING_SAFE_MAP_H_ #define ART_LIBARTBASE_BASE_TRACKING_SAFE_MAP_H_ -#include "base/allocator.h" -#include "base/safe_map.h" +#include "allocator.h" +#include "safe_map.h" namespace art { diff --git a/libartbase/base/transform_array_ref.h b/libartbase/base/transform_array_ref.h index de2739e2fcf2e4a0453875f635bc3d5f7a403eb0..ef2957332b20f11a52d4aa87bbc7400e136272bc 100644 --- a/libartbase/base/transform_array_ref.h +++ b/libartbase/base/transform_array_ref.h @@ -19,8 +19,8 @@ #include -#include "base/array_ref.h" -#include "base/transform_iterator.h" +#include "array_ref.h" +#include "transform_iterator.h" namespace art { diff --git a/libartbase/base/transform_array_ref_test.cc b/libartbase/base/transform_array_ref_test.cc index da0340d36b87b2bdf9f73b58582e3986d888e74f..fc73d567794ceb1d9667059da6000719103d87d2 100644 --- a/libartbase/base/transform_array_ref_test.cc +++ b/libartbase/base/transform_array_ref_test.cc @@ -18,8 +18,7 @@ #include #include "gtest/gtest.h" - -#include "base/transform_array_ref.h" +#include "transform_array_ref.h" namespace art { diff --git a/libartbase/base/transform_iterator.h b/libartbase/base/transform_iterator.h index 82d9f9e325e64aedfc8d20a633839cb731e70e85..92655438f4e11e68faaa6f8bd92cc79682ec94db 100644 --- a/libartbase/base/transform_iterator.h +++ b/libartbase/base/transform_iterator.h @@ -20,7 +20,7 @@ #include #include -#include "base/iteration_range.h" +#include "iteration_range.h" namespace art { diff --git a/libartbase/base/transform_iterator_test.cc b/libartbase/base/transform_iterator_test.cc index 63b6e4f531f3b5b90023cc5fbd11af01230c3d31..5a5c37d11d5ffe1d344a0db2eca960ed587104ba 100644 --- a/libartbase/base/transform_iterator_test.cc +++ b/libartbase/base/transform_iterator_test.cc @@ -21,8 +21,7 @@ #include #include "gtest/gtest.h" - -#include "base/transform_iterator.h" +#include "transform_iterator.h" namespace art { diff --git a/libartbase/base/unix_file/fd_file.cc b/libartbase/base/unix_file/fd_file.cc index b2881b8ed085d1e8189a0a8c60b64dce799e7511..c5313e969d3aac5ee8563981e1de9deafd76b565 100644 --- a/libartbase/base/unix_file/fd_file.cc +++ b/libartbase/base/unix_file/fd_file.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "base/unix_file/fd_file.h" +#include "fd_file.h" #include #include @@ -30,8 +30,8 @@ #include #else #include -#include "base/stl_util.h" #include "base/globals.h" +#include "base/stl_util.h" #endif namespace unix_file { diff --git a/libartbase/base/unix_file/fd_file.h b/libartbase/base/unix_file/fd_file.h index fe3317f64ee916576dc1a260959b8d59d1d2947d..d61dab6ce3af2d2a87314ce40908d2426628f21b 100644 --- a/libartbase/base/unix_file/fd_file.h +++ b/libartbase/base/unix_file/fd_file.h @@ -22,7 +22,7 @@ #include #include "base/macros.h" -#include "base/unix_file/random_access_file.h" +#include "random_access_file.h" namespace unix_file { diff --git a/libartbase/base/unix_file/fd_file_test.cc b/libartbase/base/unix_file/fd_file_test.cc index 042fbc928422cd553dbe56e7b05ea2afbe35a20c..1f731a7a7b98e00f9436a92ec54c48bfb88d8d48 100644 --- a/libartbase/base/unix_file/fd_file_test.cc +++ b/libartbase/base/unix_file/fd_file_test.cc @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "base/unix_file/fd_file.h" -#include "base/unix_file/random_access_file_test.h" -#include "common_runtime_test.h" // For ScratchFile +#include "base/common_art_test.h" // For ScratchFile #include "gtest/gtest.h" +#include "fd_file.h" +#include "random_access_file_test.h" namespace unix_file { diff --git a/libartbase/base/unix_file/random_access_file_test.h b/libartbase/base/unix_file/random_access_file_test.h index 1de5f7b72cb2764d88768cb3f26f534006ffbba7..dbe6ca948f8a96cb087390534cc74702b4d6c14f 100644 --- a/libartbase/base/unix_file/random_access_file_test.h +++ b/libartbase/base/unix_file/random_access_file_test.h @@ -21,7 +21,7 @@ #include #include -#include "common_runtime_test.h" +#include "base/common_art_test.h" namespace unix_file { @@ -35,11 +35,11 @@ class RandomAccessFileTest : public testing::Test { virtual RandomAccessFile* MakeTestFile() = 0; virtual void SetUp() { - art::CommonRuntimeTest::SetUpAndroidData(android_data_); + art::CommonArtTest::SetUpAndroidData(android_data_); } virtual void TearDown() { - art::CommonRuntimeTest::TearDownAndroidData(android_data_, true); + art::CommonArtTest::TearDownAndroidData(android_data_, true); } std::string GetTmpPath(const std::string& name) { diff --git a/libartbase/base/unix_file/random_access_file_utils.cc b/libartbase/base/unix_file/random_access_file_utils.cc index aae65c1cde3312bad7c5be57ff8764d718f0a98a..10d829964c0fe857592a0f46bb1f667840125d3d 100644 --- a/libartbase/base/unix_file/random_access_file_utils.cc +++ b/libartbase/base/unix_file/random_access_file_utils.cc @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "base/unix_file/random_access_file_utils.h" +#include "random_access_file_utils.h" #include -#include "base/unix_file/random_access_file.h" +#include "random_access_file.h" namespace unix_file { diff --git a/libartbase/base/utils.cc b/libartbase/base/utils.cc index 029cffd3abbad38c4f93ed02c02e2de4c4e80e38..b7a542fbbe63071ea30e2f61466efcc4dd8ca9d4 100644 --- a/libartbase/base/utils.cc +++ b/libartbase/base/utils.cc @@ -30,7 +30,7 @@ #include "android-base/stringprintf.h" #include "android-base/strings.h" -#include "base/os.h" +#include "os.h" #if defined(__APPLE__) #include diff --git a/libartbase/base/utils.h b/libartbase/base/utils.h index c8c5b72bf72ea1a4c46c39e95db255dcc132c6c8..6e3b78e12c81d203d112113bb1fa9c4043aaa963 100644 --- a/libartbase/base/utils.h +++ b/libartbase/base/utils.h @@ -25,11 +25,11 @@ #include -#include "base/casts.h" -#include "base/enums.h" -#include "base/globals.h" -#include "base/macros.h" -#include "base/stringpiece.h" +#include "casts.h" +#include "enums.h" +#include "globals.h" +#include "macros.h" +#include "stringpiece.h" namespace art { @@ -244,20 +244,6 @@ static inline void CheckedCall(const Func& function, const char* what, Args... a } } -// Hash bytes using a relatively fast hash. -static inline size_t HashBytes(const uint8_t* data, size_t len) { - size_t hash = 0x811c9dc5; - for (uint32_t i = 0; i < len; ++i) { - hash = (hash * 16777619) ^ data[i]; - } - hash += hash << 13; - hash ^= hash >> 7; - hash += hash << 3; - hash ^= hash >> 17; - hash += hash << 5; - return hash; -} - } // namespace art #endif // ART_LIBARTBASE_BASE_UTILS_H_ diff --git a/libartbase/base/value_object.h b/libartbase/base/value_object.h index 441bd1a384ecaac143de86d334c0195140c76a97..dab6b76b269c18f2951e19e84f7db82c23370d26 100644 --- a/libartbase/base/value_object.h +++ b/libartbase/base/value_object.h @@ -17,7 +17,7 @@ #ifndef ART_LIBARTBASE_BASE_VALUE_OBJECT_H_ #define ART_LIBARTBASE_BASE_VALUE_OBJECT_H_ -#include "base/macros.h" +#include "macros.h" namespace art { diff --git a/libartbase/base/variant_map.h b/libartbase/base/variant_map.h index 4e02c54499125a658b0a3e3e65bb54156d988fcb..581bc234cc00605c19458902f13553f92e0d277d 100644 --- a/libartbase/base/variant_map.h +++ b/libartbase/base/variant_map.h @@ -23,7 +23,7 @@ #include #include "android-base/logging.h" -#include "base/stl_util_identity.h" +#include "stl_util_identity.h" namespace art { diff --git a/libartbase/base/variant_map_test.cc b/libartbase/base/variant_map_test.cc index 4677b6d3b36e2d2cb214d3ceceb0760fb36683db..f2da3389b1b81240f802fdb19d7d49ae2f24bac4 100644 --- a/libartbase/base/variant_map_test.cc +++ b/libartbase/base/variant_map_test.cc @@ -108,7 +108,7 @@ TEST(VariantMaps, RuleOfFive) { EXPECT_EQ(size_t(2), fmFilled.Size()); // Test copy constructor - FruitMap fmEmptyCopy(fmEmpty); + FruitMap fmEmptyCopy(fmEmpty); // NOLINT EXPECT_EQ(size_t(0), fmEmptyCopy.Size()); // Test copy constructor diff --git a/runtime/zip_archive.cc b/libartbase/base/zip_archive.cc similarity index 97% rename from runtime/zip_archive.cc rename to libartbase/base/zip_archive.cc index 5b3cab42d8ce2ab4664dc2ef3c87fdf3719e4f35..b5f946e5a2503249ba22f4234a71ba0fa814c3f4 100644 --- a/runtime/zip_archive.cc +++ b/libartbase/base/zip_archive.cc @@ -27,9 +27,8 @@ #include "android-base/stringprintf.h" #include "ziparchive/zip_archive.h" -#include "base/bit_utils.h" -#include "base/unix_file/fd_file.h" -#include "dex/dex_file.h" +#include "bit_utils.h" +#include "unix_file/fd_file.h" namespace art { @@ -55,10 +54,6 @@ bool ZipEntry::IsAlignedTo(size_t alignment) const { return IsAlignedParam(zip_entry_->offset, static_cast(alignment)); } -bool ZipEntry::IsAlignedToDexHeader() const { - return IsAlignedTo(alignof(DexFile::Header)); -} - ZipEntry::~ZipEntry() { delete zip_entry_; } diff --git a/runtime/zip_archive.h b/libartbase/base/zip_archive.h similarity index 92% rename from runtime/zip_archive.h rename to libartbase/base/zip_archive.h index aa54018574419573be15e4ac5461fda49bf33df9..73495da96a118e1faad0594da048ac7614b289a0 100644 --- a/runtime/zip_archive.h +++ b/libartbase/base/zip_archive.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_ZIP_ARCHIVE_H_ -#define ART_RUNTIME_ZIP_ARCHIVE_H_ +#ifndef ART_LIBARTBASE_BASE_ZIP_ARCHIVE_H_ +#define ART_LIBARTBASE_BASE_ZIP_ARCHIVE_H_ #include #include @@ -23,11 +23,11 @@ #include -#include "base/os.h" -#include "base/safe_map.h" -#include "base/unix_file/random_access_file.h" #include "globals.h" #include "mem_map.h" +#include "os.h" +#include "safe_map.h" +#include "unix_file/random_access_file.h" // system/core/zip_archive definitions. struct ZipEntry; @@ -64,7 +64,6 @@ class ZipEntry { bool IsUncompressed(); bool IsAlignedTo(size_t alignment) const; - bool IsAlignedToDexHeader() const; private: ZipEntry(ZipArchiveHandle handle, @@ -102,4 +101,4 @@ class ZipArchive { } // namespace art -#endif // ART_RUNTIME_ZIP_ARCHIVE_H_ +#endif // ART_LIBARTBASE_BASE_ZIP_ARCHIVE_H_ diff --git a/runtime/zip_archive_test.cc b/libartbase/base/zip_archive_test.cc similarity index 93% rename from runtime/zip_archive_test.cc rename to libartbase/base/zip_archive_test.cc index 48ee94ce8c1609c14aead332077e1ebe6e94207f..b99a471e046611b17ba5011a1edc66561c0244f2 100644 --- a/runtime/zip_archive_test.cc +++ b/libartbase/base/zip_archive_test.cc @@ -22,13 +22,13 @@ #include #include -#include "base/os.h" -#include "base/unix_file/fd_file.h" -#include "common_runtime_test.h" +#include "base/common_art_test.h" +#include "os.h" +#include "unix_file/fd_file.h" namespace art { -class ZipArchiveTest : public CommonRuntimeTest {}; +class ZipArchiveTest : public CommonArtTest {}; TEST_F(ZipArchiveTest, FindAndExtract) { std::string error_msg; diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp index 3fd61ee251a8631f7a28199dd60434bae6306bc9..06fd19e2fe1767238cd26666175361266a9764a2 100644 --- a/libdexfile/Android.bp +++ b/libdexfile/Android.bp @@ -19,6 +19,7 @@ cc_defaults { defaults: ["art_defaults"], host_supported: true, srcs: [ + "dex/art_dex_file_loader.cc", "dex/compact_dex_file.cc", "dex/compact_offset_table.cc", "dex/descriptors_names.cc", @@ -32,6 +33,7 @@ cc_defaults { "dex/modifiers.cc", "dex/primitive.cc", "dex/standard_dex_file.cc", + "dex/type_lookup_table.cc", "dex/utf.cc", ], @@ -54,24 +56,20 @@ cc_defaults { }, generated_sources: ["dexfile_operator_srcs"], shared_libs: [ - // Important note: relying on libartbase's header_lib is perfectly acceptable. - // However, relying on the libartbase shared library introduces further, possibly cyclic, - // dependencies for clients outside of ART. + // For MemMap. + "libartbase", "liblog", + // For atrace. + "libcutils", // For common macros. "libbase", "libz", ], - header_libs: [ - "art_libartbase_headers", - ], export_include_dirs: ["."], export_shared_lib_headers: [ + "libartbase", "libbase", ], - export_header_lib_headers: [ - "art_libartbase_headers", - ], } gensrcs { @@ -113,6 +111,8 @@ art_cc_test { "art_gtest_defaults", ], srcs: [ + "dex/art_dex_file_loader_test.cc", + "dex/class_accessor_test.cc", "dex/code_item_accessors_test.cc", "dex/compact_dex_file_test.cc", "dex/compact_offset_table_test.cc", @@ -123,6 +123,7 @@ art_cc_test { "dex/dex_instruction_test.cc", "dex/primitive_test.cc", "dex/string_reference_test.cc", + "dex/type_lookup_table_test.cc", "dex/utf_test.cc", ], shared_libs: [ diff --git a/runtime/dex/art_dex_file_loader.cc b/libdexfile/dex/art_dex_file_loader.cc similarity index 99% rename from runtime/dex/art_dex_file_loader.cc rename to libdexfile/dex/art_dex_file_loader.cc index 16d26dcb2a43b13a455e1e7cfd2a24f703b77425..392ce1e7f597344137db2f2514a4c4a6105b189f 100644 --- a/runtime/dex/art_dex_file_loader.cc +++ b/libdexfile/dex/art_dex_file_loader.cc @@ -26,11 +26,11 @@ #include "base/stl_util.h" #include "base/systrace.h" #include "base/unix_file/fd_file.h" +#include "base/zip_archive.h" #include "dex/compact_dex_file.h" #include "dex/dex_file.h" #include "dex/dex_file_verifier.h" #include "dex/standard_dex_file.h" -#include "zip_archive.h" namespace art { @@ -128,7 +128,7 @@ bool ArtDexFileLoader::GetMultiDexChecksums(const char* filename, do { if (zip_file_only_contains_uncompressed_dex != nullptr) { - if (!(zip_entry->IsUncompressed() && zip_entry->IsAlignedToDexHeader())) { + if (!(zip_entry->IsUncompressed() && zip_entry->IsAlignedTo(alignof(DexFile::Header)))) { *zip_file_only_contains_uncompressed_dex = false; } } diff --git a/runtime/dex/art_dex_file_loader.h b/libdexfile/dex/art_dex_file_loader.h similarity index 97% rename from runtime/dex/art_dex_file_loader.h rename to libdexfile/dex/art_dex_file_loader.h index 75779456320fb2fabbe8b6e45813d0915b664a35..a460aee60fc34d6e47fd6968300410be4d65fe33 100644 --- a/runtime/dex/art_dex_file_loader.h +++ b/libdexfile/dex/art_dex_file_loader.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_DEX_ART_DEX_FILE_LOADER_H_ -#define ART_RUNTIME_DEX_ART_DEX_FILE_LOADER_H_ +#ifndef ART_LIBDEXFILE_DEX_ART_DEX_FILE_LOADER_H_ +#define ART_LIBDEXFILE_DEX_ART_DEX_FILE_LOADER_H_ #include #include @@ -137,4 +137,4 @@ class ArtDexFileLoader : public DexFileLoader { } // namespace art -#endif // ART_RUNTIME_DEX_ART_DEX_FILE_LOADER_H_ +#endif // ART_LIBDEXFILE_DEX_ART_DEX_FILE_LOADER_H_ diff --git a/runtime/dex/art_dex_file_loader_test.cc b/libdexfile/dex/art_dex_file_loader_test.cc similarity index 95% rename from runtime/dex/art_dex_file_loader_test.cc rename to libdexfile/dex/art_dex_file_loader_test.cc index 802046ec12ee3e6d7cae667d093828ca469c2faa..5f3fc0266fe9b77347d0a927d105688b6e523ccd 100644 --- a/runtime/dex/art_dex_file_loader_test.cc +++ b/libdexfile/dex/art_dex_file_loader_test.cc @@ -14,26 +14,25 @@ * limitations under the License. */ +#include "art_dex_file_loader.h" + #include #include #include -#include "art_dex_file_loader.h" +#include "base/common_art_test.h" #include "base/file_utils.h" +#include "base/mem_map.h" #include "base/os.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" -#include "common_runtime_test.h" #include "dex/base64_test_util.h" #include "dex/code_item_accessors-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" -#include "mem_map.h" -#include "scoped_thread_state_change-inl.h" -#include "thread-current-inl.h" namespace art { @@ -43,26 +42,35 @@ static void Copy(const std::string& src, const std::string& dst) { dst_stream << src_stream.rdbuf(); } -class ArtDexFileLoaderTest : public CommonRuntimeTest {}; +class ArtDexFileLoaderTest : public CommonArtTest { + void SetUp() OVERRIDE { + CommonArtTest::SetUp(); + // Open a jar file from the boot classpath for use in basic tests of dex accessors. + std::vector lib_core_dex_file_names = GetLibCoreDexFileNames(); + CHECK_NE(lib_core_dex_file_names.size(), 0U); + dex_files_ = OpenDexFiles(lib_core_dex_file_names[0].c_str()); + CHECK_NE(dex_files_.size(), 0U); + // Save a dex file for use by tests. + java_lang_dex_file_ = dex_files_[0].get(); + } -// TODO: Port OpenTestDexFile(s) need to be ported to use non-ART utilities, and -// the tests that depend upon them should be moved to dex_file_loader_test.cc + protected: + std::vector> dex_files_; + const DexFile* java_lang_dex_file_; +}; TEST_F(ArtDexFileLoaderTest, Open) { - ScopedObjectAccess soa(Thread::Current()); std::unique_ptr dex(OpenTestDexFile("Nested")); ASSERT_TRUE(dex.get() != nullptr); } TEST_F(ArtDexFileLoaderTest, GetLocationChecksum) { - ScopedObjectAccess soa(Thread::Current()); std::unique_ptr raw(OpenTestDexFile("Main")); EXPECT_NE(raw->GetHeader().checksum_, raw->GetLocationChecksum()); } TEST_F(ArtDexFileLoaderTest, GetChecksum) { std::vector checksums; - ScopedObjectAccess soa(Thread::Current()); std::string error_msg; const ArtDexFileLoader dex_file_loader; EXPECT_TRUE(dex_file_loader.GetMultiDexChecksums(GetLibCoreDexFileNames()[0].c_str(), @@ -94,7 +102,6 @@ TEST_F(ArtDexFileLoaderTest, GetMultiDexChecksums) { } TEST_F(ArtDexFileLoaderTest, ClassDefs) { - ScopedObjectAccess soa(Thread::Current()); std::unique_ptr raw(OpenTestDexFile("Nested")); ASSERT_TRUE(raw.get() != nullptr); EXPECT_EQ(3U, raw->NumClassDefs()); @@ -110,7 +117,6 @@ TEST_F(ArtDexFileLoaderTest, ClassDefs) { } TEST_F(ArtDexFileLoaderTest, GetMethodSignature) { - ScopedObjectAccess soa(Thread::Current()); std::unique_ptr raw(OpenTestDexFile("GetMethodSignature")); ASSERT_TRUE(raw.get() != nullptr); EXPECT_EQ(1U, raw->NumClassDefs()); @@ -215,7 +221,6 @@ TEST_F(ArtDexFileLoaderTest, GetMethodSignature) { } TEST_F(ArtDexFileLoaderTest, FindStringId) { - ScopedObjectAccess soa(Thread::Current()); std::unique_ptr raw(OpenTestDexFile("GetMethodSignature")); ASSERT_TRUE(raw.get() != nullptr); EXPECT_EQ(1U, raw->NumClassDefs()); @@ -245,7 +250,7 @@ TEST_F(ArtDexFileLoaderTest, FindTypeId) { TEST_F(ArtDexFileLoaderTest, FindProtoId) { for (size_t i = 0; i < java_lang_dex_file_->NumProtoIds(); i++) { - const DexFile::ProtoId& to_find = java_lang_dex_file_->GetProtoId(i); + const DexFile::ProtoId& to_find = java_lang_dex_file_->GetProtoId(dex::ProtoIndex(i)); const DexFile::TypeList* to_find_tl = java_lang_dex_file_->GetProtoParameters(to_find); std::vector to_find_types; if (to_find_tl != nullptr) { @@ -256,7 +261,7 @@ TEST_F(ArtDexFileLoaderTest, FindProtoId) { const DexFile::ProtoId* found = java_lang_dex_file_->FindProtoId(to_find.return_type_idx_, to_find_types); ASSERT_TRUE(found != nullptr); - EXPECT_EQ(java_lang_dex_file_->GetIndexForProtoId(*found), i); + EXPECT_EQ(java_lang_dex_file_->GetIndexForProtoId(*found), dex::ProtoIndex(i)); } } diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..dd91438ff7078d5a32d8b80066155d5838efe6b8 --- /dev/null +++ b/libdexfile/dex/class_accessor-inl.h @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_INL_H_ +#define ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_INL_H_ + +#include "class_accessor.h" + +#include "base/leb128.h" +#include "class_iterator.h" +#include "code_item_accessors-inl.h" + +namespace art { + +inline ClassAccessor::ClassAccessor(const ClassIteratorData& data) + : 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), + 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), + num_virtual_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u) {} + +inline void ClassAccessor::Method::Read() { + index_ += DecodeUnsignedLeb128(&ptr_pos_); + access_flags_ = DecodeUnsignedLeb128(&ptr_pos_); + code_off_ = DecodeUnsignedLeb128(&ptr_pos_); +} + +inline void ClassAccessor::Field::Read() { + index_ += DecodeUnsignedLeb128(&ptr_pos_); + access_flags_ = DecodeUnsignedLeb128(&ptr_pos_); +} + +template +inline void ClassAccessor::VisitMembers(size_t count, + const Visitor& visitor, + DataType* data) const { + DCHECK(data != nullptr); + for ( ; count != 0; --count) { + data->Read(); + visitor(*data); + } +} + +template +inline void ClassAccessor::VisitFieldsAndMethods( + const StaticFieldVisitor& static_field_visitor, + const InstanceFieldVisitor& instance_field_visitor, + const DirectMethodVisitor& direct_method_visitor, + const VirtualMethodVisitor& virtual_method_visitor) const { + Field field(dex_file_, ptr_pos_); + VisitMembers(num_static_fields_, static_field_visitor, &field); + field.NextSection(); + VisitMembers(num_instance_fields_, instance_field_visitor, &field); + + Method method(dex_file_, field.ptr_pos_, /*is_static_or_direct*/ true); + VisitMembers(num_direct_methods_, direct_method_visitor, &method); + method.NextSection(); + VisitMembers(num_virtual_methods_, virtual_method_visitor, &method); +} + +template +inline void ClassAccessor::VisitMethods(const DirectMethodVisitor& direct_method_visitor, + const VirtualMethodVisitor& virtual_method_visitor) const { + VisitFieldsAndMethods(VoidFunctor(), + VoidFunctor(), + direct_method_visitor, + virtual_method_visitor); +} + +template +inline void ClassAccessor::VisitFields(const StaticFieldVisitor& static_field_visitor, + const InstanceFieldVisitor& instance_field_visitor) const { + VisitFieldsAndMethods(static_field_visitor, + instance_field_visitor, + VoidFunctor(), + VoidFunctor()); +} + +inline const DexFile::CodeItem* ClassAccessor::GetCodeItem(const Method& method) const { + return dex_file_.GetCodeItem(method.GetCodeItemOffset()); +} + +inline CodeItemInstructionAccessor ClassAccessor::Method::GetInstructions() const { + return CodeItemInstructionAccessor(dex_file_, dex_file_.GetCodeItem(GetCodeItemOffset())); +} + +inline CodeItemDataAccessor ClassAccessor::Method::GetInstructionsAndData() const { + return CodeItemDataAccessor(dex_file_, dex_file_.GetCodeItem(GetCodeItemOffset())); +} + +inline const char* ClassAccessor::GetDescriptor() const { + return dex_file_.StringByTypeIdx(GetClassIdx()); +} + +inline const DexFile::CodeItem* ClassAccessor::Method::GetCodeItem() const { + return dex_file_.GetCodeItem(code_off_); +} + +inline IterationRange> + ClassAccessor::GetFieldsInternal(size_t count) const { + return { DataIterator(dex_file_, 0u, num_static_fields_, count, ptr_pos_), + DataIterator(dex_file_, count, num_static_fields_, count, ptr_pos_) }; +} + +// Return an iteration range for the first methods. +inline IterationRange> + ClassAccessor::GetMethodsInternal(size_t count) const { + // Skip over the fields. + Field field(dex_file_, ptr_pos_); + VisitMembers(NumFields(), VoidFunctor(), &field); + // Return the iterator pair. + return { DataIterator(dex_file_, 0u, num_direct_methods_, count, field.ptr_pos_), + DataIterator(dex_file_, count, num_direct_methods_, count, field.ptr_pos_) }; +} + +inline IterationRange> ClassAccessor::GetFields() + const { + return GetFieldsInternal(num_static_fields_ + num_instance_fields_); +} + +inline IterationRange> + ClassAccessor::GetStaticFields() const { + return GetFieldsInternal(num_static_fields_); +} + + +inline IterationRange> + ClassAccessor::GetInstanceFields() const { + IterationRange> fields = GetFields(); + // Skip the static fields. + return { std::next(fields.begin(), NumStaticFields()), fields.end() }; +} + +inline IterationRange> + ClassAccessor::GetMethods() const { + return GetMethodsInternal(NumMethods()); +} + +inline IterationRange> + ClassAccessor::GetDirectMethods() const { + return GetMethodsInternal(NumDirectMethods()); +} + +inline IterationRange> + ClassAccessor::GetVirtualMethods() const { + IterationRange> methods = GetMethods(); + // Skip the direct fields. + return { std::next(methods.begin(), NumDirectMethods()), methods.end() }; +} + +inline void ClassAccessor::Field::UnHideAccessFlags() const { + DexFile::UnHideAccessFlags(const_cast(ptr_pos_), GetAccessFlags(), /*is_method*/ false); +} + +inline void ClassAccessor::Method::UnHideAccessFlags() const { + DexFile::UnHideAccessFlags(const_cast(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 new file mode 100644 index 0000000000000000000000000000000000000000..0d87f93d60d02fb5280fdd796e951436eec8a870 --- /dev/null +++ b/libdexfile/dex/class_accessor.h @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_H_ +#define ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_H_ + +#include "base/utils.h" +#include "code_item_accessors.h" +#include "dex_file.h" +#include "hidden_api_access_flags.h" +#include "invoke_type.h" +#include "method_reference.h" +#include "modifiers.h" + +namespace art { + +class ClassIteratorData; + +// Classes to access Dex data. +class ClassAccessor { + private: + class BaseItem { + public: + explicit BaseItem(const uint8_t* ptr_pos) : ptr_pos_(ptr_pos) {} + + uint32_t GetIndex() const { + return index_; + } + + uint32_t GetAccessFlags() const { + return HiddenApiAccessFlags::RemoveFromDex(access_flags_); + } + + HiddenApiAccessFlags::ApiList DecodeHiddenAccessFlags() const { + return HiddenApiAccessFlags::DecodeFromDex(access_flags_); + } + + bool IsFinal() const { + return (GetAccessFlags() & kAccFinal) != 0; + } + + protected: + // Internal data pointer for reading. + const uint8_t* ptr_pos_ = nullptr; + uint32_t index_ = 0u; + uint32_t access_flags_ = 0u; + }; + + public: + // A decoded version of the method of a class_data_item. + class Method : public BaseItem { + public: + uint32_t GetCodeItemOffset() const { + return code_off_; + } + + InvokeType GetInvokeType(uint32_t class_access_flags) const { + return is_static_or_direct_ + ? GetDirectMethodInvokeType() + : GetVirtualMethodInvokeType(class_access_flags); + } + + MethodReference GetReference() const { + return MethodReference(&dex_file_, GetIndex()); + } + + CodeItemInstructionAccessor GetInstructions() const; + CodeItemDataAccessor GetInstructionsAndData() const; + + const DexFile::CodeItem* GetCodeItem() const; + + bool IsStaticOrDirect() const { + return is_static_or_direct_; + } + + // Unhide the hidden API access flags at the iterator position. TODO: Deprecate. + void UnHideAccessFlags() const; + + private: + explicit Method(const DexFile& dex_file, + const uint8_t* ptr_pos, + bool is_static_or_direct = true) + : BaseItem(ptr_pos), + dex_file_(dex_file), + is_static_or_direct_(is_static_or_direct) {} + + void Read(); + + InvokeType GetDirectMethodInvokeType() const { + return (GetAccessFlags() & kAccStatic) != 0 ? kStatic : kDirect; + } + + InvokeType GetVirtualMethodInvokeType(uint32_t class_access_flags) const { + DCHECK_EQ(GetAccessFlags() & kAccStatic, 0U); + if ((class_access_flags & kAccInterface) != 0) { + return kInterface; + } else if ((GetAccessFlags() & kAccConstructor) != 0) { + return kSuper; + } else { + return kVirtual; + } + } + + // Move to virtual method section. + void NextSection() { + DCHECK(is_static_or_direct_) << "Already in the virtual methods section"; + is_static_or_direct_ = false; + index_ = 0u; + } + + const DexFile& dex_file_; + bool is_static_or_direct_ = true; + uint32_t code_off_ = 0u; + + friend class ClassAccessor; + }; + + // A decoded version of the field of a class_data_item. + class Field : public BaseItem { + public: + explicit Field(const DexFile& dex_file, + const uint8_t* ptr_pos) : BaseItem(ptr_pos), dex_file_(dex_file) {} + + const DexFile& GetDexFile() const { + return dex_file_; + } + + bool IsStatic() const { + return is_static_; + } + + // Unhide the hidden API access flags at the iterator position. TODO: Deprecate. + void UnHideAccessFlags() const; + + private: + void Read(); + + // Move to instance fields section. + void NextSection() { + index_ = 0u; + is_static_ = false; + } + + const DexFile& dex_file_; + bool is_static_ = true; + friend class ClassAccessor; + }; + + template + class DataIterator : public std::iterator { + public: + using value_type = typename std::iterator::value_type; + using difference_type = + typename std::iterator::difference_type; + + DataIterator(const DexFile& dex_file, + uint32_t position, + uint32_t partition_pos, + uint32_t iterator_end, + const uint8_t* ptr_pos) + : data_(dex_file, ptr_pos), + position_(position), + partition_pos_(partition_pos), + iterator_end_(iterator_end) { + ReadData(); + } + + bool IsValid() const { + return position_ < iterator_end_; + } + + // Value after modification. + DataIterator& operator++() { + ++position_; + ReadData(); + return *this; + } + + const value_type& operator*() const { + return data_; + } + + const value_type* operator->() const { + return &data_; + } + + bool operator==(const DataIterator& rhs) const { + DCHECK_EQ(&data_.dex_file_, &rhs.data_.dex_file_) << "Comparing different dex files."; + return position_ == rhs.position_; + } + + bool operator!=(const DataIterator& rhs) const { + return !(*this == rhs); + } + + bool operator<(const DataIterator& rhs) const { + DCHECK_EQ(&data_.dex_file_, &rhs.data_.dex_file_) << "Comparing different dex files."; + return position_ < rhs.position_; + } + + bool operator>(const DataIterator& rhs) const { + return rhs < *this; + } + + bool operator<=(const DataIterator& rhs) const { + return !(rhs < *this); + } + + bool operator>=(const DataIterator& rhs) const { + return !(*this < rhs); + } + + private: + // Read data at current position. + void ReadData() { + if (IsValid()) { + // At the end of the first section, go to the next section. + if (position_ == partition_pos_) { + data_.NextSection(); + } + data_.Read(); + } + } + + DataType data_; + // Iterator position. + uint32_t position_; + // At partition_pos_, we go to the next section. + const uint32_t partition_pos_; + // At iterator_end_, the iterator is no longer valid. + const uint32_t iterator_end_; + }; + + // Not explicit specifically for range-based loops. + ALWAYS_INLINE ClassAccessor(const ClassIteratorData& data); + + 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; + + // Iterator data is not very iterator friendly, use visitors to get around this. + template + void VisitFieldsAndMethods(const StaticFieldVisitor& static_field_visitor, + const InstanceFieldVisitor& instance_field_visitor, + const DirectMethodVisitor& direct_method_visitor, + const VirtualMethodVisitor& virtual_method_visitor) const; + + template + void VisitMethods(const DirectMethodVisitor& direct_method_visitor, + const VirtualMethodVisitor& virtual_method_visitor) const; + + template + void VisitFields(const StaticFieldVisitor& static_field_visitor, + const InstanceFieldVisitor& instance_field_visitor) const; + + // Return the iteration range for all the fields. + IterationRange> GetFields() const; + + // Return the iteration range for all the static fields. + IterationRange> GetStaticFields() const; + + // Return the iteration range for all the instance fields. + IterationRange> GetInstanceFields() const; + + // Return the iteration range for all the methods. + IterationRange> GetMethods() const; + + // Return the iteration range for the direct methods. + IterationRange> GetDirectMethods() const; + + // Return the iteration range for the virtual methods. + IterationRange> GetVirtualMethods() const; + + uint32_t NumStaticFields() const { + return num_static_fields_; + } + + uint32_t NumInstanceFields() const { + return num_instance_fields_; + } + + uint32_t NumFields() const { + return NumStaticFields() + NumInstanceFields(); + } + + uint32_t NumDirectMethods() const { + return num_direct_methods_; + } + + uint32_t NumVirtualMethods() const { + return num_virtual_methods_; + } + + uint32_t NumMethods() const { + return NumDirectMethods() + NumVirtualMethods(); + } + + const char* GetDescriptor() const; + + dex::TypeIndex GetClassIdx() const; + + const DexFile& GetDexFile() const { + return dex_file_; + } + + bool HasClassData() const { + 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. + template + void VisitMembers(size_t count, const Visitor& visitor, DataType* data) const + NO_THREAD_SAFETY_ANALYSIS; + + // Return an iteration range for the first fields. + IterationRange> GetFieldsInternal(size_t count) const; + + // Return an iteration range for the first methods. + IterationRange> GetMethodsInternal(size_t count) const; + + const DexFile& dex_file_; + 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; + const uint32_t num_direct_methods_ = 0u; + const uint32_t num_virtual_methods_ = 0u; +}; + +} // namespace art + +#endif // ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_H_ diff --git a/libdexfile/dex/class_accessor_test.cc b/libdexfile/dex/class_accessor_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..1f30ae54d6b4833125734a445c6237f57126b464 --- /dev/null +++ b/libdexfile/dex/class_accessor_test.cc @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dex/class_accessor-inl.h" + +#include "base/common_art_test.h" + +namespace art { + +class ClassAccessorTest : public CommonArtTest {}; + +TEST_F(ClassAccessorTest, TestVisiting) { + std::vector> dex_files( + OpenDexFiles(GetLibCoreDexFileNames()[0].c_str())); + ASSERT_GT(dex_files.size(), 0u); + for (const std::unique_ptr& dex_file : dex_files) { + 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(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(); + auto fields = accessor.GetFields(); + auto method_it = methods.begin(); + auto field_it = fields.begin(); + auto instance_fields = accessor.GetInstanceFields(); + auto instance_field_it = instance_fields.begin(); + accessor.VisitFieldsAndMethods( + // Static fields. + [&](const ClassAccessor::Field& field) { + EXPECT_TRUE(field.IsStatic()); + EXPECT_TRUE(field_it->IsStatic()); + EXPECT_EQ(field.GetIndex(), field_it->GetIndex()); + EXPECT_EQ(field.GetAccessFlags(), field_it->GetAccessFlags()); + ++field_it; + }, + // Instance fields. + [&](const ClassAccessor::Field& field) { + EXPECT_FALSE(field.IsStatic()); + EXPECT_FALSE(field_it->IsStatic()); + EXPECT_EQ(field.GetIndex(), field_it->GetIndex()); + EXPECT_EQ(field.GetAccessFlags(), field_it->GetAccessFlags()); + EXPECT_EQ(field.GetIndex(), instance_field_it->GetIndex()); + EXPECT_EQ(field.GetAccessFlags(), instance_field_it->GetAccessFlags()); + ++field_it; + ++instance_field_it; + }, + // Direct methods. + [&](const ClassAccessor::Method& method) { + EXPECT_TRUE(method.IsStaticOrDirect()); + EXPECT_EQ(method.IsStaticOrDirect(), method_it->IsStaticOrDirect()); + EXPECT_EQ(method.GetIndex(), method_it->GetIndex()); + EXPECT_EQ(method.GetAccessFlags(), method_it->GetAccessFlags()); + EXPECT_EQ(method.GetCodeItem(), method_it->GetCodeItem()); + ++method_it; + }, + // Virtual methods. + [&](const ClassAccessor::Method& method) { + EXPECT_FALSE(method.IsStaticOrDirect()); + EXPECT_EQ(method.IsStaticOrDirect(), method_it->IsStaticOrDirect()); + EXPECT_EQ(method.GetIndex(), method_it->GetIndex()); + EXPECT_EQ(method.GetAccessFlags(), method_it->GetAccessFlags()); + EXPECT_EQ(method.GetCodeItem(), method_it->GetCodeItem()); + ++method_it; + }); + ASSERT_TRUE(field_it == fields.end()); + ASSERT_TRUE(method_it == methods.end()); + ASSERT_TRUE(instance_field_it == instance_fields.end()); + } + EXPECT_EQ(class_def_idx, dex_file->NumClassDefs()); + } +} + +} // namespace art diff --git a/libdexfile/dex/class_iterator.h b/libdexfile/dex/class_iterator.h new file mode 100644 index 0000000000000000000000000000000000000000..477c93b50884a1995294cfd4686f260434b7aae8 --- /dev/null +++ b/libdexfile/dex/class_iterator.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_LIBDEXFILE_DEX_CLASS_ITERATOR_H_ +#define ART_LIBDEXFILE_DEX_CLASS_ITERATOR_H_ + +#include "base/logging.h" + +namespace art { + +class ClassAccessor; +class ClassIterator; +class DexFile; + +// Holder class, used to construct ClassAccessors. +class ClassIteratorData { + public: + ClassIteratorData(const DexFile& dex_file, uint32_t class_def_idx) + : dex_file_(dex_file), + class_def_idx_(class_def_idx) {} + + private: + const DexFile& dex_file_; + uint32_t class_def_idx_ = 0u; + + friend class ClassAccessor; + friend class ClassIterator; +}; + +// Iterator for visiting classes in a Dex file. +class ClassIterator : public std::iterator { + public: + using value_type = std::iterator::value_type; + using difference_type = std::iterator::difference_type; + + ClassIterator(const DexFile& dex_file, uint32_t class_def_idx) + : data_(dex_file, class_def_idx) {} + + // Value after modification. + ClassIterator& operator++() { + ++data_.class_def_idx_; + return *this; + } + + // Value before modification. + ClassIterator operator++(int) { + ClassIterator temp = *this; + ++*this; + return temp; + } + + const value_type& operator*() const { + return data_; + } + + bool operator==(const ClassIterator& rhs) const { + DCHECK_EQ(&data_.dex_file_, &rhs.data_.dex_file_) << "Comparing different dex files."; + return data_.class_def_idx_ == rhs.data_.class_def_idx_; + } + + bool operator!=(const ClassIterator& rhs) const { + return !(*this == rhs); + } + + bool operator<(const ClassIterator& rhs) const { + DCHECK_EQ(&data_.dex_file_, &rhs.data_.dex_file_) << "Comparing different dex files."; + return data_.class_def_idx_ < rhs.data_.class_def_idx_; + } + + bool operator>(const ClassIterator& rhs) const { + return rhs < *this; + } + + bool operator<=(const ClassIterator& rhs) const { + return !(rhs < *this); + } + + bool operator>=(const ClassIterator& rhs) const { + return !(*this < rhs); + } + + protected: + ClassIteratorData data_; +}; + +} // namespace art + +#endif // ART_LIBDEXFILE_DEX_CLASS_ITERATOR_H_ diff --git a/libdexfile/dex/code_item_accessors.h b/libdexfile/dex/code_item_accessors.h index ba7c126ed841fcbeabe8408b010ae37a04dfd00a..5786d3f6111e94480ecd96ad1237c450e5a21745 100644 --- a/libdexfile/dex/code_item_accessors.h +++ b/libdexfile/dex/code_item_accessors.h @@ -47,6 +47,11 @@ class CodeItemInstructionAccessor { return insns_size_in_code_units_; } + uint32_t InsnsSizeInBytes() const { + static constexpr uint32_t kCodeUnitSizeInBytes = 2u; + return insns_size_in_code_units_ * kCodeUnitSizeInBytes; + } + const uint16_t* Insns() const { return insns_; } diff --git a/libdexfile/dex/descriptors_names.cc b/libdexfile/dex/descriptors_names.cc index 8124e7256f11620f6bea945f89cb51ae7ee6d599..e338b55a9fa372e8bd455b5c0f67085d5b08d685 100644 --- a/libdexfile/dex/descriptors_names.cc +++ b/libdexfile/dex/descriptors_names.cc @@ -403,22 +403,6 @@ bool IsValidDescriptor(const char* s) { return IsValidClassName(s); } -void Split(const std::string& s, char separator, std::vector* result) { - const char* p = s.data(); - const char* end = p + s.size(); - while (p != end) { - if (*p == separator) { - ++p; - } else { - const char* start = p; - while (++p != end && *p != separator) { - // Skip to the next occurrence of the separator. - } - result->push_back(std::string(start, p - start)); - } - } -} - std::string PrettyDescriptor(Primitive::Type type) { return PrettyDescriptor(Primitive::Descriptor(type)); } diff --git a/libdexfile/dex/dex_file-inl.h b/libdexfile/dex/dex_file-inl.h index d1b32007c3e12a79329f43b63e432917b221cb63..09668594dde82a7bddd7439386547090e4da67bd 100644 --- a/libdexfile/dex/dex_file-inl.h +++ b/libdexfile/dex/dex_file-inl.h @@ -17,11 +17,14 @@ #ifndef ART_LIBDEXFILE_DEX_DEX_FILE_INL_H_ #define ART_LIBDEXFILE_DEX_DEX_FILE_INL_H_ +#include "dex_file.h" + #include "base/casts.h" #include "base/leb128.h" #include "base/stringpiece.h" +#include "class_iterator.h" #include "compact_dex_file.h" -#include "dex_file.h" +#include "dex_instruction_iterator.h" #include "invoke_type.h" #include "standard_dex_file.h" @@ -127,7 +130,7 @@ inline const char* DexFile::GetReturnTypeDescriptor(const ProtoId& proto_id) con return StringByTypeIdx(proto_id.return_type_idx_); } -inline const char* DexFile::GetShorty(uint32_t proto_idx) const { +inline const char* DexFile::GetShorty(dex::ProtoIndex proto_idx) const { const ProtoId& proto_id = GetProtoId(proto_idx); return StringDataByIdx(proto_id.shorty_idx_); } @@ -527,6 +530,10 @@ inline void DexFile::ClassDef::VisitMethods(const DexFile* dex_file, const Visit } } +inline IterationRange DexFile::GetClasses() const { + return { ClassIterator(*this, 0u), ClassIterator(*this, NumClassDefs()) }; +} + } // namespace art #endif // ART_LIBDEXFILE_DEX_DEX_FILE_INL_H_ diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc index 1f471106df4e94493d61d57aa7b5b0dd4f64a05d..f1f896058cc692550c7cc1c91088680344b59601 100644 --- a/libdexfile/dex/dex_file.cc +++ b/libdexfile/dex/dex_file.cc @@ -45,19 +45,18 @@ static_assert(std::is_trivially_copyable::value, "StringIndex static_assert(sizeof(dex::TypeIndex) == sizeof(uint16_t), "TypeIndex size is wrong"); static_assert(std::is_trivially_copyable::value, "TypeIndex not trivial"); -void DexFile::UnHideAccessFlags(ClassDataItemIterator& class_it) { - uint8_t* data = const_cast(class_it.DataPointer()); - uint32_t new_flag = class_it.GetMemberAccessFlags(); - bool is_method = class_it.IsAtMethod(); +void DexFile::UnHideAccessFlags(uint8_t* data_ptr, + uint32_t new_access_flags, + bool is_method) { // Go back 1 uleb to start. - data = ReverseSearchUnsignedLeb128(data); + data_ptr = ReverseSearchUnsignedLeb128(data_ptr); if (is_method) { // Methods have another uleb field before the access flags - data = ReverseSearchUnsignedLeb128(data); + data_ptr = ReverseSearchUnsignedLeb128(data_ptr); } - DCHECK_EQ(HiddenApiAccessFlags::RemoveFromDex(DecodeUnsignedLeb128WithoutMovingCursor(data)), - new_flag); - UpdateUnsignedLeb128(data, new_flag); + DCHECK_EQ(HiddenApiAccessFlags::RemoveFromDex(DecodeUnsignedLeb128WithoutMovingCursor(data_ptr)), + new_access_flags); + UpdateUnsignedLeb128(data_ptr, new_access_flags); } uint32_t DexFile::CalculateChecksum() const { @@ -281,7 +280,7 @@ const DexFile::MethodId* DexFile::FindMethodId(const DexFile::TypeId& declaring_ // Binary search MethodIds knowing that they are sorted by class_idx, name_idx then proto_idx const dex::TypeIndex class_idx = GetIndexForTypeId(declaring_klass); const dex::StringIndex name_idx = GetIndexForStringId(name); - const uint16_t proto_idx = GetIndexForProtoId(signature); + const dex::ProtoIndex proto_idx = GetIndexForProtoId(signature); int32_t lo = 0; int32_t hi = NumMethodIds() - 1; while (hi >= lo) { @@ -349,25 +348,6 @@ const DexFile::TypeId* DexFile::FindTypeId(const char* string) const { return nullptr; } -const DexFile::StringId* DexFile::FindStringId(const uint16_t* string, size_t length) const { - int32_t lo = 0; - int32_t hi = NumStringIds() - 1; - while (hi >= lo) { - int32_t mid = (hi + lo) / 2; - const DexFile::StringId& str_id = GetStringId(dex::StringIndex(mid)); - const char* str = GetStringData(str_id); - int compare = CompareModifiedUtf8ToUtf16AsCodePointValues(str, string, length); - if (compare > 0) { - lo = mid + 1; - } else if (compare < 0) { - hi = mid - 1; - } else { - return &str_id; - } - } - return nullptr; -} - const DexFile::TypeId* DexFile::FindTypeId(dex::StringIndex string_idx) const { int32_t lo = 0; int32_t hi = NumTypeIds() - 1; @@ -392,7 +372,8 @@ const DexFile::ProtoId* DexFile::FindProtoId(dex::TypeIndex return_type_idx, int32_t hi = NumProtoIds() - 1; while (hi >= lo) { int32_t mid = (hi + lo) / 2; - const DexFile::ProtoId& proto = GetProtoId(mid); + const dex::ProtoIndex proto_idx = static_cast(mid); + const DexFile::ProtoId& proto = GetProtoId(proto_idx); int compare = return_type_idx.index_ - proto.return_type_idx_.index_; if (compare == 0) { DexFileParameterIterator it(*this, proto); @@ -624,6 +605,15 @@ std::string DexFile::PrettyType(dex::TypeIndex type_idx) const { 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) { @@ -796,6 +786,11 @@ void EncodedArrayValueIterator::Next() { namespace dex { +std::ostream& operator<<(std::ostream& os, const ProtoIndex& index) { + os << "ProtoIndex[" << index.index_ << "]"; + return os; +} + std::ostream& operator<<(std::ostream& os, const StringIndex& index) { os << "StringIndex[" << index.index_ << "]"; return os; diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index 683a8243edc3f08516417e978687c225be4cafdd..4e88ef69857a4888e2e738830c58c0fd80806ba4 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -27,8 +27,8 @@ #include "base/iteration_range.h" #include "base/macros.h" #include "base/value_object.h" +#include "class_iterator.h" #include "dex_file_types.h" -#include "dex_instruction_iterator.h" #include "hidden_api_access_flags.h" #include "jni.h" #include "modifiers.h" @@ -37,6 +37,7 @@ namespace art { class ClassDataItemIterator; class CompactDexFile; +class DexInstructionIterator; enum InvokeType : uint32_t; class MemMap; class OatDexFile; @@ -189,7 +190,7 @@ class DexFile { // Raw method_id_item. struct MethodId { dex::TypeIndex class_idx_; // index into type_ids_ array for defining class - uint16_t proto_idx_; // index into proto_ids_ array for method prototype + dex::ProtoIndex proto_idx_; // index into proto_ids_ array for method prototype dex::StringIndex name_idx_; // index into string_ids_ array for method name private: @@ -504,9 +505,6 @@ class DexFile { const TypeId* FindTypeId(const char* string) const; - // Looks up a string id for a given utf16 string. - const StringId* FindStringId(const uint16_t* string, size_t length) const; - // Returns the number of type identifiers in the .dex file. uint32_t NumTypeIds() const { DCHECK(header_ != nullptr) << GetLocation(); @@ -695,15 +693,15 @@ class DexFile { } // Returns the ProtoId at the specified index. - const ProtoId& GetProtoId(uint16_t idx) const { - DCHECK_LT(idx, NumProtoIds()) << GetLocation(); - return proto_ids_[idx]; + const ProtoId& GetProtoId(dex::ProtoIndex idx) const { + DCHECK_LT(idx.index_, NumProtoIds()) << GetLocation(); + return proto_ids_[idx.index_]; } - uint16_t GetIndexForProtoId(const ProtoId& proto_id) const { + dex::ProtoIndex GetIndexForProtoId(const ProtoId& proto_id) const { CHECK_GE(&proto_id, proto_ids_) << GetLocation(); CHECK_LT(&proto_id, proto_ids_ + header_->proto_ids_size_) << GetLocation(); - return &proto_id - proto_ids_; + return dex::ProtoIndex(&proto_id - proto_ids_); } // Looks up a proto id for a given return type and signature type list @@ -725,7 +723,7 @@ class DexFile { const Signature CreateSignature(const StringPiece& signature) const; // Returns the short form method descriptor for the given prototype. - const char* GetShorty(uint32_t proto_idx) const; + const char* GetShorty(dex::ProtoIndex proto_idx) const; const TypeList* GetProtoParameters(const ProtoId& proto_id) const { return DataPointer(proto_id.parameters_off_); @@ -739,6 +737,8 @@ class DexFile { 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. @@ -1012,8 +1012,10 @@ class DexFile { return container_.get(); } - // Changes the dex file pointed to by class_it to not have any hiddenapi flags. - static void UnHideAccessFlags(ClassDataItemIterator& class_it); + // Changes the dex class data pointed to by data_ptr it to not have any hiddenapi flags. + static void UnHideAccessFlags(uint8_t* data_ptr, uint32_t new_access_flags, bool is_method); + + inline IterationRange GetClasses() const; protected: // First Dex format version supporting default methods. @@ -1198,6 +1200,9 @@ class ClassDataItemIterator { bool IsAtMethod() const { return pos_ >= EndOfInstanceFieldsPos(); } + bool IsAtVirtualMethod() const { + return pos_ >= EndOfDirectMethodsPos(); + } bool HasNextStaticField() const { return pos_ < EndOfStaticFieldsPos(); } @@ -1349,9 +1354,6 @@ class ClassDataItemIterator { uint32_t field_idx_delta_; // delta of index into the field_ids array for FieldId uint32_t access_flags_; // access flags for the field ClassDataField() : field_idx_delta_(0), access_flags_(0) {} - - private: - DISALLOW_COPY_AND_ASSIGN(ClassDataField); }; ClassDataField field_; @@ -1364,9 +1366,6 @@ class ClassDataItemIterator { uint32_t access_flags_; uint32_t code_off_; ClassDataMethod() : method_idx_delta_(0), access_flags_(0), code_off_(0) {} - - private: - DISALLOW_COPY_AND_ASSIGN(ClassDataMethod); }; ClassDataMethod method_; @@ -1377,7 +1376,6 @@ class ClassDataItemIterator { size_t pos_; // integral number of items passed const uint8_t* ptr_pos_; // pointer into stream of class_data_item uint32_t last_idx_; // last read field or method index to apply delta to - DISALLOW_IMPLICIT_CONSTRUCTORS(ClassDataItemIterator); }; class EncodedArrayValueIterator { diff --git a/libdexfile/dex/dex_file_tracking_registrar.cc b/libdexfile/dex/dex_file_tracking_registrar.cc index 78ea9c16cb8826d4efa4016be03d31aa888eea82..551bea108c43fcdde213653cd5ecde9e35f978b6 100644 --- a/libdexfile/dex/dex_file_tracking_registrar.cc +++ b/libdexfile/dex/dex_file_tracking_registrar.cc @@ -130,7 +130,8 @@ inline void SetRegistrationRange(const void* begin, size_t size, bool should_poi MEMORY_TOOL_MAKE_NOACCESS(begin, size); } else { // Note: MEMORY_TOOL_MAKE_UNDEFINED has the same functionality with Address - // Sanitizer. The difference has not been tested with Valgrind + // Sanitizer. + // Historical note: The difference has not been tested with Valgrind. MEMORY_TOOL_MAKE_DEFINED(begin, size); } } diff --git a/libdexfile/dex/dex_file_types.h b/libdexfile/dex/dex_file_types.h index 2bb70ff261b8f81705b065f4ed75b9c84cde6bf9..d4fb3de5044326ba1418f545d4d92dd74efea8f9 100644 --- a/libdexfile/dex/dex_file_types.h +++ b/libdexfile/dex/dex_file_types.h @@ -25,72 +25,66 @@ namespace dex { constexpr uint32_t kDexNoIndex = 0xFFFFFFFF; -class StringIndex { +template +class DexIndex { public: - uint32_t index_; + T index_; - constexpr StringIndex() : index_(std::numeric_limits::max()) {} - explicit constexpr StringIndex(uint32_t idx) : index_(idx) {} + constexpr DexIndex() : index_(std::numeric_limits::max()) {} + explicit constexpr DexIndex(T idx) : index_(idx) {} bool IsValid() const { return index_ != std::numeric_limits::max(); } - static StringIndex Invalid() { - return StringIndex(std::numeric_limits::max()); + static constexpr DexIndex Invalid() { + return DexIndex(std::numeric_limits::max()); } - - bool operator==(const StringIndex& other) const { + bool operator==(const DexIndex& other) const { return index_ == other.index_; } - bool operator!=(const StringIndex& other) const { + bool operator!=(const DexIndex& other) const { return index_ != other.index_; } - bool operator<(const StringIndex& other) const { + bool operator<(const DexIndex& other) const { return index_ < other.index_; } - bool operator<=(const StringIndex& other) const { + bool operator<=(const DexIndex& other) const { return index_ <= other.index_; } - bool operator>(const StringIndex& other) const { + bool operator>(const DexIndex& other) const { return index_ > other.index_; } - bool operator>=(const StringIndex& other) const { + bool operator>=(const DexIndex& other) const { return index_ >= other.index_; } }; -std::ostream& operator<<(std::ostream& os, const StringIndex& index); -class TypeIndex { +class ProtoIndex : public DexIndex { public: - uint16_t index_; - - constexpr TypeIndex() : index_(std::numeric_limits::max()) {} - explicit constexpr TypeIndex(uint16_t idx) : index_(idx) {} - - bool IsValid() const { - return index_ != std::numeric_limits::max(); - } - static TypeIndex Invalid() { - return TypeIndex(std::numeric_limits::max()); + ProtoIndex() {} + explicit constexpr ProtoIndex(uint16_t index) : DexIndex(index) {} + static constexpr ProtoIndex Invalid() { + return ProtoIndex(std::numeric_limits::max()); } +}; +std::ostream& operator<<(std::ostream& os, const ProtoIndex& index); - bool operator==(const TypeIndex& other) const { - return index_ == other.index_; - } - bool operator!=(const TypeIndex& other) const { - return index_ != other.index_; - } - bool operator<(const TypeIndex& other) const { - return index_ < other.index_; - } - bool operator<=(const TypeIndex& other) const { - return index_ <= other.index_; - } - bool operator>(const TypeIndex& other) const { - return index_ > other.index_; +class StringIndex : public DexIndex { + public: + StringIndex() {} + explicit constexpr StringIndex(uint32_t index) : DexIndex(index) {} + static constexpr StringIndex Invalid() { + return StringIndex(std::numeric_limits::max()); } - bool operator>=(const TypeIndex& other) const { - return index_ >= other.index_; +}; +std::ostream& operator<<(std::ostream& os, const StringIndex& index); + +class TypeIndex : public DexIndex { + public: + TypeIndex() {} + explicit constexpr TypeIndex(uint16_t index) : DexIndex(index) {} + static constexpr TypeIndex Invalid() { + return TypeIndex(std::numeric_limits::max()); } }; std::ostream& operator<<(std::ostream& os, const TypeIndex& index); @@ -100,15 +94,21 @@ std::ostream& operator<<(std::ostream& os, const TypeIndex& index); namespace std { +template<> struct hash { + size_t operator()(const art::dex::ProtoIndex& index) const { + return hash()(index.index_); + } +}; + template<> struct hash { size_t operator()(const art::dex::StringIndex& index) const { - return hash()(index.index_); + return hash()(index.index_); } }; template<> struct hash { size_t operator()(const art::dex::TypeIndex& index) const { - return hash()(index.index_); + return hash()(index.index_); } }; diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc index 68bd19ea86ead5c94333d7f5af2f5cceaaeaadcc..fda6376454b271089056c97ab76682333f2a93a6 100644 --- a/libdexfile/dex/dex_file_verifier.cc +++ b/libdexfile/dex/dex_file_verifier.cc @@ -18,7 +18,6 @@ #include -#include #include #include "android-base/stringprintf.h" @@ -106,66 +105,6 @@ const char* DexFileVerifier::CheckLoadStringByIdx(dex::StringIndex idx, const ch return dex_file_->StringDataByIdx(idx); } -// Try to find the name of the method with the given index. We do not want to rely on DexFile -// infrastructure at this point, so do it all by hand. begin and header correspond to begin_ and -// header_ of the DexFileVerifier. str will contain the pointer to the method name on success -// (flagged by the return value), otherwise error_msg will contain an error string. -static bool FindMethodName(uint32_t method_index, - const uint8_t* begin, - const DexFile::Header* header, - const char** str, - std::string* error_msg) { - if (method_index >= header->method_ids_size_) { - *error_msg = "Method index not available for method flags verification"; - return false; - } - uint32_t string_idx = - (reinterpret_cast(begin + header->method_ids_off_) + - method_index)->name_idx_.index_; - if (string_idx >= header->string_ids_size_) { - *error_msg = "String index not available for method flags verification"; - return false; - } - uint32_t string_off = - (reinterpret_cast(begin + header->string_ids_off_) + string_idx)-> - string_data_off_; - if (string_off >= header->file_size_) { - *error_msg = "String offset out of bounds for method flags verification"; - return false; - } - const uint8_t* str_data_ptr = begin + string_off; - uint32_t dummy; - if (!DecodeUnsignedLeb128Checked(&str_data_ptr, begin + header->file_size_, &dummy)) { - *error_msg = "String size out of bounds for method flags verification"; - return false; - } - *str = reinterpret_cast(str_data_ptr); - return true; -} - -// Gets constructor flags based on the |method_name|. Returns true if -// method_name is either or and sets -// |constructor_flags_by_name| appropriately. Otherwise set -// |constructor_flags_by_name| to zero and returns whether -// |method_name| is valid. -bool GetConstructorFlagsForMethodName(const char* method_name, - uint32_t* constructor_flags_by_name) { - if (method_name[0] != '<') { - *constructor_flags_by_name = 0; - return true; - } - if (strcmp(method_name + 1, "clinit>") == 0) { - *constructor_flags_by_name = kAccStatic | kAccConstructor; - return true; - } - if (strcmp(method_name + 1, "init>") == 0) { - *constructor_flags_by_name = kAccConstructor; - return true; - } - *constructor_flags_by_name = 0; - return false; -} - const char* DexFileVerifier::CheckLoadStringByTypeIdx(dex::TypeIndex type_idx, const char* error_string) { if (UNLIKELY(!CheckIndex(type_idx.index_, dex_file_->NumTypeIds(), error_string))) { @@ -188,8 +127,9 @@ const DexFile::MethodId* DexFileVerifier::CheckLoadMethodId(uint32_t idx, const return &dex_file_->GetMethodId(idx); } -const DexFile::ProtoId* DexFileVerifier::CheckLoadProtoId(uint32_t idx, const char* err_string) { - if (UNLIKELY(!CheckIndex(idx, dex_file_->NumProtoIds(), err_string))) { +const DexFile::ProtoId* DexFileVerifier::CheckLoadProtoId(dex::ProtoIndex idx, + const char* err_string) { + if (UNLIKELY(!CheckIndex(idx.index_, dex_file_->NumProtoIds(), err_string))) { return nullptr; } return &dex_file_->GetProtoId(idx); @@ -674,18 +614,19 @@ bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, uint32_t class_access_flags, dex::TypeIndex class_type_index, uint32_t code_offset, - std::unordered_set* direct_method_indexes, + ClassDataItemIterator* direct_it, bool expect_direct) { - DCHECK(direct_method_indexes != nullptr); + DCHECK_EQ(expect_direct, direct_it == nullptr); // Check for overflow. if (!CheckIndex(idx, header_->method_ids_size_, "class_data_item method_idx")) { return false; } + const DexFile::MethodId& method_id = + *(reinterpret_cast(begin_ + header_->method_ids_off_) + idx); + // Check that it's the right class. - dex::TypeIndex my_class_index = - (reinterpret_cast(begin_ + header_->method_ids_off_) + idx)-> - class_idx_; + dex::TypeIndex my_class_index = method_id.class_idx_; if (class_type_index != my_class_index) { ErrorStringPrintf("Method's class index unexpected, %" PRIu16 " vs %" PRIu16, my_class_index.index_, @@ -694,24 +635,39 @@ bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, } // Check that it's not defined as both direct and virtual. - if (expect_direct) { - direct_method_indexes->insert(idx); - } else if (direct_method_indexes->find(idx) != direct_method_indexes->end()) { - ErrorStringPrintf("Found virtual method with same index as direct method: %d", idx); - return false; + if (!expect_direct) { + // The direct methods are already known to be in ascending index order. So just keep up + // with the current index. + for (; direct_it->HasNextDirectMethod(); direct_it->Next()) { + uint32_t direct_idx = direct_it->GetMemberIndex(); + if (direct_idx > idx) { + break; + } + if (direct_idx == idx) { + ErrorStringPrintf("Found virtual method with same index as direct method: %d", idx); + return false; + } + } } std::string error_msg; - const char* method_name; - if (!FindMethodName(idx, begin_, header_, &method_name, &error_msg)) { - ErrorStringPrintf("%s", error_msg.c_str()); - return false; - } - uint32_t constructor_flags_by_name = 0; - if (!GetConstructorFlagsForMethodName(method_name, &constructor_flags_by_name)) { - ErrorStringPrintf("Bad method name: %s", method_name); - return false; + { + uint32_t string_idx = method_id.name_idx_.index_; + if (!CheckIndex(string_idx, header_->string_ids_size_, "method flags verification")) { + return false; + } + if (UNLIKELY(string_idx < angle_bracket_end_index_) && + string_idx >= angle_bracket_start_index_) { + if (string_idx == angle_clinit_angle_index_) { + constructor_flags_by_name = kAccStatic | kAccConstructor; + } else if (string_idx == angle_init_angle_index_) { + constructor_flags_by_name = kAccConstructor; + } else { + ErrorStringPrintf("Bad method name for method index %u", idx); + return false; + } + } } bool has_code = (code_offset != 0); @@ -986,31 +942,16 @@ bool DexFileVerifier::FindClassIndexAndDef(uint32_t index, return false; } -bool DexFileVerifier::CheckOrderAndGetClassDef(bool is_field, - const char* type_descr, - uint32_t curr_index, - uint32_t prev_index, - bool* have_class, - dex::TypeIndex* class_type_index, - const DexFile::ClassDef** class_def) { - if (curr_index < prev_index) { +bool DexFileVerifier::CheckOrder(const char* type_descr, + uint32_t curr_index, + uint32_t prev_index) { + if (UNLIKELY(curr_index < prev_index)) { ErrorStringPrintf("out-of-order %s indexes %" PRIu32 " and %" PRIu32, type_descr, prev_index, curr_index); return false; } - - if (!*have_class) { - *have_class = FindClassIndexAndDef(curr_index, is_field, class_type_index, class_def); - if (!*have_class) { - // Should have really found one. - ErrorStringPrintf("could not find declaring class for %s index %" PRIu32, - type_descr, - curr_index); - return false; - } - } return true; } @@ -1115,20 +1056,29 @@ bool DexFileVerifier::CheckIntraClassDataItemFields(ClassDataItemIterator* it, dex::TypeIndex* class_type_index, const DexFile::ClassDef** class_def) { DCHECK(it != nullptr); + constexpr const char* kTypeDescr = kStatic ? "static field" : "instance field"; + // These calls use the raw access flags to check whether the whole dex field is valid. + + if (!*have_class && (kStatic ? it->HasNextStaticField() : it->HasNextInstanceField())) { + *have_class = FindClassIndexAndDef(it->GetMemberIndex(), true, class_type_index, class_def); + if (!*have_class) { + // Should have really found one. + ErrorStringPrintf("could not find declaring class for %s index %" PRIu32, + kTypeDescr, + it->GetMemberIndex()); + return false; + } + } + DCHECK(*class_def != nullptr || + !(kStatic ? it->HasNextStaticField() : it->HasNextInstanceField())); + uint32_t prev_index = 0; for (; kStatic ? it->HasNextStaticField() : it->HasNextInstanceField(); it->Next()) { uint32_t curr_index = it->GetMemberIndex(); - if (!CheckOrderAndGetClassDef(true, - kStatic ? "static field" : "instance field", - curr_index, - prev_index, - have_class, - class_type_index, - class_def)) { + if (!CheckOrder(kTypeDescr, curr_index, prev_index)) { return false; } - DCHECK(class_def != nullptr); if (!CheckClassDataItemField(curr_index, it->GetRawMemberAccessFlags(), (*class_def)->access_flags_, @@ -1146,29 +1096,38 @@ bool DexFileVerifier::CheckIntraClassDataItemFields(ClassDataItemIterator* it, template bool DexFileVerifier::CheckIntraClassDataItemMethods( ClassDataItemIterator* it, - std::unordered_set* direct_method_indexes, + ClassDataItemIterator* direct_it, bool* have_class, dex::TypeIndex* class_type_index, const DexFile::ClassDef** class_def) { + DCHECK(it != nullptr); + constexpr const char* kTypeDescr = kDirect ? "direct method" : "virtual method"; + + if (!*have_class && (kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod())) { + *have_class = FindClassIndexAndDef(it->GetMemberIndex(), false, class_type_index, class_def); + if (!*have_class) { + // Should have really found one. + ErrorStringPrintf("could not find declaring class for %s index %" PRIu32, + kTypeDescr, + it->GetMemberIndex()); + return false; + } + } + DCHECK(*class_def != nullptr || + !(kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod())); + uint32_t prev_index = 0; for (; kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod(); it->Next()) { uint32_t curr_index = it->GetMemberIndex(); - if (!CheckOrderAndGetClassDef(false, - kDirect ? "direct method" : "virtual method", - curr_index, - prev_index, - have_class, - class_type_index, - class_def)) { + if (!CheckOrder(kTypeDescr, curr_index, prev_index)) { return false; } - DCHECK(class_def != nullptr); if (!CheckClassDataItemMethod(curr_index, it->GetRawMemberAccessFlags(), (*class_def)->access_flags_, *class_type_index, it->GetMethodCodeItemOffset(), - direct_method_indexes, + direct_it, kDirect)) { return false; } @@ -1181,7 +1140,6 @@ bool DexFileVerifier::CheckIntraClassDataItemMethods( bool DexFileVerifier::CheckIntraClassDataItem() { ClassDataItemIterator it(*dex_file_, ptr_); - std::unordered_set direct_method_indexes; // This code is complicated by the fact that we don't directly know which class this belongs to. // So we need to explicitly search with the first item we find (either field or method), and then, @@ -1205,15 +1163,17 @@ bool DexFileVerifier::CheckIntraClassDataItem() { } // Check methods. + ClassDataItemIterator direct_it = it; + if (!CheckIntraClassDataItemMethods(&it, - &direct_method_indexes, + nullptr /* direct_it */, &have_class, &class_type_index, &class_def)) { return false; } if (!CheckIntraClassDataItemMethods(&it, - &direct_method_indexes, + &direct_it, &have_class, &class_type_index, &class_def)) { @@ -1288,7 +1248,20 @@ bool DexFileVerifier::CheckIntraCodeItem() { return false; } - std::unique_ptr handler_offsets(new uint32_t[handlers_size]); + // Avoid an expensive allocation, if possible. + std::unique_ptr handler_offsets_uptr; + uint32_t* handler_offsets; + constexpr size_t kAllocaMaxSize = 1024; + if (handlers_size < kAllocaMaxSize/sizeof(uint32_t)) { + // Note: Clang does not specify alignment guarantees for alloca. So align by hand. + handler_offsets = + AlignUp(reinterpret_cast(alloca((handlers_size + 1) * sizeof(uint32_t))), + alignof(uint32_t[])); + } else { + handler_offsets_uptr.reset(new uint32_t[handlers_size]); + handler_offsets = handler_offsets_uptr.get(); + } + if (!CheckAndGetHandlerOffsets(code_item, &handler_offsets[0], handlers_size)) { return false; } @@ -1613,11 +1586,11 @@ bool DexFileVerifier::CheckIntraAnnotationsDirectoryItem() { return true; } -bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_count, - DexFile::MapItemType type) { +template +bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_count) { // Get the right alignment mask for the type of section. size_t alignment_mask; - switch (type) { + switch (kType) { case DexFile::kDexTypeClassDataItem: case DexFile::kDexTypeStringDataItem: case DexFile::kDexTypeDebugInfoItem: @@ -1635,13 +1608,13 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_c size_t aligned_offset = (offset + alignment_mask) & ~alignment_mask; // Check the padding between items. - if (!CheckPadding(offset, aligned_offset, type)) { + if (!CheckPadding(offset, aligned_offset, kType)) { return false; } // Check depending on the section type. const uint8_t* start_ptr = ptr_; - switch (type) { + switch (kType) { case DexFile::kDexTypeStringIdItem: { if (!CheckListSize(ptr_, 1, sizeof(DexFile::StringId), "string_ids")) { return false; @@ -1764,17 +1737,17 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_c } if (start_ptr == ptr_) { - ErrorStringPrintf("Unknown map item type %x", type); + ErrorStringPrintf("Unknown map item type %x", kType); return false; } - if (IsDataSectionType(type)) { + if (IsDataSectionType(kType)) { if (aligned_offset == 0u) { ErrorStringPrintf("Item %d offset is 0", i); return false; } - DCHECK(offset_to_type_map_.Find(aligned_offset) == offset_to_type_map_.end()); - offset_to_type_map_.Insert(std::pair(aligned_offset, type)); + DCHECK(offset_to_type_map_.find(aligned_offset) == offset_to_type_map_.end()); + offset_to_type_map_.insert(std::pair(aligned_offset, kType)); } aligned_offset = ptr_ - begin_; @@ -1789,14 +1762,13 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_c return true; } -bool DexFileVerifier::CheckIntraIdSection(size_t offset, - uint32_t count, - DexFile::MapItemType type) { +template +bool DexFileVerifier::CheckIntraIdSection(size_t offset, uint32_t count) { uint32_t expected_offset; uint32_t expected_size; // Get the expected offset and size from the header. - switch (type) { + switch (kType) { case DexFile::kDexTypeStringIdItem: expected_offset = header_->string_ids_off_; expected_size = header_->string_ids_size_; @@ -1822,7 +1794,7 @@ bool DexFileVerifier::CheckIntraIdSection(size_t offset, expected_size = header_->class_defs_size_; break; default: - ErrorStringPrintf("Bad type for id section: %x", type); + ErrorStringPrintf("Bad type for id section: %x", kType); return false; } @@ -1836,12 +1808,11 @@ bool DexFileVerifier::CheckIntraIdSection(size_t offset, return false; } - return CheckIntraSectionIterate(offset, count, type); + return CheckIntraSectionIterate(offset, count); } -bool DexFileVerifier::CheckIntraDataSection(size_t offset, - uint32_t count, - DexFile::MapItemType type) { +template +bool DexFileVerifier::CheckIntraDataSection(size_t offset, uint32_t count) { size_t data_start = header_->data_off_; size_t data_end = data_start + header_->data_size_; @@ -1851,7 +1822,7 @@ bool DexFileVerifier::CheckIntraDataSection(size_t offset, return false; } - if (!CheckIntraSectionIterate(offset, count, type)) { + if (!CheckIntraSectionIterate(offset, count)) { return false; } @@ -1868,7 +1839,8 @@ bool DexFileVerifier::CheckIntraDataSection(size_t offset, } bool DexFileVerifier::CheckIntraSection() { - const DexFile::MapList* map = reinterpret_cast(begin_ + header_->map_off_); + const DexFile::MapList* map = + reinterpret_cast(begin_ + header_->map_off_); const DexFile::MapItem* item = map->list_; size_t offset = 0; uint32_t count = map->size_; @@ -1889,6 +1861,10 @@ bool DexFileVerifier::CheckIntraSection() { return false; } + if (type == DexFile::kDexTypeClassDataItem) { + FindStringRangesForMethodNames(); + } + // Check each item based on its type. switch (type) { case DexFile::kDexTypeHeaderItem: @@ -1903,17 +1879,22 @@ bool DexFileVerifier::CheckIntraSection() { ptr_ = begin_ + header_->header_size_; offset = header_->header_size_; break; - case DexFile::kDexTypeStringIdItem: - case DexFile::kDexTypeTypeIdItem: - case DexFile::kDexTypeProtoIdItem: - case DexFile::kDexTypeFieldIdItem: - case DexFile::kDexTypeMethodIdItem: - case DexFile::kDexTypeClassDefItem: - if (!CheckIntraIdSection(section_offset, section_count, type)) { - return false; - } - offset = ptr_ - begin_; + +#define CHECK_INTRA_ID_SECTION_CASE(type) \ + case type: \ + if (!CheckIntraIdSection(section_offset, section_count)) { \ + return false; \ + } \ + offset = ptr_ - begin_; \ break; + CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeStringIdItem) + CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeTypeIdItem) + CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeProtoIdItem) + CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeFieldIdItem) + CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeMethodIdItem) + CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeClassDefItem) +#undef CHECK_INTRA_ID_SECTION_CASE + case DexFile::kDexTypeMapList: if (UNLIKELY(section_count != 1)) { ErrorStringPrintf("Multiple map list items"); @@ -1927,26 +1908,34 @@ bool DexFileVerifier::CheckIntraSection() { ptr_ += sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem)); offset = section_offset + sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem)); break; - case DexFile::kDexTypeMethodHandleItem: - case DexFile::kDexTypeCallSiteIdItem: - CheckIntraSectionIterate(section_offset, section_count, type); - offset = ptr_ - begin_; + +#define CHECK_INTRA_SECTION_ITERATE_CASE(type) \ + case type: \ + CheckIntraSectionIterate(section_offset, section_count); \ + offset = ptr_ - begin_; \ break; - case DexFile::kDexTypeTypeList: - case DexFile::kDexTypeAnnotationSetRefList: - case DexFile::kDexTypeAnnotationSetItem: - case DexFile::kDexTypeClassDataItem: - case DexFile::kDexTypeCodeItem: - case DexFile::kDexTypeStringDataItem: - case DexFile::kDexTypeDebugInfoItem: - case DexFile::kDexTypeAnnotationItem: - case DexFile::kDexTypeEncodedArrayItem: - case DexFile::kDexTypeAnnotationsDirectoryItem: - if (!CheckIntraDataSection(section_offset, section_count, type)) { - return false; - } - offset = ptr_ - begin_; + CHECK_INTRA_SECTION_ITERATE_CASE(DexFile::kDexTypeMethodHandleItem) + CHECK_INTRA_SECTION_ITERATE_CASE(DexFile::kDexTypeCallSiteIdItem) +#undef CHECK_INTRA_SECTION_ITERATE_CASE + +#define CHECK_INTRA_DATA_SECTION_CASE(type) \ + case type: \ + if (!CheckIntraDataSection(section_offset, section_count)) { \ + return false; \ + } \ + offset = ptr_ - begin_; \ break; + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeTypeList) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeAnnotationSetRefList) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeAnnotationSetItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeClassDataItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeCodeItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeStringDataItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeDebugInfoItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeAnnotationItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeEncodedArrayItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeAnnotationsDirectoryItem) +#undef CHECK_INTRA_DATA_SECTION_CASE } if (offset == current_offset) { @@ -1962,7 +1951,7 @@ bool DexFileVerifier::CheckIntraSection() { bool DexFileVerifier::CheckOffsetToTypeMap(size_t offset, uint16_t type) { DCHECK_NE(offset, 0u); - auto it = offset_to_type_map_.Find(offset); + auto it = offset_to_type_map_.find(offset); if (UNLIKELY(it == offset_to_type_map_.end())) { ErrorStringPrintf("No data map entry found @ %zx; expected %x", offset, type); return false; @@ -2220,7 +2209,7 @@ bool DexFileVerifier::CheckInterMethodIdItem() { } // Check that the proto id is valid. - if (UNLIKELY(!CheckIndex(item->proto_idx_, dex_file_->NumProtoIds(), + if (UNLIKELY(!CheckIndex(item->proto_idx_.index_, dex_file_->NumProtoIds(), "inter_method_id_item proto_idx"))) { return false; } @@ -2889,11 +2878,14 @@ void DexFileVerifier::ErrorStringPrintf(const char* fmt, ...) { } // Fields and methods may have only one of public/protected/private. -static bool CheckAtMostOneOfPublicProtectedPrivate(uint32_t flags) { - size_t count = (((flags & kAccPublic) == 0) ? 0 : 1) + - (((flags & kAccProtected) == 0) ? 0 : 1) + - (((flags & kAccPrivate) == 0) ? 0 : 1); - return count <= 1; +ALWAYS_INLINE +static constexpr bool CheckAtMostOneOfPublicProtectedPrivate(uint32_t flags) { + // Semantically we want 'return POPCOUNT(flags & kAcc) <= 1;'. + static_assert(IsPowerOfTwo(0), "0 not marked as power of two"); + static_assert(IsPowerOfTwo(kAccPublic), "kAccPublic not marked as power of two"); + static_assert(IsPowerOfTwo(kAccProtected), "kAccProtected not marked as power of two"); + static_assert(IsPowerOfTwo(kAccPrivate), "kAccPrivate not marked as power of two"); + return IsPowerOfTwo(flags & (kAccPublic | kAccProtected | kAccPrivate)); } // Helper functions to retrieve names from the dex file. We do not want to rely on DexFile @@ -3052,6 +3044,55 @@ bool DexFileVerifier::CheckFieldAccessFlags(uint32_t idx, return true; } +void DexFileVerifier::FindStringRangesForMethodNames() { + // Use DexFile::StringId* as RandomAccessIterator. + const DexFile::StringId* first = reinterpret_cast( + begin_ + header_->string_ids_off_); + const DexFile::StringId* last = first + header_->string_ids_size_; + + auto get_string = [begin = begin_](const DexFile::StringId& id) { + const uint8_t* str_data_ptr = begin + id.string_data_off_; + DecodeUnsignedLeb128(&str_data_ptr); + return reinterpret_cast(str_data_ptr); + }; + auto compare = [&get_string](const DexFile::StringId& lhs, const char* rhs) { + return CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(get_string(lhs), rhs) < 0; + }; + + // '=' follows '<' + static_assert('<' + 1 == '=', "Unexpected character relation"); + const auto angle_end = std::lower_bound(first, last, "=", compare); + angle_bracket_end_index_ = angle_end - first; + + const auto angle_start = std::lower_bound(first, angle_end, "<", compare); + angle_bracket_start_index_ = angle_start - first; + if (angle_start == angle_end) { + // No strings starting with '<'. + angle_init_angle_index_ = std::numeric_limits::max(); + angle_clinit_angle_index_ = std::numeric_limits::max(); + return; + } + + { + constexpr const char* kClinit = ""; + const auto it = std::lower_bound(angle_start, angle_end, kClinit, compare); + if (it != angle_end && strcmp(get_string(*it), kClinit) == 0) { + angle_clinit_angle_index_ = it - first; + } else { + angle_clinit_angle_index_ = std::numeric_limits::max(); + } + } + { + constexpr const char* kInit = ""; + const auto it = std::lower_bound(angle_start, angle_end, kInit, compare); + if (it != angle_end && strcmp(get_string(*it), kInit) == 0) { + angle_init_angle_index_ = it - first; + } else { + angle_init_angle_index_ = std::numeric_limits::max(); + } + } +} + bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, uint32_t method_access_flags, uint32_t class_access_flags, diff --git a/libdexfile/dex/dex_file_verifier.h b/libdexfile/dex/dex_file_verifier.h index a80a9d569a45c726e1e45f8862ac2f4f6976fb2b..43d1093809b2f7eae7610cd9df43ad409a0492d2 100644 --- a/libdexfile/dex/dex_file_verifier.h +++ b/libdexfile/dex/dex_file_verifier.h @@ -17,6 +17,7 @@ #ifndef ART_LIBDEXFILE_DEX_DEX_FILE_VERIFIER_H_ #define ART_LIBDEXFILE_DEX_DEX_FILE_VERIFIER_H_ +#include #include #include "base/hash_map.h" @@ -52,7 +53,11 @@ class DexFileVerifier { verify_checksum_(verify_checksum), header_(&dex_file->GetHeader()), ptr_(nullptr), - previous_item_(nullptr) { + previous_item_(nullptr), + angle_bracket_start_index_(std::numeric_limits::max()), + angle_bracket_end_index_(std::numeric_limits::max()), + angle_init_angle_index_(std::numeric_limits::max()), + angle_clinit_angle_index_(std::numeric_limits::max()) { } bool Verify(); @@ -85,15 +90,10 @@ class DexFileVerifier { uint32_t class_access_flags, dex::TypeIndex class_type_index, uint32_t code_offset, - std::unordered_set* direct_method_indexes, + ClassDataItemIterator* direct_it, bool expect_direct); - bool CheckOrderAndGetClassDef(bool is_field, - const char* type_descr, - uint32_t curr_index, - uint32_t prev_index, - bool* have_class, - dex::TypeIndex* class_type_index, - const DexFile::ClassDef** class_def); + ALWAYS_INLINE + bool CheckOrder(const char* type_descr, uint32_t curr_index, uint32_t prev_index); bool CheckStaticFieldTypes(const DexFile::ClassDef* class_def); bool CheckPadding(size_t offset, uint32_t aligned_offset, DexFile::MapItemType type); @@ -113,7 +113,7 @@ class DexFileVerifier { // method, if necessary (and return it), or use the given values. template bool CheckIntraClassDataItemMethods(ClassDataItemIterator* it, - std::unordered_set* direct_method_indexes, + ClassDataItemIterator* direct_it, bool* have_class, dex::TypeIndex* class_type_index, const DexFile::ClassDef** class_def); @@ -124,9 +124,12 @@ class DexFileVerifier { bool CheckIntraAnnotationItem(); bool CheckIntraAnnotationsDirectoryItem(); - bool CheckIntraSectionIterate(size_t offset, uint32_t count, DexFile::MapItemType type); - bool CheckIntraIdSection(size_t offset, uint32_t count, DexFile::MapItemType type); - bool CheckIntraDataSection(size_t offset, uint32_t count, DexFile::MapItemType type); + template + bool CheckIntraSectionIterate(size_t offset, uint32_t count); + template + bool CheckIntraIdSection(size_t offset, uint32_t count); + template + bool CheckIntraDataSection(size_t offset, uint32_t count); bool CheckIntraSection(); bool CheckOffsetToTypeMap(size_t offset, uint16_t type); @@ -161,7 +164,7 @@ class DexFileVerifier { // error if not. If there is an error, null is returned. const DexFile::FieldId* CheckLoadFieldId(uint32_t idx, const char* error_fmt); const DexFile::MethodId* CheckLoadMethodId(uint32_t idx, const char* error_fmt); - const DexFile::ProtoId* CheckLoadProtoId(uint32_t idx, const char* error_fmt); + const DexFile::ProtoId* CheckLoadProtoId(dex::ProtoIndex idx, const char* error_fmt); void ErrorStringPrintf(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) COLD_ATTR; @@ -197,6 +200,8 @@ class DexFileVerifier { // Check validity of given method if it's a constructor or class initializer. bool CheckConstructorProperties(uint32_t method_index, uint32_t constructor_flags); + void FindStringRangesForMethodNames(); + const DexFile* const dex_file_; const uint8_t* const begin_; const size_t size_; @@ -239,6 +244,20 @@ class DexFileVerifier { // Set of type ids for which there are ClassDef elements in the dex file. std::unordered_set defined_classes_; + + // Cached string indices for "interesting" entries wrt/ method names. Will be populated by + // FindStringRangesForMethodNames (which is automatically called before verifying the + // classdataitem section). + // + // Strings starting with '<' are in the range + // [angle_bracket_start_index_,angle_bracket_end_index_). + // angle_init_angle_index_ and angle_clinit_angle_index_ denote the indices of "" and + // angle_clinit_angle_index_, respectively. If any value is not found, the corresponding + // index will be larger than any valid string index for this dex file. + size_t angle_bracket_start_index_; + size_t angle_bracket_end_index_; + size_t angle_init_angle_index_; + size_t angle_clinit_angle_index_; }; } // namespace art diff --git a/libdexfile/dex/dex_file_verifier_test.cc b/libdexfile/dex/dex_file_verifier_test.cc index 4c3cf776ee411c56432ce5e619b015004e466cf8..65448cabd1a3260a842a31c6da6bfca8659da7a4 100644 --- a/libdexfile/dex/dex_file_verifier_test.cc +++ b/libdexfile/dex/dex_file_verifier_test.cc @@ -161,7 +161,7 @@ TEST_F(DexFileVerifierTest, MethodId) { "method_id_proto_idx", [](DexFile* dex_file) { DexFile::MethodId* method_id = const_cast(&dex_file->GetMethodId(0)); - method_id->proto_idx_ = 0xFF; + method_id->proto_idx_ = dex::ProtoIndex(0xFF); }, "inter_method_id_item proto_idx"); @@ -173,7 +173,22 @@ TEST_F(DexFileVerifierTest, MethodId) { DexFile::MethodId* method_id = const_cast(&dex_file->GetMethodId(0)); method_id->name_idx_ = dex::StringIndex(0xFF); }, - "String index not available for method flags verification"); + "Bad index for method flags verification"); +} + +TEST_F(DexFileVerifierTest, InitCachingWithUnicode) { + static const char kInitWithUnicode[] = + "ZGV4CjAzNQDhN60rgMnSK13MoRscTuD+NZe7f6rIkHAAAgAAcAAAAHhWNBIAAAAAAAAAAGwBAAAJ" + "AAAAcAAAAAMAAACUAAAAAQAAAKAAAAAAAAAAAAAAAAIAAACsAAAAAQAAALwAAAAkAQAA3AAAANwA" + "AADgAAAA5gAAAO4AAAD1AAAAAQEAABUBAAAgAQAAIwEAAAQAAAAFAAAABwAAAAcAAAACAAAAAAAA" + "AAAAAAACAAAAAQAAAAIAAAAAAAAAAAAAAAEAAAAAAAAABgAAAAAAAABgAQAAAAAAAAHAgAACwIDA" + "gAAGPGluaXQ+AAVIZWxsbwAKTFRlc3RTeW5jOwASTGphdmEvbGFuZy9PYmplY3Q7AAlNYWluLmph" + "dmEAAVYABVdvcmxkAAAAAAAAAAYABw4AAAAACgABAAEAAAAwAQAADAAAAHAQAQAJABoBAwAaAggA" + "GgMAABoEAQAOAAAAAQAAgIAEuAIAAAwAAAAAAAAAAQAAAAAAAAABAAAACQAAAHAAAAACAAAAAwAA" + "AJQAAAADAAAAAQAAAKAAAAAFAAAAAgAAAKwAAAAGAAAAAQAAALwAAAACIAAACQAAANwAAAADEAAA" + "AQAAACwBAAADIAAAAQAAADABAAABIAAAAQAAADgBAAAAIAAAAQAAAGABAAAAEAAAAQAAAGwBAAA="; + // Just ensure it verifies w/o modification. + VerifyModification(kInitWithUnicode, "init_with_unicode", [](DexFile*) {}, nullptr); } // Method flags test class generated from the following smali code. The declared-synchronized @@ -1425,12 +1440,13 @@ TEST_F(DexFileVerifierTest, ProtoOrdering) { CHECK_LT(method_idx + 1u, dex_file->NumMethodIds()); CHECK_EQ(dex_file->GetMethodId(method_idx).name_idx_, dex_file->GetMethodId(method_idx + 1).name_idx_); - CHECK_EQ(dex_file->GetMethodId(method_idx).proto_idx_ + 1u, - dex_file->GetMethodId(method_idx + 1).proto_idx_); + CHECK_EQ(dex_file->GetMethodId(method_idx).proto_idx_.index_ + 1u, + dex_file->GetMethodId(method_idx + 1).proto_idx_.index_); // Their return types should be the same. - uint32_t proto1_idx = dex_file->GetMethodId(method_idx).proto_idx_; + dex::ProtoIndex proto1_idx = dex_file->GetMethodId(method_idx).proto_idx_; const DexFile::ProtoId& proto1 = dex_file->GetProtoId(proto1_idx); - const DexFile::ProtoId& proto2 = dex_file->GetProtoId(proto1_idx + 1u); + dex::ProtoIndex proto2_idx(proto1_idx.index_ + 1u); + const DexFile::ProtoId& proto2 = dex_file->GetProtoId(proto2_idx); CHECK_EQ(proto1.return_type_idx_, proto2.return_type_idx_); // And the first should not have any parameters while the second should have some. CHECK(!DexFileParameterIterator(*dex_file, proto1).HasNext()); diff --git a/libdexfile/dex/dex_instruction-inl.h b/libdexfile/dex/dex_instruction-inl.h index 6bef18c85fccc2fa3e8bae792ca88f239eea0c23..e0cffdd2f43d86375985f7c7ae0c78f58e75c3c2 100644 --- a/libdexfile/dex/dex_instruction-inl.h +++ b/libdexfile/dex/dex_instruction-inl.h @@ -508,7 +508,7 @@ inline bool Instruction::HasVarArgs() const { return (FormatOf(Opcode()) == k35c) || (FormatOf(Opcode()) == k45cc); } -inline void Instruction::GetVarArgs(uint32_t arg[kMaxVarArgRegs], uint16_t inst_data) const { +inline uint32_t Instruction::GetVarArgs(uint32_t arg[kMaxVarArgRegs], uint16_t inst_data) const { DCHECK(HasVarArgs()); /* @@ -551,6 +551,7 @@ inline void Instruction::GetVarArgs(uint32_t arg[kMaxVarArgRegs], uint16_t inst_ default: // case 0 break; // Valid, but no need to do anything. } + return count; } } // namespace art diff --git a/libdexfile/dex/dex_instruction.cc b/libdexfile/dex/dex_instruction.cc index 886218129e45429c8e4455b8080f6b3c8e10bfc2..8378211c4308c02e370252515e14a456f6f00736 100644 --- a/libdexfile/dex/dex_instruction.cc +++ b/libdexfile/dex/dex_instruction.cc @@ -467,10 +467,10 @@ std::string Instruction::DumpString(const DexFile* file) const { case k45cc: { uint32_t arg[kMaxVarArgRegs]; GetVarArgs(arg); - uint32_t method_idx = VRegB_45cc(); - uint32_t proto_idx = VRegH_45cc(); + uint16_t method_idx = VRegB_45cc(); + dex::ProtoIndex proto_idx(VRegH_45cc()); os << opcode << " {"; - for (int i = 0; i < VRegA_45cc(); ++i) { + for (uint32_t i = 0; i < VRegA_45cc(); ++i) { if (i != 0) { os << ", "; } @@ -478,7 +478,8 @@ std::string Instruction::DumpString(const DexFile* file) const { } os << "}"; if (file != nullptr) { - os << ", " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx) + os << ", " << file->PrettyMethod(method_idx) + << ", " << file->GetShorty(proto_idx) << " // "; } else { os << ", "; @@ -490,18 +491,19 @@ std::string Instruction::DumpString(const DexFile* file) const { switch (Opcode()) { case INVOKE_POLYMORPHIC_RANGE: { if (file != nullptr) { - uint32_t method_idx = VRegB_4rcc(); - uint32_t proto_idx = VRegH_4rcc(); + uint16_t method_idx = VRegB_4rcc(); + dex::ProtoIndex proto_idx(VRegH_4rcc()); os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc()) - << "}, " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx) + << "}, " << file->PrettyMethod(method_idx) + << ", " << file->GetShorty(dex::ProtoIndex(proto_idx)) << " // method@" << method_idx << ", proto@" << proto_idx; break; } } FALLTHROUGH_INTENDED; default: { - uint32_t method_idx = VRegB_4rcc(); - uint32_t proto_idx = VRegH_4rcc(); + uint16_t method_idx = VRegB_4rcc(); + dex::ProtoIndex proto_idx(VRegH_4rcc()); os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc()) << "}, method@" << method_idx << ", proto@" << proto_idx; } diff --git a/libdexfile/dex/dex_instruction.h b/libdexfile/dex/dex_instruction.h index bf5083622ba5c515d4fbe90e55ec85963a069ea3..6807025e136c7e558811161bd30257ded60c04ae 100644 --- a/libdexfile/dex/dex_instruction.h +++ b/libdexfile/dex/dex_instruction.h @@ -462,8 +462,8 @@ class Instruction { // Fills the given array with the 'arg' array of the instruction. bool HasVarArgs() const; - void GetVarArgs(uint32_t args[kMaxVarArgRegs], uint16_t inst_data) const; - void GetVarArgs(uint32_t args[kMaxVarArgRegs]) const { + uint32_t GetVarArgs(uint32_t args[kMaxVarArgRegs], uint16_t inst_data) const; + uint32_t GetVarArgs(uint32_t args[kMaxVarArgRegs]) const { return GetVarArgs(args, Fetch16(0)); } diff --git a/libdexfile/dex/dex_instruction_iterator.h b/libdexfile/dex/dex_instruction_iterator.h index db3ff95e0251318d31ffa8edb8e7af5390e3fec9..b75a95bf5c17ff664790f88da44dc02b7783a9c0 100644 --- a/libdexfile/dex/dex_instruction_iterator.h +++ b/libdexfile/dex/dex_instruction_iterator.h @@ -123,7 +123,7 @@ class DexInstructionIterator : public DexInstructionIteratorBase { using DexInstructionIteratorBase::DexInstructionIteratorBase; explicit DexInstructionIterator(const uint16_t* inst, uint32_t dex_pc) - : DexInstructionIteratorBase(Instruction::At(inst), dex_pc) {} + : DexInstructionIteratorBase(inst != nullptr ? Instruction::At(inst) : nullptr, dex_pc) {} explicit DexInstructionIterator(const DexInstructionPcPair& pair) : DexInstructionIterator(pair.Instructions(), pair.DexPc()) {} diff --git a/libdexfile/dex/dex_instruction_test.cc b/libdexfile/dex/dex_instruction_test.cc index c944085b9e379f57eb283806ca77d07a63d63778..6ce9dbafc81817bbcc024d77be80d45b14f6e3c6 100644 --- a/libdexfile/dex/dex_instruction_test.cc +++ b/libdexfile/dex/dex_instruction_test.cc @@ -135,7 +135,7 @@ TEST(Instruction, PropertiesOf4rcc) { static void Build35c(uint16_t* out, Instruction::Code code, uint16_t method_idx, - std::vector args) { + const std::vector& args) { out[0] = 0; out[0] |= (args.size() << 12); out[0] |= static_cast(code); @@ -152,7 +152,7 @@ static void Build35c(uint16_t* out, static std::string DumpInst35c(Instruction::Code code, uint16_t method_idx, - std::vector args) { + const std::vector& args) { uint16_t inst[6] = {}; Build35c(inst, code, method_idx, args); return Instruction::At(inst)->DumpString(nullptr); diff --git a/libdexfile/dex/invoke_type.h b/libdexfile/dex/invoke_type.h index 9b3af673a8aeb86367e1d42a315d8f2e769b1e3f..1740c079bb6cc78b2243b40305f5ff2822b995b9 100644 --- a/libdexfile/dex/invoke_type.h +++ b/libdexfile/dex/invoke_type.h @@ -28,7 +28,8 @@ enum InvokeType : uint32_t { kSuper, // <> kInterface, // <> kPolymorphic, // <> - kMaxInvokeType = kPolymorphic + kCustom, // <> + kMaxInvokeType = kCustom }; std::ostream& operator<<(std::ostream& os, const InvokeType& rhs); diff --git a/libdexfile/dex/type_lookup_table.cc b/libdexfile/dex/type_lookup_table.cc new file mode 100644 index 0000000000000000000000000000000000000000..00ec358b02eab6f4519579b87baa057fc62b396a --- /dev/null +++ b/libdexfile/dex/type_lookup_table.cc @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "type_lookup_table.h" + +#include +#include + +#include "base/bit_utils.h" +#include "base/leb128.h" +#include "dex/dex_file-inl.h" +#include "dex/utf-inl.h" + +namespace art { + +static inline bool ModifiedUtf8StringEquals(const char* lhs, const char* rhs) { + return CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(lhs, rhs) == 0; +} + +TypeLookupTable TypeLookupTable::Create(const DexFile& dex_file) { + uint32_t num_class_defs = dex_file.NumClassDefs(); + if (UNLIKELY(!SupportedSize(num_class_defs))) { + return TypeLookupTable(); + } + size_t mask_bits = CalculateMaskBits(num_class_defs); + size_t size = 1u << mask_bits; + std::unique_ptr owned_entries(new Entry[size]); + Entry* entries = owned_entries.get(); + + static_assert(alignof(Entry) == 4u, "Expecting Entry to be 4-byte aligned."); + const uint32_t mask = Entry::GetMask(mask_bits); + std::vector conflict_class_defs; + // The first stage. Put elements on their initial positions. If an initial position is already + // occupied then delay the insertion of the element to the second stage to reduce probing + // distance. + for (size_t class_def_idx = 0; class_def_idx < dex_file.NumClassDefs(); ++class_def_idx) { + const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx); + const DexFile::TypeId& type_id = dex_file.GetTypeId(class_def.class_idx_); + const DexFile::StringId& str_id = dex_file.GetStringId(type_id.descriptor_idx_); + const uint32_t hash = ComputeModifiedUtf8Hash(dex_file.GetStringData(str_id)); + const uint32_t pos = hash & mask; + if (entries[pos].IsEmpty()) { + entries[pos] = Entry(str_id.string_data_off_, hash, class_def_idx, mask_bits); + DCHECK(entries[pos].IsLast(mask_bits)); + } else { + conflict_class_defs.push_back(class_def_idx); + } + } + // The second stage. The initial position of these elements had a collision. Put these elements + // into the nearest free cells and link them together by updating next_pos_delta. + for (uint16_t class_def_idx : conflict_class_defs) { + const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx); + const DexFile::TypeId& type_id = dex_file.GetTypeId(class_def.class_idx_); + const DexFile::StringId& str_id = dex_file.GetStringId(type_id.descriptor_idx_); + const uint32_t hash = ComputeModifiedUtf8Hash(dex_file.GetStringData(str_id)); + // Find the last entry in the chain. + uint32_t tail_pos = hash & mask; + DCHECK(!entries[tail_pos].IsEmpty()); + while (!entries[tail_pos].IsLast(mask_bits)) { + tail_pos = (tail_pos + entries[tail_pos].GetNextPosDelta(mask_bits)) & mask; + DCHECK(!entries[tail_pos].IsEmpty()); + } + // Find an empty entry for insertion. + uint32_t insert_pos = tail_pos; + do { + insert_pos = (insert_pos + 1) & mask; + } while (!entries[insert_pos].IsEmpty()); + // Insert and chain the new entry. + entries[insert_pos] = Entry(str_id.string_data_off_, hash, class_def_idx, mask_bits); + entries[tail_pos].SetNextPosDelta((insert_pos - tail_pos) & mask, mask_bits); + DCHECK(entries[insert_pos].IsLast(mask_bits)); + DCHECK(!entries[tail_pos].IsLast(mask_bits)); + } + + return TypeLookupTable(dex_file.DataBegin(), mask_bits, entries, std::move(owned_entries)); +} + +TypeLookupTable TypeLookupTable::Open(const uint8_t* dex_data_pointer, + const uint8_t* raw_data, + uint32_t num_class_defs) { + DCHECK_ALIGNED(raw_data, alignof(Entry)); + const Entry* entries = reinterpret_cast(raw_data); + size_t mask_bits = CalculateMaskBits(num_class_defs); + return TypeLookupTable(dex_data_pointer, mask_bits, entries, /* owned_entries */ nullptr); +} + +uint32_t TypeLookupTable::Lookup(const char* str, uint32_t hash) const { + uint32_t mask = Entry::GetMask(mask_bits_); + uint32_t pos = hash & mask; + // Thanks to special insertion algorithm, the element at position pos can be empty + // or start of the right bucket, or anywhere in the wrong bucket's chain. + const Entry* entry = &entries_[pos]; + if (entry->IsEmpty()) { + return dex::kDexNoIndex; + } + // Look for the partial hash match first, even if traversing the wrong bucket's chain. + uint32_t compared_hash_bits = (hash << mask_bits_) >> (2 * mask_bits_); + while (compared_hash_bits != entry->GetHashBits(mask_bits_)) { + if (entry->IsLast(mask_bits_)) { + return dex::kDexNoIndex; + } + pos = (pos + entry->GetNextPosDelta(mask_bits_)) & mask; + entry = &entries_[pos]; + DCHECK(!entry->IsEmpty()); + } + // Found partial hash match, compare strings (expecting this to succeed). + const char* first_checked_str = GetStringData(*entry); + if (ModifiedUtf8StringEquals(str, first_checked_str)) { + return entry->GetClassDefIdx(mask_bits_); + } + // If we're at the end of the chain, return before doing further expensive work. + if (entry->IsLast(mask_bits_)) { + return dex::kDexNoIndex; + } + // Check if we're traversing the right bucket. This is important if the compared + // partial hash has only a few bits (i.e. it can match frequently). + if (((ComputeModifiedUtf8Hash(first_checked_str) ^ hash) & mask) != 0u) { + return dex::kDexNoIndex; // Low hash bits mismatch. + } + // Continue looking for the string in the rest of the chain. + do { + pos = (pos + entry->GetNextPosDelta(mask_bits_)) & mask; + entry = &entries_[pos]; + DCHECK(!entry->IsEmpty()); + if (compared_hash_bits == entry->GetHashBits(mask_bits_) && + ModifiedUtf8StringEquals(str, GetStringData(*entry))) { + return entry->GetClassDefIdx(mask_bits_); + } + } while (!entry->IsLast(mask_bits_)); + // Not found. + return dex::kDexNoIndex; +} + +uint32_t TypeLookupTable::RawDataLength(uint32_t num_class_defs) { + return SupportedSize(num_class_defs) ? RoundUpToPowerOfTwo(num_class_defs) * sizeof(Entry) : 0u; +} + +uint32_t TypeLookupTable::CalculateMaskBits(uint32_t num_class_defs) { + return SupportedSize(num_class_defs) ? MinimumBitsToStore(num_class_defs - 1u) : 0u; +} + +bool TypeLookupTable::SupportedSize(uint32_t num_class_defs) { + return num_class_defs != 0u && num_class_defs <= std::numeric_limits::max(); +} + +TypeLookupTable::TypeLookupTable(const uint8_t* dex_data_pointer, + uint32_t mask_bits, + const Entry* entries, + std::unique_ptr owned_entries) + : dex_data_begin_(dex_data_pointer), + mask_bits_(mask_bits), + entries_(entries), + owned_entries_(std::move(owned_entries)) {} + +const char* TypeLookupTable::GetStringData(const Entry& entry) const { + DCHECK(dex_data_begin_ != nullptr); + const uint8_t* ptr = dex_data_begin_ + entry.GetStringOffset(); + // Skip string length. + DecodeUnsignedLeb128(&ptr); + return reinterpret_cast(ptr); +} + +} // namespace art diff --git a/libdexfile/dex/type_lookup_table.h b/libdexfile/dex/type_lookup_table.h new file mode 100644 index 0000000000000000000000000000000000000000..7005d34b884862a3692f894f83557e1f18c3defc --- /dev/null +++ b/libdexfile/dex/type_lookup_table.h @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_LIBDEXFILE_DEX_TYPE_LOOKUP_TABLE_H_ +#define ART_LIBDEXFILE_DEX_TYPE_LOOKUP_TABLE_H_ + +#include "base/logging.h" +#include "dex/dex_file_types.h" + +namespace art { + +class DexFile; + +/** + * TypeLookupTable used to find class_def_idx by class descriptor quickly. + * Implementation of TypeLookupTable is based on hash table. + * This class instantiated at compile time by calling Create() method and written into OAT file. + * At runtime, the raw data is read from memory-mapped file by calling Open() method. The table + * memory remains clean. + */ +class TypeLookupTable { + public: + // Method creates lookup table for dex file. + static TypeLookupTable Create(const DexFile& dex_file); + + // Method opens lookup table from binary data. Lookups will traverse strings and other + // data contained in dex_file as well. Lookup table does not own raw_data or dex_file. + static TypeLookupTable Open(const uint8_t* dex_data_pointer, + const uint8_t* raw_data, + uint32_t num_class_defs); + + // Create an invalid lookup table. + TypeLookupTable() + : dex_data_begin_(nullptr), + mask_bits_(0u), + entries_(nullptr), + owned_entries_(nullptr) {} + + TypeLookupTable(TypeLookupTable&& src) noexcept = default; + TypeLookupTable& operator=(TypeLookupTable&& src) noexcept = default; + + ~TypeLookupTable() { + // Implicit deallocation by std::unique_ptr<> destructor. + } + + // Returns whether the TypeLookupTable is valid. + bool Valid() const { + return entries_ != nullptr; + } + + // Return the number of buckets in the lookup table. + uint32_t Size() const { + DCHECK(Valid()); + return 1u << mask_bits_; + } + + // Method search class_def_idx by class descriptor and it's hash. + // If no data found then the method returns dex::kDexNoIndex. + uint32_t Lookup(const char* str, uint32_t hash) const; + + // Method returns pointer to binary data of lookup table. Used by the oat writer. + const uint8_t* RawData() const { + DCHECK(Valid()); + return reinterpret_cast(entries_); + } + + // Method returns length of binary data. Used by the oat writer. + uint32_t RawDataLength() const { + DCHECK(Valid()); + return Size() * sizeof(Entry); + } + + // Method returns length of binary data for the specified number of class definitions. + static uint32_t RawDataLength(uint32_t num_class_defs); + + private: + /** + * To find element we need to compare strings. + * It is faster to compare first hashes and then strings itself. + * But we have no full hash of element of table. But we can use 2 ideas. + * 1. All minor bits of hash inside one bucket are equal. + * (TODO: We're not actually using this, are we?) + * 2. If the dex file contains N classes and the size of the hash table is 2^n (where N <= 2^n) + * then we need n bits for the class def index and n bits for the next position delta. + * So we can encode part of element's hash into the remaining 32-2*n (n <= 16) bits which + * would be otherwise wasted as a padding. + * So hash of element can be divided on three parts: + * XXXX XXXX XXXY YYYY YYYY YZZZ ZZZZ ZZZZ (example with n=11) + * Z - a part of hash encoded implicitly in the bucket index + * (these bits are same for all elements in bucket) + * Y - a part of hash that we can write into free 32-2*n bits + * X - a part of hash that we can't use without increasing the size of the entry + * So the `data` element of Entry is used to store the next position delta, class_def_index + * and a part of hash of the entry. + */ + class Entry { + public: + Entry() : str_offset_(0u), data_(0u) {} + Entry(uint32_t str_offset, uint32_t hash, uint32_t class_def_index, uint32_t mask_bits) + : str_offset_(str_offset), + data_(((hash & ~GetMask(mask_bits)) | class_def_index) << mask_bits) { + DCHECK_EQ(class_def_index & ~GetMask(mask_bits), 0u); + } + + void SetNextPosDelta(uint32_t next_pos_delta, uint32_t mask_bits) { + DCHECK_EQ(GetNextPosDelta(mask_bits), 0u); + DCHECK_EQ(next_pos_delta & ~GetMask(mask_bits), 0u); + DCHECK_NE(next_pos_delta, 0u); + data_ |= next_pos_delta; + } + + bool IsEmpty() const { + return str_offset_ == 0u; + } + + bool IsLast(uint32_t mask_bits) const { + return GetNextPosDelta(mask_bits) == 0u; + } + + uint32_t GetStringOffset() const { + return str_offset_; + } + + uint32_t GetNextPosDelta(uint32_t mask_bits) const { + return data_ & GetMask(mask_bits); + } + + uint32_t GetClassDefIdx(uint32_t mask_bits) const { + return (data_ >> mask_bits) & GetMask(mask_bits); + } + + uint32_t GetHashBits(uint32_t mask_bits) const { + DCHECK_LE(mask_bits, 16u); + return data_ >> (2u * mask_bits); + } + + static uint32_t GetMask(uint32_t mask_bits) { + DCHECK_LE(mask_bits, 16u); + return ~(std::numeric_limits::max() << mask_bits); + } + + private: + uint32_t str_offset_; + uint32_t data_; + }; + + static uint32_t CalculateMaskBits(uint32_t num_class_defs); + static bool SupportedSize(uint32_t num_class_defs); + + // Construct the TypeLookupTable. + TypeLookupTable(const uint8_t* dex_data_pointer, + uint32_t mask_bits, + const Entry* entries, + std::unique_ptr owned_entries); + + const char* GetStringData(const Entry& entry) const; + + const uint8_t* dex_data_begin_; + uint32_t mask_bits_; + const Entry* entries_; + // `owned_entries_` is either null (not owning `entries_`) or same pointer as `entries_`. + std::unique_ptr owned_entries_; +}; + +} // namespace art + +#endif // ART_LIBDEXFILE_DEX_TYPE_LOOKUP_TABLE_H_ diff --git a/runtime/type_lookup_table_test.cc b/libdexfile/dex/type_lookup_table_test.cc similarity index 77% rename from runtime/type_lookup_table_test.cc rename to libdexfile/dex/type_lookup_table_test.cc index b6ab6da78cc45adf655e5e13a4d001404f805d94..4316be0bd6bce638f3e73bc8b943f805e0468c6e 100644 --- a/runtime/type_lookup_table_test.cc +++ b/libdexfile/dex/type_lookup_table_test.cc @@ -18,7 +18,7 @@ #include -#include "common_runtime_test.h" +#include "base/common_art_test.h" #include "dex/dex_file-inl.h" #include "dex/utf-inl.h" #include "scoped_thread_state_change-inl.h" @@ -26,26 +26,24 @@ namespace art { using DescriptorClassDefIdxPair = std::pair; -class TypeLookupTableTest : public CommonRuntimeTestWithParam {}; +class TypeLookupTableTest : public CommonArtTestWithParam {}; TEST_F(TypeLookupTableTest, CreateLookupTable) { - ScopedObjectAccess soa(Thread::Current()); std::unique_ptr dex_file(OpenTestDexFile("Lookup")); - std::unique_ptr table(TypeLookupTable::Create(*dex_file)); - ASSERT_NE(nullptr, table.get()); - ASSERT_NE(nullptr, table->RawData()); - ASSERT_EQ(32U, table->RawDataLength()); + TypeLookupTable table = TypeLookupTable::Create(*dex_file); + ASSERT_TRUE(table.Valid()); + ASSERT_NE(nullptr, table.RawData()); + ASSERT_EQ(32U, table.RawDataLength()); } TEST_P(TypeLookupTableTest, Find) { - ScopedObjectAccess soa(Thread::Current()); std::unique_ptr dex_file(OpenTestDexFile("Lookup")); - std::unique_ptr table(TypeLookupTable::Create(*dex_file)); - ASSERT_NE(nullptr, table.get()); + TypeLookupTable table(TypeLookupTable::Create(*dex_file)); + ASSERT_TRUE(table.Valid()); auto pair = GetParam(); const char* descriptor = pair.first; size_t hash = ComputeModifiedUtf8Hash(descriptor); - uint32_t class_def_idx = table->Lookup(descriptor, hash); + uint32_t class_def_idx = table.Lookup(descriptor, hash); ASSERT_EQ(pair.second, class_def_idx); } diff --git a/libprofile/Android.bp b/libprofile/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..b9883f658d346d9be0bbaf6c8aba3c43a572ec19 --- /dev/null +++ b/libprofile/Android.bp @@ -0,0 +1,104 @@ +// +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_defaults { + name: "libprofile_defaults", + defaults: ["art_defaults"], + host_supported: true, + srcs: [ + "profile/profile_compilation_info.cc", + ], + target: { + android: { + static_libs: [ + // ZipArchive support, the order matters here to get all symbols. + "libziparchive", + "libz", + ], + shared_libs: [ + // For android::FileMap used by libziparchive. + "libutils", + ], + }, + host: { + shared_libs: [ + "libziparchive", + "libz", + ], + }, + }, + //generated_sources: ["art_libartbase_operator_srcs"], + cflags: ["-DBUILDING_LIBART=1"], + shared_libs: [ + "libartbase", + "libdexfile", + "libartbase", + // For atrace. + "libcutils", + ], + export_include_dirs: ["."], + // ART's macros.h depends on libbase's macros.h. + // Note: runtime_options.h depends on cmdline. But we don't really want to export this + // generically. dex2oat takes care of it itself. + export_shared_lib_headers: ["libbase"], +} + +art_cc_library { + name: "libprofile", + defaults: ["libprofile_defaults"], + // Leave the symbols in the shared library so that stack unwinders can + // produce meaningful name resolution. + strip: { + keep_symbols: true, + }, + shared_libs: [ + "libbase", + "libziparchive", + ], + export_shared_lib_headers: ["libbase"], +} + +art_cc_library { + name: "libprofiled", + defaults: [ + "art_debug_defaults", + "libprofile_defaults", + ], + shared_libs: [ + "libbase", + "libziparchive", + ], + export_shared_lib_headers: ["libbase"], +} + +// For now many of these tests still use CommonRuntimeTest, almost universally because of +// ScratchFile and related. +// TODO: Remove CommonRuntimeTest dependency from these tests. +art_cc_test { + name: "art_libprofile_tests", + defaults: [ + "art_gtest_defaults", + ], + srcs: [ + "profile/profile_compilation_info_test.cc", + ], + shared_libs: [ + "libartbased", + "libdexfiled", + "libartbased", + "libziparchive", + ], +} diff --git a/runtime/jit/profile_compilation_info.cc b/libprofile/profile/profile_compilation_info.cc similarity index 98% rename from runtime/jit/profile_compilation_info.cc rename to libprofile/profile/profile_compilation_info.cc index 12081731ef441b07859d19a853ac19d9a15c2884..6f49adf71800e58621ef2e2af575969eedb327c6 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/libprofile/profile/profile_compilation_info.cc @@ -34,9 +34,8 @@ #include "base/arena_allocator.h" #include "base/dumpable.h" -#include "base/file_utils.h" #include "base/logging.h" // For VLOG. -#include "base/mutex.h" +#include "base/malloc_arena_pool.h" #include "base/os.h" #include "base/safe_map.h" #include "base/scoped_flock.h" @@ -45,9 +44,8 @@ #include "base/time_utils.h" #include "base/unix_file/fd_file.h" #include "base/utils.h" +#include "base/zip_archive.h" #include "dex/dex_file_loader.h" -#include "jit/profiling_info.h" -#include "zip_archive.h" namespace art { @@ -59,7 +57,7 @@ const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '1', '0', '\0' // The name of the profile entry in the dex metadata file. // DO NOT CHANGE THIS! (it's similar to classes.dex in the apk files). -const char* ProfileCompilationInfo::kDexMetadataProfileEntry = "primary.prof"; +const char ProfileCompilationInfo::kDexMetadataProfileEntry[] = "primary.prof"; static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX; @@ -71,12 +69,12 @@ static constexpr bool kDebugIgnoreChecksum = false; static constexpr uint8_t kIsMissingTypesEncoding = 6; static constexpr uint8_t kIsMegamorphicEncoding = 7; -static_assert(sizeof(InlineCache::kIndividualCacheSize) == sizeof(uint8_t), - "InlineCache::kIndividualCacheSize does not have the expect type size"); -static_assert(InlineCache::kIndividualCacheSize < kIsMegamorphicEncoding, - "InlineCache::kIndividualCacheSize is larger than expected"); -static_assert(InlineCache::kIndividualCacheSize < kIsMissingTypesEncoding, - "InlineCache::kIndividualCacheSize is larger than expected"); +static_assert(sizeof(ProfileCompilationInfo::kIndividualInlineCacheSize) == sizeof(uint8_t), + "InlineCache::kIndividualInlineCacheSize does not have the expect type size"); +static_assert(ProfileCompilationInfo::kIndividualInlineCacheSize < kIsMegamorphicEncoding, + "InlineCache::kIndividualInlineCacheSize is larger than expected"); +static_assert(ProfileCompilationInfo::kIndividualInlineCacheSize < kIsMissingTypesEncoding, + "InlineCache::kIndividualInlineCacheSize is larger than expected"); static bool ChecksumMatch(uint32_t dex_file_checksum, uint32_t checksum) { return kDebugIgnoreChecksum || dex_file_checksum == checksum; @@ -90,7 +88,7 @@ ProfileCompilationInfo::ProfileCompilationInfo(ArenaPool* custom_arena_pool) } ProfileCompilationInfo::ProfileCompilationInfo() - : default_arena_pool_(/*use_malloc*/true, /*low_4gb*/false, "ProfileCompilationInfo"), + : default_arena_pool_(), allocator_(&default_arena_pool_), info_(allocator_.Adapter(kArenaAllocProfile)), profile_key_map_(std::less(), allocator_.Adapter(kArenaAllocProfile)) { @@ -119,7 +117,7 @@ void ProfileCompilationInfo::DexPcData::AddClass(uint16_t dex_profile_idx, } // Check if the adding the type will cause the cache to become megamorphic. - if (classes.size() + 1 >= InlineCache::kIndividualCacheSize) { + if (classes.size() + 1 >= ProfileCompilationInfo::kIndividualInlineCacheSize) { is_megamorphic = true; classes.clear(); return; @@ -502,7 +500,7 @@ void ProfileCompilationInfo::AddInlineCacheToBuffer(std::vector* buffer continue; } - DCHECK_LT(classes.size(), InlineCache::kIndividualCacheSize); + DCHECK_LT(classes.size(), ProfileCompilationInfo::kIndividualInlineCacheSize); DCHECK_NE(classes.size(), 0u) << "InlineCache contains a dex_pc with 0 classes"; SafeMap> dex_to_classes_map; @@ -1183,8 +1181,8 @@ ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::OpenSource( // Allow archives without the profile entry. In this case, create an empty profile. // This gives more flexible when ure-using archives that may miss the entry. // (e.g. dex metadata files) - LOG(WARNING) << std::string("Could not find entry ") + kDexMetadataProfileEntry + - " in the zip archive. Creating an empty profile."; + LOG(WARNING) << "Could not find entry " << kDexMetadataProfileEntry + << " in the zip archive. Creating an empty profile."; source->reset(ProfileSource::Create(nullptr)); return kProfileLoadSuccess; } @@ -1385,7 +1383,7 @@ bool ProfileCompilationInfo::RemapProfileIndex( // the current profile info. // Note that the number of elements should be very small, so this should not // be a performance issue. - for (const ProfileLineHeader other_profile_line_header : profile_line_headers) { + for (const ProfileLineHeader& other_profile_line_header : profile_line_headers) { if (!filter_fn(other_profile_line_header.dex_location, other_profile_line_header.checksum)) { continue; } @@ -1674,6 +1672,7 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector* os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix); } os << " [index=" << static_cast(dex_data->profile_index) << "]"; + os << " [checksum=" << std::hex << dex_data->checksum << "]" << std::dec; const DexFile* dex_file = nullptr; if (dex_files != nullptr) { for (size_t i = 0; i < dex_files->size(); i++) { @@ -2022,9 +2021,9 @@ ProfileCompilationInfo::FindOrAddDexPc(InlineCacheMap* inline_cache, uint32_t de return &(inline_cache->FindOrAdd(dex_pc, DexPcData(&allocator_))->second); } -std::unordered_set ProfileCompilationInfo::GetClassDescriptors( +HashSet ProfileCompilationInfo::GetClassDescriptors( const std::vector& dex_files) { - std::unordered_set ret; + HashSet ret; for (const DexFile* dex_file : dex_files) { const DexFileData* data = FindDexData(dex_file); if (data != nullptr) { @@ -2033,7 +2032,7 @@ std::unordered_set ProfileCompilationInfo::GetClassDescriptors( // Something went bad. The profile is probably corrupted. Abort and return an emtpy set. LOG(WARNING) << "Corrupted profile: invalid type index " << type_idx.index_ << " in dex " << dex_file->GetLocation(); - return std::unordered_set(); + return HashSet(); } const DexFile::TypeId& type_id = dex_file->GetTypeId(type_idx); ret.insert(dex_file->GetTypeDescriptor(type_id)); diff --git a/runtime/jit/profile_compilation_info.h b/libprofile/profile/profile_compilation_info.h similarity index 98% rename from runtime/jit/profile_compilation_info.h rename to libprofile/profile/profile_compilation_info.h index 90204cd5ed02a42bf779aaf5423e6e36745ffbd8..3596f3e5a6a9585a3fabb0e76d6563176b0c1aa9 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/libprofile/profile/profile_compilation_info.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_JIT_PROFILE_COMPILATION_INFO_H_ -#define ART_RUNTIME_JIT_PROFILE_COMPILATION_INFO_H_ +#ifndef ART_LIBPROFILE_PROFILE_PROFILE_COMPILATION_INFO_H_ +#define ART_LIBPROFILE_PROFILE_PROFILE_COMPILATION_INFO_H_ #include #include @@ -23,14 +23,16 @@ #include "base/arena_containers.h" #include "base/arena_object.h" #include "base/atomic.h" +#include "base/bit_memory_region.h" +#include "base/hash_set.h" +#include "base/malloc_arena_pool.h" +#include "base/mem_map.h" #include "base/safe_map.h" -#include "bit_memory_region.h" #include "dex/dex_cache_resolved_classes.h" #include "dex/dex_file.h" #include "dex/dex_file_types.h" #include "dex/method_reference.h" #include "dex/type_reference.h" -#include "mem_map.h" namespace art { @@ -72,7 +74,9 @@ class ProfileCompilationInfo { static const uint8_t kProfileMagic[]; static const uint8_t kProfileVersion[]; - static const char* kDexMetadataProfileEntry; + static const char kDexMetadataProfileEntry[]; + + static constexpr uint8_t kIndividualInlineCacheSize = 5; // Data structures for encoding the offline representation of inline caches. // This is exposed as public in order to make it available to dex2oat compilations @@ -423,7 +427,7 @@ class ProfileCompilationInfo { ArenaAllocator* GetAllocator() { return &allocator_; } // Return all of the class descriptors in the profile for a set of dex files. - std::unordered_set GetClassDescriptors(const std::vector& dex_files); + HashSet GetClassDescriptors(const std::vector& dex_files); // Return true if the fd points to a profile file. bool IsProfileFile(int fd); @@ -795,7 +799,7 @@ class ProfileCompilationInfo { friend class ProfileAssistantTest; friend class Dex2oatLayoutTest; - ArenaPool default_arena_pool_; + MallocArenaPool default_arena_pool_; ArenaAllocator allocator_; // Vector containing the actual profile info. @@ -811,4 +815,4 @@ class ProfileCompilationInfo { } // namespace art -#endif // ART_RUNTIME_JIT_PROFILE_COMPILATION_INFO_H_ +#endif // ART_LIBPROFILE_PROFILE_PROFILE_COMPILATION_INFO_H_ diff --git a/runtime/jit/profile_compilation_info_test.cc b/libprofile/profile/profile_compilation_info_test.cc similarity index 82% rename from runtime/jit/profile_compilation_info_test.cc rename to libprofile/profile/profile_compilation_info_test.cc index 7e12ddef6bd2da9bee0de38cda32645cce2bb78d..42c3320ea57b2fdb27b97a3a7cdb20f78e20a2c1 100644 --- a/runtime/jit/profile_compilation_info_test.cc +++ b/libprofile/profile/profile_compilation_info_test.cc @@ -17,20 +17,14 @@ #include #include -#include "art_method-inl.h" +#include "base/arena_allocator.h" +#include "base/common_art_test.h" #include "base/unix_file/fd_file.h" -#include "class_linker-inl.h" -#include "common_runtime_test.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "dex/method_reference.h" #include "dex/type_reference.h" -#include "handle_scope-inl.h" -#include "jit/profile_compilation_info.h" -#include "linear_alloc.h" -#include "mirror/class-inl.h" -#include "mirror/class_loader.h" -#include "scoped_thread_state_change-inl.h" +#include "profile/profile_compilation_info.h" #include "ziparchive/zip_writer.h" namespace art { @@ -39,31 +33,14 @@ using Hotness = ProfileCompilationInfo::MethodHotness; static constexpr size_t kMaxMethodIds = 65535; -class ProfileCompilationInfoTest : public CommonRuntimeTest { +class ProfileCompilationInfoTest : public CommonArtTest { public: - void PostRuntimeCreate() OVERRIDE { - allocator_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool())); + void SetUp() OVERRIDE { + CommonArtTest::SetUp(); + allocator_.reset(new ArenaAllocator(&pool_)); } protected: - std::vector GetVirtualMethods(jobject class_loader, - const std::string& clazz) { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - Thread* self = Thread::Current(); - ScopedObjectAccess soa(self); - StackHandleScope<1> hs(self); - Handle h_loader( - hs.NewHandle(self->DecodeJObject(class_loader)->AsClassLoader())); - mirror::Class* klass = class_linker->FindClass(self, clazz.c_str(), h_loader); - - const auto pointer_size = class_linker->GetImagePointerSize(); - std::vector methods; - for (auto& m : klass->GetVirtualMethods(pointer_size)) { - methods.push_back(&m); - } - return methods; - } - bool AddMethod(const std::string& dex_location, uint32_t checksum, uint16_t method_index, @@ -97,89 +74,6 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { return static_cast(file.GetFd()); } - bool SaveProfilingInfo( - const std::string& filename, - const std::vector& methods, - const std::set& resolved_classes, - Hotness::Flag flags) { - ProfileCompilationInfo info; - std::vector profile_methods; - ScopedObjectAccess soa(Thread::Current()); - for (ArtMethod* method : methods) { - profile_methods.emplace_back( - MethodReference(method->GetDexFile(), method->GetDexMethodIndex())); - } - if (!info.AddMethods(profile_methods, flags) || !info.AddClasses(resolved_classes)) { - return false; - } - if (info.GetNumberOfMethods() != profile_methods.size()) { - return false; - } - ProfileCompilationInfo file_profile; - if (!file_profile.Load(filename, false)) { - return false; - } - if (!info.MergeWith(file_profile)) { - return false; - } - - return info.Save(filename, nullptr); - } - - // Saves the given art methods to a profile backed by 'filename' and adds - // some fake inline caches to it. The added inline caches are returned in - // the out map `profile_methods_map`. - bool SaveProfilingInfoWithFakeInlineCaches( - const std::string& filename, - const std::vector& methods, - Hotness::Flag flags, - /*out*/ SafeMap* profile_methods_map) { - ProfileCompilationInfo info; - std::vector profile_methods; - ScopedObjectAccess soa(Thread::Current()); - for (ArtMethod* method : methods) { - std::vector caches; - // Monomorphic - for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) { - std::vector classes; - classes.emplace_back(method->GetDexFile(), dex::TypeIndex(0)); - caches.emplace_back(dex_pc, /*is_missing_types*/false, classes); - } - // Polymorphic - for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) { - std::vector classes; - for (uint16_t k = 0; k < InlineCache::kIndividualCacheSize / 2; k++) { - classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k)); - } - caches.emplace_back(dex_pc, /*is_missing_types*/false, classes); - } - // Megamorphic - for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) { - std::vector classes; - for (uint16_t k = 0; k < 2 * InlineCache::kIndividualCacheSize; k++) { - classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k)); - } - caches.emplace_back(dex_pc, /*is_missing_types*/false, classes); - } - // Missing types - for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) { - std::vector classes; - caches.emplace_back(dex_pc, /*is_missing_types*/true, classes); - } - ProfileMethodInfo pmi(MethodReference(method->GetDexFile(), - method->GetDexMethodIndex()), - caches); - profile_methods.push_back(pmi); - profile_methods_map->Put(method, pmi); - } - - if (!info.AddMethods(profile_methods, flags) - || info.GetNumberOfMethods() != profile_methods.size()) { - return false; - } - return info.Save(filename, nullptr); - } - // Creates an inline cache which will be destructed at the end of the test. ProfileCompilationInfo::InlineCacheMap* CreateInlineCacheMap() { used_inline_caches.emplace_back(new ProfileCompilationInfo::InlineCacheMap( @@ -187,35 +81,6 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { return used_inline_caches.back().get(); } - ProfileCompilationInfo::OfflineProfileMethodInfo ConvertProfileMethodInfo( - const ProfileMethodInfo& pmi) { - ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap(); - ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi(ic_map); - SafeMap dex_map; // dex files to profile index - for (const auto& inline_cache : pmi.inline_caches) { - ProfileCompilationInfo::DexPcData& dex_pc_data = - ic_map->FindOrAdd( - inline_cache.dex_pc, ProfileCompilationInfo::DexPcData(allocator_.get()))->second; - if (inline_cache.is_missing_types) { - dex_pc_data.SetIsMissingTypes(); - } - for (const auto& class_ref : inline_cache.classes) { - uint8_t dex_profile_index = dex_map.FindOrAdd(const_cast(class_ref.dex_file), - static_cast(dex_map.size()))->second; - dex_pc_data.AddClass(dex_profile_index, class_ref.TypeIndex()); - if (dex_profile_index >= offline_pmi.dex_references.size()) { - // This is a new dex. - const std::string& dex_key = ProfileCompilationInfo::GetProfileDexFileKey( - class_ref.dex_file->GetLocation()); - offline_pmi.dex_references.emplace_back(dex_key, - class_ref.dex_file->GetLocationChecksum(), - class_ref.dex_file->NumMethodIds()); - } - } - } - return offline_pmi; - } - // Creates an offline profile used for testing inline caches. ProfileCompilationInfo::OfflineProfileMethodInfo GetOfflineProfileMethodInfo() { ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap(); @@ -261,7 +126,7 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { ProfileCompilationInfo::InlineCacheMap* ic_map = const_cast(pmi->inline_caches); for (auto it : *ic_map) { - for (uint16_t k = 0; k <= 2 * InlineCache::kIndividualCacheSize; k++) { + for (uint16_t k = 0; k <= 2 * ProfileCompilationInfo::kIndividualInlineCacheSize; k++) { it.second.AddClass(0, dex::TypeIndex(k)); } } @@ -327,6 +192,7 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { static constexpr int kProfileMagicSize = 4; static constexpr int kProfileVersionSize = 4; + MallocArenaPool pool_; std::unique_ptr allocator_; // Cache of inline caches generated during tests. @@ -335,61 +201,6 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { std::vector> used_inline_caches; }; -TEST_F(ProfileCompilationInfoTest, SaveArtMethods) { - ScratchFile profile; - - Thread* self = Thread::Current(); - jobject class_loader; - { - ScopedObjectAccess soa(self); - class_loader = LoadDex("ProfileTestMultiDex"); - } - ASSERT_NE(class_loader, nullptr); - - // Save virtual methods from Main. - std::set resolved_classes; - std::vector main_methods = GetVirtualMethods(class_loader, "LMain;"); - ASSERT_TRUE(SaveProfilingInfo( - profile.GetFilename(), main_methods, resolved_classes, Hotness::kFlagPostStartup)); - - // Check that what we saved is in the profile. - ProfileCompilationInfo info1; - ASSERT_TRUE(info1.Load(GetFd(profile))); - ASSERT_EQ(info1.GetNumberOfMethods(), main_methods.size()); - { - ScopedObjectAccess soa(self); - for (ArtMethod* m : main_methods) { - Hotness h = info1.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())); - ASSERT_TRUE(h.IsHot()); - ASSERT_TRUE(h.IsPostStartup()); - } - } - - // Save virtual methods from Second. - std::vector second_methods = GetVirtualMethods(class_loader, "LSecond;"); - ASSERT_TRUE(SaveProfilingInfo( - profile.GetFilename(), second_methods, resolved_classes, Hotness::kFlagStartup)); - - // Check that what we saved is in the profile (methods form Main and Second). - ProfileCompilationInfo info2; - ASSERT_TRUE(profile.GetFile()->ResetOffset()); - ASSERT_TRUE(info2.Load(GetFd(profile))); - ASSERT_EQ(info2.GetNumberOfMethods(), main_methods.size() + second_methods.size()); - { - ScopedObjectAccess soa(self); - for (ArtMethod* m : main_methods) { - Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())); - ASSERT_TRUE(h.IsHot()); - ASSERT_TRUE(h.IsPostStartup()); - } - for (ArtMethod* m : second_methods) { - Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())); - ASSERT_TRUE(h.IsHot()); - ASSERT_TRUE(h.IsStartup()); - } - } -} - TEST_F(ProfileCompilationInfoTest, SaveFd) { ScratchFile profile; @@ -722,48 +533,6 @@ TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCaches) { ASSERT_TRUE(*loaded_pmi1 == pmi_extra); } -TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) { - ScratchFile profile; - - Thread* self = Thread::Current(); - jobject class_loader; - { - ScopedObjectAccess soa(self); - class_loader = LoadDex("ProfileTestMultiDex"); - } - ASSERT_NE(class_loader, nullptr); - - // Save virtual methods from Main. - std::set resolved_classes; - std::vector main_methods = GetVirtualMethods(class_loader, "LMain;"); - - SafeMap profile_methods_map; - ASSERT_TRUE(SaveProfilingInfoWithFakeInlineCaches( - profile.GetFilename(), main_methods, Hotness::kFlagStartup, &profile_methods_map)); - - // Check that what we saved is in the profile. - ProfileCompilationInfo info; - ASSERT_TRUE(info.Load(GetFd(profile))); - ASSERT_EQ(info.GetNumberOfMethods(), main_methods.size()); - { - ScopedObjectAccess soa(self); - for (ArtMethod* m : main_methods) { - Hotness h = info.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())); - ASSERT_TRUE(h.IsHot()); - ASSERT_TRUE(h.IsStartup()); - const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second; - std::unique_ptr offline_pmi = - info.GetMethod(m->GetDexFile()->GetLocation(), - m->GetDexFile()->GetLocationChecksum(), - m->GetDexMethodIndex()); - ASSERT_TRUE(offline_pmi != nullptr); - ProfileCompilationInfo::OfflineProfileMethodInfo converted_pmi = - ConvertProfileMethodInfo(pmi); - ASSERT_EQ(converted_pmi, *offline_pmi); - } - } -} - TEST_F(ProfileCompilationInfoTest, InvalidChecksumInInlineCache) { ScratchFile profile; diff --git a/oatdump/Android.bp b/oatdump/Android.bp index 71e276db10309ec1fdd413aac385a2c97bda8fbd..3cd8ae0f6d29cf651ee56f347a6f76b9a5a1d623 100644 --- a/oatdump/Android.bp +++ b/oatdump/Android.bp @@ -24,6 +24,8 @@ cc_defaults { shared_libs: ["libcutils"], }, }, + // b/79417743, oatdump 32-bit tests failed with clang lld + use_clang_lld: false, header_libs: [ "art_cmdlineparser_headers", ], @@ -37,6 +39,8 @@ art_cc_binary { "libart-compiler", "libart-disassembler", "libdexfile", + "libartbase", + "libprofile", "libbase", ], } @@ -52,12 +56,14 @@ art_cc_binary { "libartd-compiler", "libartd-disassembler", "libdexfiled", + "libartbased", + "libprofiled", "libbase", ], } -art_cc_binary { - name: "oatdumps", +cc_defaults { + name: "oatdumps-defaults", device_supported: false, static_executable: true, defaults: ["oatdump-defaults"], @@ -74,45 +80,45 @@ art_cc_binary { // Try to get rid of it. "-z muldefs", ], + static_libs: art_static_dependencies, +} + +art_cc_binary { + name: "oatdumps", + defaults: ["oatdumps-defaults"], static_libs: [ "libart", "libdexfile", + "libprofile", + "libartbase", "libart-compiler", "libart-disassembler", "libvixl-arm", "libvixl-arm64", - ] + art_static_dependencies, + ], } art_cc_binary { name: "oatdumpds", - device_supported: false, - static_executable: true, defaults: [ "art_debug_defaults", - "oatdump-defaults", + "oatdumps-defaults", ], target: { - darwin: { - enabled: false, + linux_glibc_x86_64: { + use_clang_lld: true, }, }, - ldflags: [ - // We need this because GC stress mode makes use of - // _Unwind_GetIP and _Unwind_Backtrace and the symbols are also - // defined in libgcc_eh.a(unwind-dw2.o) - // TODO: Having this is not ideal as it might obscure errors. - // Try to get rid of it. - "-z muldefs", - ], static_libs: [ "libartd", "libdexfiled", + "libprofiled", + "libartbased", "libartd-compiler", "libartd-disassembler", "libvixld-arm", "libvixld-arm64", - ] + art_static_dependencies, + ], } art_cc_test { diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 547a24c6342b17aa3c27b5b752fbe8963b576b8f..21ce8c84c47a772fd21d1d863a409aa42a3ba248 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -34,21 +35,26 @@ #include "art_field-inl.h" #include "art_method-inl.h" #include "base/bit_utils_iterator.h" +#include "base/indenter.h" #include "base/os.h" #include "base/safe_map.h" +#include "base/stats.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "class_linker-inl.h" #include "class_linker.h" +#include "class_root.h" #include "compiled_method.h" #include "debug/debug_info.h" #include "debug/elf_debug_writer.h" #include "debug/method_debug_info.h" +#include "dex/class_accessor-inl.h" #include "dex/code_item_accessors-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_instruction-inl.h" #include "dex/string_reference.h" +#include "dex/type_lookup_table.h" #include "disassembler.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/space/image_space.h" @@ -56,7 +62,6 @@ #include "gc/space/space-inl.h" #include "image-inl.h" #include "imtable-inl.h" -#include "indenter.h" #include "subtype_check.h" #include "index_bss_mapping.h" #include "interpreter/unstarted_runtime.h" @@ -75,7 +80,6 @@ #include "stack.h" #include "stack_map.h" #include "thread_list.h" -#include "type_lookup_table.h" #include "vdex_file.h" #include "verifier/method_verifier.h" #include "verifier/verifier_deps.h" @@ -103,14 +107,17 @@ const char* image_methods_descriptions_[] = { const char* image_roots_descriptions_[] = { "kDexCaches", "kClassRoots", + "kOomeWhenThrowingException", + "kOomeWhenThrowingOome", + "kOomeWhenHandlingStackOverflow", + "kNoClassDefFoundError", "kClassLoader", }; // Map is so that we don't allocate multiple dex files for the same OatDexFile. -static std::map> opened_dex_files; +static std::map> opened_dex_files; -const DexFile* OpenDexFile(const OatFile::OatDexFile* oat_dex_file, std::string* error_msg) { +const DexFile* OpenDexFile(const OatDexFile* oat_dex_file, std::string* error_msg) { DCHECK(oat_dex_file != nullptr); auto it = opened_dex_files.find(oat_dex_file); if (it != opened_dex_files.end()) { @@ -172,6 +179,7 @@ class OatSymbolizer FINAL { builder_->PrepareDynamicSection(elf_file->GetPath(), rodata_size, text_size, + oat_file_->DataBimgRelRoSize(), oat_file_->BssSize(), oat_file_->BssMethodsOffset(), oat_file_->BssRootsOffset(), @@ -231,15 +239,15 @@ class OatSymbolizer FINAL { } void Walk() { - std::vector oat_dex_files = oat_file_->GetOatDexFiles(); + std::vector oat_dex_files = oat_file_->GetOatDexFiles(); for (size_t i = 0; i < oat_dex_files.size(); i++) { - const OatFile::OatDexFile* oat_dex_file = oat_dex_files[i]; + const OatDexFile* oat_dex_file = oat_dex_files[i]; CHECK(oat_dex_file != nullptr); WalkOatDexFile(oat_dex_file); } } - void WalkOatDexFile(const OatFile::OatDexFile* oat_dex_file) { + void WalkOatDexFile(const OatDexFile* oat_dex_file) { std::string error_msg; const DexFile* const dex_file = OpenDexFile(oat_dex_file, &error_msg); if (dex_file == nullptr) { @@ -267,25 +275,18 @@ class OatSymbolizer FINAL { void WalkOatClass(const OatFile::OatClass& oat_class, const DexFile& dex_file, uint32_t class_def_index) { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { // empty class such as a marker interface? - return; - } + 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. - ClassDataItemIterator it(dex_file, class_data); uint32_t class_method_idx = 0; - it.SkipAllFields(); - for (; it.HasNextMethod(); it.Next()) { + for (const ClassAccessor::Method& method : accessor.GetMethods()) { WalkOatMethod(oat_class.GetOatMethod(class_method_idx++), dex_file, class_def_index, - it.GetMemberIndex(), - it.GetMethodCodeItem(), - it.GetMethodAccessFlags()); + method.GetIndex(), + method.GetCodeItem(), + method.GetAccessFlags()); } - DCHECK(!it.HasNext()); } void WalkOatMethod(const OatFile::OatMethod& oat_method, @@ -513,10 +514,21 @@ class OatDumper { os << StringPrintf("0x%08x\n\n", resolved_addr2instr_); } + // Dump .data.bimg.rel.ro entries. + DumpDataBimgRelRoEntries(os); + + // Dump .bss summary, individual entries are dumped per dex file. + os << ".bss: "; + if (oat_file_.GetBssMethods().empty() && oat_file_.GetBssGcRoots().empty()) { + os << "empty.\n\n"; + } else { + os << oat_file_.GetBssMethods().size() << " methods, "; + os << oat_file_.GetBssGcRoots().size() << " GC roots.\n\n"; + } + // Dumping the dex file overview is compact enough to do even if header only. - DexFileData cumulative; for (size_t i = 0; i < oat_dex_files_.size(); i++) { - const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; + const OatDexFile* oat_dex_file = oat_dex_files_[i]; CHECK(oat_dex_file != nullptr); std::string error_msg; const DexFile* const dex_file = OpenDexFile(oat_dex_file, &error_msg); @@ -525,10 +537,7 @@ class OatDumper { << error_msg; continue; } - DexFileData data(*dex_file); - os << "Dex file data for " << dex_file->GetLocation() << "\n"; - data.Dump(os); - os << "\n"; + const DexLayoutSections* const layout_sections = oat_dex_file->GetDexLayoutSections(); if (layout_sections != nullptr) { os << "Layout data\n"; @@ -536,34 +545,31 @@ class OatDumper { os << "\n"; } - cumulative.Add(data); - - // Dump .bss entries. - DumpBssEntries( - os, - "ArtMethod", - oat_dex_file->GetMethodBssMapping(), - dex_file->NumMethodIds(), - static_cast(GetInstructionSetPointerSize(instruction_set_)), - [=](uint32_t index) { return dex_file->PrettyMethod(index); }); - DumpBssEntries( - os, - "Class", - oat_dex_file->GetTypeBssMapping(), - dex_file->NumTypeIds(), - sizeof(GcRoot), - [=](uint32_t index) { return dex_file->PrettyType(dex::TypeIndex(index)); }); - DumpBssEntries( - os, - "String", - oat_dex_file->GetStringBssMapping(), - dex_file->NumStringIds(), - sizeof(GcRoot), - [=](uint32_t index) { return dex_file->StringDataByIdx(dex::StringIndex(index)); }); - } - os << "Cumulative dex file data\n"; - cumulative.Dump(os); - os << "\n"; + if (!options_.dump_header_only_) { + // Dump .bss entries. + DumpBssEntries( + os, + "ArtMethod", + oat_dex_file->GetMethodBssMapping(), + dex_file->NumMethodIds(), + static_cast(GetInstructionSetPointerSize(instruction_set_)), + [=](uint32_t index) { return dex_file->PrettyMethod(index); }); + DumpBssEntries( + os, + "Class", + oat_dex_file->GetTypeBssMapping(), + dex_file->NumTypeIds(), + sizeof(GcRoot), + [=](uint32_t index) { return dex_file->PrettyType(dex::TypeIndex(index)); }); + DumpBssEntries( + os, + "String", + oat_dex_file->GetStringBssMapping(), + dex_file->NumStringIds(), + sizeof(GcRoot), + [=](uint32_t index) { return dex_file->StringDataByIdx(dex::StringIndex(index)); }); + } + } if (!options_.dump_header_only_) { VariableIndentationOutputStream vios(&os); @@ -591,7 +597,7 @@ class OatDumper { << "\n"; } for (size_t i = 0; i < oat_dex_files_.size(); i++) { - const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; + const OatDexFile* oat_dex_file = oat_dex_files_[i]; CHECK(oat_dex_file != nullptr); if (!DumpOatDexFile(os, *oat_dex_file)) { success = false; @@ -623,7 +629,7 @@ class OatDumper { size_t i = 0; for (const auto& vdex_dex_file : vdex_dex_files) { - const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; + const OatDexFile* oat_dex_file = oat_dex_files_[i]; CHECK(oat_dex_file != nullptr); CHECK(vdex_dex_file != nullptr); if (!ExportDexFile(os, *oat_dex_file, vdex_dex_file.get())) { @@ -636,7 +642,8 @@ class OatDumper { { os << "OAT FILE STATS:\n"; VariableIndentationOutputStream vios(&os); - stats_.Dump(vios); + stats_.AddBytes(oat_file_.Size()); + DumpStats(vios, "OatFile", stats_, stats_.Value()); } os << std::flush; @@ -662,7 +669,7 @@ class OatDumper { const void* GetQuickOatCode(ArtMethod* m) REQUIRES_SHARED(Locks::mutator_lock_) { for (size_t i = 0; i < oat_dex_files_.size(); i++) { - const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; + const OatDexFile* oat_dex_file = oat_dex_files_[i]; CHECK(oat_dex_file != nullptr); std::string error_msg; const DexFile* const dex_file = OpenDexFile(oat_dex_file, &error_msg); @@ -734,154 +741,42 @@ class OatDumper { return vdex_file; } - struct Stats { - enum ByteKind { - kByteKindCode, - kByteKindQuickMethodHeader, - kByteKindCodeInfoLocationCatalog, - kByteKindCodeInfoDexRegisterMap, - kByteKindCodeInfoEncoding, - kByteKindCodeInfoInvokeInfo, - kByteKindCodeInfoStackMasks, - kByteKindCodeInfoRegisterMasks, - kByteKindStackMapNativePc, - kByteKindStackMapDexPc, - kByteKindStackMapDexRegisterMap, - kByteKindStackMapInlineInfoIndex, - kByteKindStackMapRegisterMaskIndex, - kByteKindStackMapStackMaskIndex, - kByteKindInlineInfoMethodIndexIdx, - kByteKindInlineInfoDexPc, - kByteKindInlineInfoExtraData, - kByteKindInlineInfoDexRegisterMap, - kByteKindInlineInfoIsLast, - kByteKindCount, - // Special ranges for std::accumulate convenience. - kByteKindStackMapFirst = kByteKindStackMapNativePc, - kByteKindStackMapLast = kByteKindStackMapStackMaskIndex, - kByteKindInlineInfoFirst = kByteKindInlineInfoMethodIndexIdx, - kByteKindInlineInfoLast = kByteKindInlineInfoIsLast, - }; - int64_t bits[kByteKindCount] = {}; - // Since code has deduplication, seen tracks already seen pointers to avoid double counting - // deduplicated code and tables. - std::unordered_set seen; - - // Returns true if it was newly added. - bool AddBitsIfUnique(ByteKind kind, int64_t count, const void* address) { - if (seen.insert(address).second == true) { - // True means the address was not already in the set. - AddBits(kind, count); - return true; + bool AddStatsObject(const void* address) { + return seen_stats_objects_.insert(address).second; // Inserted new entry. + } + + void DumpStats(VariableIndentationOutputStream& os, + const std::string& name, + const Stats& stats, + double total) { + if (std::fabs(stats.Value()) > 0 || !stats.Children().empty()) { + double percent = 100.0 * stats.Value() / total; + os.Stream() + << std::setw(40 - os.GetIndentation()) << std::left << name << std::right << " " + << std::setw(8) << stats.Count() << " " + << std::setw(12) << std::fixed << std::setprecision(3) << stats.Value() / KB << "KB " + << std::setw(8) << std::fixed << std::setprecision(1) << percent << "%\n"; + + // Sort all children by largest value first, than by name. + std::map, const Stats&> sorted_children; + for (const auto& it : stats.Children()) { + sorted_children.emplace(std::make_pair(-it.second.Value(), it.first), it.second); } - return false; - } - void AddBits(ByteKind kind, int64_t count) { - bits[kind] += count; - } - - void Dump(VariableIndentationOutputStream& os) { - const int64_t sum = std::accumulate(bits, bits + kByteKindCount, 0u); - os.Stream() << "Dumping cumulative use of " << sum / kBitsPerByte << " accounted bytes\n"; - if (sum > 0) { - Dump(os, "Code ", bits[kByteKindCode], sum); - Dump(os, "QuickMethodHeader ", bits[kByteKindQuickMethodHeader], sum); - Dump(os, "CodeInfoEncoding ", bits[kByteKindCodeInfoEncoding], sum); - Dump(os, "CodeInfoLocationCatalog ", bits[kByteKindCodeInfoLocationCatalog], sum); - Dump(os, "CodeInfoDexRegisterMap ", bits[kByteKindCodeInfoDexRegisterMap], sum); - Dump(os, "CodeInfoStackMasks ", bits[kByteKindCodeInfoStackMasks], sum); - Dump(os, "CodeInfoRegisterMasks ", bits[kByteKindCodeInfoRegisterMasks], sum); - Dump(os, "CodeInfoInvokeInfo ", bits[kByteKindCodeInfoInvokeInfo], sum); - // Stack map section. - const int64_t stack_map_bits = std::accumulate(bits + kByteKindStackMapFirst, - bits + kByteKindStackMapLast + 1, - 0u); - Dump(os, "CodeInfoStackMap ", stack_map_bits, sum); - { - ScopedIndentation indent1(&os); - Dump(os, - "StackMapNativePc ", - bits[kByteKindStackMapNativePc], - stack_map_bits, - "stack map"); - Dump(os, - "StackMapDexPcEncoding ", - bits[kByteKindStackMapDexPc], - stack_map_bits, - "stack map"); - Dump(os, - "StackMapDexRegisterMap ", - bits[kByteKindStackMapDexRegisterMap], - stack_map_bits, - "stack map"); - Dump(os, - "StackMapInlineInfoIndex ", - bits[kByteKindStackMapInlineInfoIndex], - stack_map_bits, - "stack map"); - Dump(os, - "StackMapRegisterMaskIndex ", - bits[kByteKindStackMapRegisterMaskIndex], - stack_map_bits, - "stack map"); - Dump(os, - "StackMapStackMaskIndex ", - bits[kByteKindStackMapStackMaskIndex], - stack_map_bits, - "stack map"); - } - // Inline info section. - const int64_t inline_info_bits = std::accumulate(bits + kByteKindInlineInfoFirst, - bits + kByteKindInlineInfoLast + 1, - 0u); - Dump(os, "CodeInfoInlineInfo ", inline_info_bits, sum); - { - ScopedIndentation indent1(&os); - Dump(os, - "InlineInfoMethodIndexIdx ", - bits[kByteKindInlineInfoMethodIndexIdx], - inline_info_bits, - "inline info"); - Dump(os, - "InlineInfoDexPc ", - bits[kByteKindStackMapDexPc], - inline_info_bits, - "inline info"); - Dump(os, - "InlineInfoExtraData ", - bits[kByteKindInlineInfoExtraData], - inline_info_bits, - "inline info"); - Dump(os, - "InlineInfoDexRegisterMap ", - bits[kByteKindInlineInfoDexRegisterMap], - inline_info_bits, - "inline info"); - Dump(os, - "InlineInfoIsLast ", - bits[kByteKindInlineInfoIsLast], - inline_info_bits, - "inline info"); - } + // Add "other" row to represent any amount not account for by the children. + Stats other; + other.AddBytes(stats.Value() - stats.SumChildrenValues(), stats.Count()); + if (std::fabs(other.Value()) > 0 && !stats.Children().empty()) { + sorted_children.emplace(std::make_pair(-other.Value(), "(other)"), other); } - os.Stream() << "\n" << std::flush; - } - private: - void Dump(VariableIndentationOutputStream& os, - const char* name, - int64_t size, - int64_t total, - const char* sum_of = "total") { - const double percent = (static_cast(size) / static_cast(total)) * 100; - os.Stream() << StringPrintf("%s = %8" PRId64 " (%2.0f%% of %s)\n", - name, - size / kBitsPerByte, - percent, - sum_of); + // Print the data. + ScopedIndentation indent1(&os); + for (const auto& it : sorted_children) { + DumpStats(os, it.first.second, it.second, total); + } } - }; + } private: void AddAllOffsets() { @@ -890,7 +785,7 @@ class OatDumper { // region, so if we keep a sorted sequence of the start of each region, we can infer the length // of a piece of code by using upper_bound to find the start of the next region. for (size_t i = 0; i < oat_dex_files_.size(); i++) { - const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; + const OatDexFile* oat_dex_file = oat_dex_files_[i]; CHECK(oat_dex_file != nullptr); std::string error_msg; const DexFile* const dex_file = OpenDexFile(oat_dex_file, &error_msg); @@ -900,20 +795,12 @@ class OatDumper { continue; } offsets_.insert(reinterpret_cast(&dex_file->GetHeader())); - for (size_t class_def_index = 0; - class_def_index < dex_file->NumClassDefs(); - class_def_index++) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index); - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data != nullptr) { - ClassDataItemIterator it(*dex_file, class_data); - it.SkipAllFields(); - uint32_t class_method_index = 0; - while (it.HasNextMethod()) { - AddOffsets(oat_class.GetOatMethod(class_method_index++)); - it.Next(); - } + for (ClassAccessor accessor : dex_file->GetClasses()) { + 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)); } } } @@ -937,121 +824,7 @@ class OatDumper { offsets_.insert(oat_method.GetVmapTableOffset()); } - // Dex file data, may be for multiple different dex files. - class DexFileData { - public: - DexFileData() {} - - explicit DexFileData(const DexFile& dex_file) - : num_string_ids_(dex_file.NumStringIds()), - num_method_ids_(dex_file.NumMethodIds()), - num_field_ids_(dex_file.NumFieldIds()), - num_type_ids_(dex_file.NumTypeIds()), - num_class_defs_(dex_file.NumClassDefs()) { - for (size_t class_def_index = 0; class_def_index < num_class_defs_; ++class_def_index) { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); - WalkClass(dex_file, class_def); - } - } - - void Add(const DexFileData& other) { - AddAll(unique_string_ids_from_code_, other.unique_string_ids_from_code_); - num_string_ids_from_code_ += other.num_string_ids_from_code_; - AddAll(dex_code_item_ptrs_, other.dex_code_item_ptrs_); - dex_code_bytes_ += other.dex_code_bytes_; - num_string_ids_ += other.num_string_ids_; - num_method_ids_ += other.num_method_ids_; - num_field_ids_ += other.num_field_ids_; - num_type_ids_ += other.num_type_ids_; - num_class_defs_ += other.num_class_defs_; - } - - void Dump(std::ostream& os) { - os << "Num string ids: " << num_string_ids_ << "\n"; - os << "Num method ids: " << num_method_ids_ << "\n"; - os << "Num field ids: " << num_field_ids_ << "\n"; - os << "Num type ids: " << num_type_ids_ << "\n"; - os << "Num class defs: " << num_class_defs_ << "\n"; - os << "Unique strings loaded from dex code: " << unique_string_ids_from_code_.size() << "\n"; - os << "Total strings loaded from dex code: " << num_string_ids_from_code_ << "\n"; - os << "Number of unique dex code items: " << dex_code_item_ptrs_.size() << "\n"; - os << "Total number of dex code bytes: " << dex_code_bytes_ << "\n"; - } - - private: - // All of the elements from one container to another. - template - static void AddAll(Dest& dest, const Src& src) { - dest.insert(src.begin(), src.end()); - } - - void WalkClass(const DexFile& dex_file, const DexFile::ClassDef& class_def) { - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { // empty class such as a marker interface? - return; - } - ClassDataItemIterator it(dex_file, class_data); - it.SkipAllFields(); - while (it.HasNextMethod()) { - WalkCodeItem(dex_file, it.GetMethodCodeItem()); - it.Next(); - } - DCHECK(!it.HasNext()); - } - - void WalkCodeItem(const DexFile& dex_file, const DexFile::CodeItem* code_item) { - if (code_item == nullptr) { - return; - } - CodeItemInstructionAccessor instructions(dex_file, code_item); - - // If we inserted a new dex code item pointer, add to total code bytes. - const uint16_t* code_ptr = instructions.Insns(); - if (dex_code_item_ptrs_.insert(code_ptr).second) { - dex_code_bytes_ += instructions.InsnsSizeInCodeUnits() * sizeof(code_ptr[0]); - } - - for (const DexInstructionPcPair& inst : instructions) { - switch (inst->Opcode()) { - case Instruction::CONST_STRING: { - const dex::StringIndex string_index(inst->VRegB_21c()); - unique_string_ids_from_code_.insert(StringReference(&dex_file, string_index)); - ++num_string_ids_from_code_; - break; - } - case Instruction::CONST_STRING_JUMBO: { - const dex::StringIndex string_index(inst->VRegB_31c()); - unique_string_ids_from_code_.insert(StringReference(&dex_file, string_index)); - ++num_string_ids_from_code_; - break; - } - default: - break; - } - } - } - - // Unique string ids loaded from dex code. - std::set unique_string_ids_from_code_; - - // Total string ids loaded from dex code. - size_t num_string_ids_from_code_ = 0; - - // Unique code pointers. - std::set dex_code_item_ptrs_; - - // Total "unique" dex code bytes. - size_t dex_code_bytes_ = 0; - - // Other dex ids. - size_t num_string_ids_ = 0; - size_t num_method_ids_ = 0; - size_t num_field_ids_ = 0; - size_t num_type_ids_ = 0; - size_t num_class_defs_ = 0; - }; - - bool DumpOatDexFile(std::ostream& os, const OatFile::OatDexFile& oat_dex_file) { + bool DumpOatDexFile(std::ostream& os, const OatDexFile& oat_dex_file) { bool success = true; bool stop_analysis = false; os << "OatDexFile:\n"; @@ -1132,9 +905,7 @@ class OatDumper { // Dex resource is extracted from the oat_dex_file and its checksum is repaired since it's not // unquickened. Otherwise the dex_file has been fully unquickened and is expected to verify the // original checksum. - bool ExportDexFile(std::ostream& os, - const OatFile::OatDexFile& oat_dex_file, - const DexFile* dex_file) { + bool ExportDexFile(std::ostream& os, const OatDexFile& oat_dex_file, const DexFile* dex_file) { std::string error_msg; std::string dex_file_location = oat_dex_file.GetDexFileLocation(); size_t fsize = oat_dex_file.FileSize(); @@ -1383,9 +1154,9 @@ class OatDumper { vios->Stream() << "OatQuickMethodHeader "; uint32_t method_header_offset = oat_method.GetOatQuickMethodHeaderOffset(); const OatQuickMethodHeader* method_header = oat_method.GetOatQuickMethodHeader(); - stats_.AddBitsIfUnique(Stats::kByteKindQuickMethodHeader, - sizeof(*method_header) * kBitsPerByte, - method_header); + if (AddStatsObject(method_header)) { + stats_.Child("QuickMethodHeader")->AddBytes(sizeof(*method_header)); + } if (options_.absolute_addresses_) { vios->Stream() << StringPrintf("%p ", method_header); } @@ -1457,7 +1228,9 @@ class OatDumper { const void* code = oat_method.GetQuickCode(); uint32_t aligned_code_begin = AlignCodeOffset(code_offset); uint64_t aligned_code_end = aligned_code_begin + code_size; - stats_.AddBitsIfUnique(Stats::kByteKindCode, code_size * kBitsPerByte, code); + if (AddStatsObject(code)) { + stats_.Child("Code")->AddBytes(code_size); + } if (options_.absolute_addresses_) { vios->Stream() << StringPrintf("%p ", code); @@ -1544,7 +1317,7 @@ class OatDumper { DCHECK(code_item_accessor.HasCodeItem()); ScopedIndentation indent1(vios); MethodInfo method_info = oat_method.GetOatQuickMethodHeader()->GetOptimizedMethodInfo(); - DumpCodeInfo(vios, code_info, oat_method, code_item_accessor, method_info); + DumpCodeInfo(vios, code_info, oat_method, method_info); } } else if (IsMethodGeneratedByDexToDexCompiler(oat_method, code_item_accessor)) { // We don't encode the size in the table, so just emit that we have quickened @@ -1560,11 +1333,9 @@ class OatDumper { void DumpCodeInfo(VariableIndentationOutputStream* vios, const CodeInfo& code_info, const OatFile::OatMethod& oat_method, - const CodeItemDataAccessor& code_item_accessor, const MethodInfo& method_info) { code_info.Dump(vios, oat_method.GetCodeOffset(), - code_item_accessor.RegistersSize(), options_.dump_code_info_stack_maps_, instruction_set_, method_info); @@ -1701,7 +1472,10 @@ class OatDumper { CHECK(dex_cache != nullptr); ArtMethod* method = runtime->GetClassLinker()->ResolveMethodWithoutInvokeType( dex_method_idx, dex_cache, *options_.class_loader_); - CHECK(method != nullptr); + if (method == nullptr) { + soa.Self()->ClearException(); + return nullptr; + } return verifier::MethodVerifier::VerifyMethodAndDump( soa.Self(), vios, dex_method_idx, dex_file, dex_cache, *options_.class_loader_, class_def, code_item, method, method_access_flags); @@ -1716,8 +1490,7 @@ class OatDumper { public: explicit StackMapsHelper(const uint8_t* raw_code_info, InstructionSet instruction_set) : code_info_(raw_code_info), - encoding_(code_info_.ExtractEncoding()), - number_of_stack_maps_(code_info_.GetNumberOfStackMaps(encoding_)), + number_of_stack_maps_(code_info_.GetNumberOfStackMaps()), indexes_(), offset_(static_cast(-1)), stack_map_index_(0u), @@ -1725,11 +1498,11 @@ class OatDumper { if (number_of_stack_maps_ != 0u) { // Check if native PCs are ordered. bool ordered = true; - StackMap last = code_info_.GetStackMapAt(0u, encoding_); + StackMap last = code_info_.GetStackMapAt(0u); for (size_t i = 1; i != number_of_stack_maps_; ++i) { - StackMap current = code_info_.GetStackMapAt(i, encoding_); - if (last.GetNativePcOffset(encoding_.stack_map.encoding, instruction_set) > - current.GetNativePcOffset(encoding_.stack_map.encoding, instruction_set)) { + StackMap current = code_info_.GetStackMapAt(i); + if (last.GetNativePcOffset(instruction_set) > + current.GetNativePcOffset(instruction_set)) { ordered = false; break; } @@ -1744,18 +1517,15 @@ class OatDumper { std::sort(indexes_.begin(), indexes_.end(), [this](size_t lhs, size_t rhs) { - StackMap left = code_info_.GetStackMapAt(lhs, encoding_); - uint32_t left_pc = left.GetNativePcOffset(encoding_.stack_map.encoding, - instruction_set_); - StackMap right = code_info_.GetStackMapAt(rhs, encoding_); - uint32_t right_pc = right.GetNativePcOffset(encoding_.stack_map.encoding, - instruction_set_); + StackMap left = code_info_.GetStackMapAt(lhs); + uint32_t left_pc = left.GetNativePcOffset(instruction_set_); + StackMap right = code_info_.GetStackMapAt(rhs); + uint32_t right_pc = right.GetNativePcOffset(instruction_set_); // If the PCs are the same, compare indexes to preserve the original order. return (left_pc < right_pc) || (left_pc == right_pc && lhs < rhs); }); } - offset_ = GetStackMapAt(0).GetNativePcOffset(encoding_.stack_map.encoding, - instruction_set_); + offset_ = GetStackMapAt(0).GetNativePcOffset(instruction_set_); } } @@ -1763,10 +1533,6 @@ class OatDumper { return code_info_; } - const CodeInfoEncoding& GetEncoding() const { - return encoding_; - } - uint32_t GetOffset() const { return offset_; } @@ -1779,8 +1545,7 @@ class OatDumper { ++stack_map_index_; offset_ = (stack_map_index_ == number_of_stack_maps_) ? static_cast(-1) - : GetStackMapAt(stack_map_index_).GetNativePcOffset(encoding_.stack_map.encoding, - instruction_set_); + : GetStackMapAt(stack_map_index_).GetNativePcOffset(instruction_set_); } private: @@ -1789,11 +1554,10 @@ class OatDumper { i = indexes_[i]; } DCHECK_LT(i, number_of_stack_maps_); - return code_info_.GetStackMapAt(i, encoding_); + return code_info_.GetStackMapAt(i); } const CodeInfo code_info_; - const CodeInfoEncoding encoding_; const size_t number_of_stack_maps_; dchecked_vector indexes_; // Used if stack map native PCs are not ordered. uint32_t offset_; @@ -1816,85 +1580,15 @@ class OatDumper { } else if (!bad_input && IsMethodGeneratedByOptimizingCompiler(oat_method, code_item_accessor)) { // The optimizing compiler outputs its CodeInfo data in the vmap table. + const OatQuickMethodHeader* method_header = oat_method.GetOatQuickMethodHeader(); StackMapsHelper helper(oat_method.GetVmapTable(), instruction_set_); - MethodInfo method_info(oat_method.GetOatQuickMethodHeader()->GetOptimizedMethodInfo()); - { - CodeInfoEncoding encoding(helper.GetEncoding()); - StackMapEncoding stack_map_encoding(encoding.stack_map.encoding); - const size_t num_stack_maps = encoding.stack_map.num_entries; - if (stats_.AddBitsIfUnique(Stats::kByteKindCodeInfoEncoding, - encoding.HeaderSize() * kBitsPerByte, - oat_method.GetVmapTable())) { - // Stack maps - stats_.AddBits( - Stats::kByteKindStackMapNativePc, - stack_map_encoding.GetNativePcEncoding().BitSize() * num_stack_maps); - stats_.AddBits( - Stats::kByteKindStackMapDexPc, - stack_map_encoding.GetDexPcEncoding().BitSize() * num_stack_maps); - stats_.AddBits( - Stats::kByteKindStackMapDexRegisterMap, - stack_map_encoding.GetDexRegisterMapEncoding().BitSize() * num_stack_maps); - stats_.AddBits( - Stats::kByteKindStackMapInlineInfoIndex, - stack_map_encoding.GetInlineInfoEncoding().BitSize() * num_stack_maps); - stats_.AddBits( - Stats::kByteKindStackMapRegisterMaskIndex, - stack_map_encoding.GetRegisterMaskIndexEncoding().BitSize() * num_stack_maps); - stats_.AddBits( - Stats::kByteKindStackMapStackMaskIndex, - stack_map_encoding.GetStackMaskIndexEncoding().BitSize() * num_stack_maps); - - // Stack masks - stats_.AddBits( - Stats::kByteKindCodeInfoStackMasks, - encoding.stack_mask.encoding.BitSize() * encoding.stack_mask.num_entries); - - // Register masks - stats_.AddBits( - Stats::kByteKindCodeInfoRegisterMasks, - encoding.register_mask.encoding.BitSize() * encoding.register_mask.num_entries); - - // Invoke infos - if (encoding.invoke_info.num_entries > 0u) { - stats_.AddBits( - Stats::kByteKindCodeInfoInvokeInfo, - encoding.invoke_info.encoding.BitSize() * encoding.invoke_info.num_entries); - } - - // Location catalog - const size_t location_catalog_bytes = - helper.GetCodeInfo().GetDexRegisterLocationCatalogSize(encoding); - stats_.AddBits(Stats::kByteKindCodeInfoLocationCatalog, - kBitsPerByte * location_catalog_bytes); - // Dex register bytes. - const size_t dex_register_bytes = - helper.GetCodeInfo().GetDexRegisterMapsSize(encoding, - code_item_accessor.RegistersSize()); - stats_.AddBits( - Stats::kByteKindCodeInfoDexRegisterMap, - kBitsPerByte * dex_register_bytes); - - // Inline infos. - const size_t num_inline_infos = encoding.inline_info.num_entries; - if (num_inline_infos > 0u) { - stats_.AddBits( - Stats::kByteKindInlineInfoMethodIndexIdx, - encoding.inline_info.encoding.GetMethodIndexIdxEncoding().BitSize() * - num_inline_infos); - stats_.AddBits( - Stats::kByteKindInlineInfoDexPc, - encoding.inline_info.encoding.GetDexPcEncoding().BitSize() * num_inline_infos); - stats_.AddBits( - Stats::kByteKindInlineInfoExtraData, - encoding.inline_info.encoding.GetExtraDataEncoding().BitSize() * num_inline_infos); - stats_.AddBits( - Stats::kByteKindInlineInfoDexRegisterMap, - encoding.inline_info.encoding.GetDexRegisterMapEncoding().BitSize() * - num_inline_infos); - stats_.AddBits(Stats::kByteKindInlineInfoIsLast, num_inline_infos); - } - } + if (AddStatsObject(oat_method.GetVmapTable())) { + helper.GetCodeInfo().AddSizeStats(&stats_); + } + MethodInfo method_info(method_header->GetOptimizedMethodInfo()); + if (AddStatsObject(method_header->GetOptimizedMethodInfoPtr())) { + size_t method_info_size = MethodInfo::ComputeSize(method_info.NumMethodIndices()); + stats_.Child("MethodInfo")->AddBytes(method_info_size); } const uint8_t* quick_native_pc = reinterpret_cast(quick_code); size_t offset = 0; @@ -1906,10 +1600,8 @@ class OatDumper { DCHECK(stack_map.IsValid()); stack_map.Dump(vios, helper.GetCodeInfo(), - helper.GetEncoding(), method_info, oat_method.GetCodeOffset(), - code_item_accessor.RegistersSize(), instruction_set_); do { helper.Next(); @@ -1927,6 +1619,66 @@ class OatDumper { } } + void DumpDataBimgRelRoEntries(std::ostream& os) { + os << ".data.bimg.rel.ro: "; + if (oat_file_.GetBootImageRelocations().empty()) { + os << "empty.\n\n"; + return; + } + + os << oat_file_.GetBootImageRelocations().size() << " entries.\n"; + Runtime* runtime = Runtime::Current(); + if (runtime != nullptr && !runtime->GetHeap()->GetBootImageSpaces().empty()) { + const std::vector& boot_image_spaces = + runtime->GetHeap()->GetBootImageSpaces(); + ScopedObjectAccess soa(Thread::Current()); + for (const uint32_t& object_offset : oat_file_.GetBootImageRelocations()) { + uint32_t entry_index = &object_offset - oat_file_.GetBootImageRelocations().data(); + uint32_t entry_offset = entry_index * sizeof(oat_file_.GetBootImageRelocations()[0]); + os << StringPrintf(" 0x%x: 0x%08x", entry_offset, object_offset); + uint8_t* object = boot_image_spaces[0]->Begin() + object_offset; + bool found = false; + for (gc::space::ImageSpace* space : boot_image_spaces) { + uint64_t local_offset = object - space->Begin(); + if (local_offset < space->GetImageHeader().GetImageSize()) { + if (space->GetImageHeader().GetObjectsSection().Contains(local_offset)) { + ObjPtr o = reinterpret_cast(object); + if (o->IsString()) { + os << " String: " << o->AsString()->ToModifiedUtf8(); + } else if (o->IsClass()) { + os << " Class: " << o->AsClass()->PrettyDescriptor(); + } else { + os << StringPrintf(" 0x%08x %s", + object_offset, + o->GetClass()->PrettyDescriptor().c_str()); + } + } else if (space->GetImageHeader().GetMethodsSection().Contains(local_offset)) { + ArtMethod* m = reinterpret_cast(object); + os << " ArtMethod: " << m->PrettyMethod(); + } else { + os << StringPrintf(" 0x%08x ", + object_offset, + space->GetImageFilename().c_str()); + } + found = true; + break; + } + } + if (!found) { + os << StringPrintf(" 0x%08x ", object_offset); + } + os << "\n"; + } + } else { + for (const uint32_t& object_offset : oat_file_.GetBootImageRelocations()) { + uint32_t entry_index = &object_offset - oat_file_.GetBootImageRelocations().data(); + uint32_t entry_offset = entry_index * sizeof(oat_file_.GetBootImageRelocations()[0]); + os << StringPrintf(" 0x%x: 0x%08x\n", entry_offset, object_offset); + } + } + os << "\n"; + } + template void DumpBssEntries(std::ostream& os, const char* slot_type, @@ -1962,13 +1714,14 @@ class OatDumper { } const OatFile& oat_file_; - const std::vector oat_dex_files_; + const std::vector oat_dex_files_; const OatDumperOptions& options_; uint32_t resolved_addr2instr_; const InstructionSet instruction_set_; std::set offsets_; Disassembler* disassembler_; Stats stats_; + std::unordered_set seen_stats_objects_; }; class ImageDumper { @@ -2016,17 +1769,17 @@ class ImageDumper { os << "COMPILE PIC: " << (image_header_.CompilePic() ? "yes" : "no") << "\n\n"; { - os << "ROOTS: " << reinterpret_cast(image_header_.GetImageRoots()) << "\n"; + os << "ROOTS: " << reinterpret_cast(image_header_.GetImageRoots().Ptr()) << "\n"; static_assert(arraysize(image_roots_descriptions_) == static_cast(ImageHeader::kImageRootsMax), "sizes must match"); DCHECK_LE(image_header_.GetImageRoots()->GetLength(), ImageHeader::kImageRootsMax); for (int32_t i = 0, size = image_header_.GetImageRoots()->GetLength(); i != size; ++i) { ImageHeader::ImageRoot image_root = static_cast(i); const char* image_root_description = image_roots_descriptions_[i]; - mirror::Object* image_root_object = image_header_.GetImageRoot(image_root); - indent_os << StringPrintf("%s: %p\n", image_root_description, image_root_object); + ObjPtr image_root_object = image_header_.GetImageRoot(image_root); + indent_os << StringPrintf("%s: %p\n", image_root_description, image_root_object.Ptr()); if (image_root_object != nullptr && image_root_object->IsObjectArray()) { - mirror::ObjectArray* image_root_object_array + ObjPtr> image_root_object_array = image_root_object->AsObjectArray(); ScopedIndentation indent2(&vios_); for (int j = 0; j < image_root_object_array->GetLength(); j++) { @@ -2100,7 +1853,7 @@ class ImageDumper { oat_dumper_.reset(new OatDumper(*oat_file, *oat_dumper_options_)); - for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) { + for (const OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) { CHECK(oat_dex_file != nullptr); stats_.oat_dex_file_sizes.push_back(std::make_pair(oat_dex_file->GetDexFileLocation(), oat_dex_file->FileSize())); @@ -2202,7 +1955,7 @@ class ImageDumper { // Intern table is 8-byte aligned. uint32_t end_caches = dex_cache_arrays_section.Offset() + dex_cache_arrays_section.Size(); - CHECK_ALIGNED(intern_section.Offset(), sizeof(uint64_t)); + CHECK_EQ(RoundUp(end_caches, 8U), intern_section.Offset()); stats_.alignment_bytes += intern_section.Offset() - end_caches; // Add space between intern table and class table. @@ -3026,7 +2779,7 @@ static jobject InstallOatFile(Runtime* runtime, OatFile* oat_file_ptr = oat_file.get(); ClassLinker* class_linker = runtime->GetClassLinker(); runtime->GetOatFileManager().RegisterOatFile(std::move(oat_file)); - for (const OatFile::OatDexFile* odf : oat_file_ptr->GetOatDexFiles()) { + for (const OatDexFile* odf : oat_file_ptr->GetOatDexFiles()) { std::string error_msg; const DexFile* const dex_file = OpenDexFile(odf, &error_msg); CHECK(dex_file != nullptr) << error_msg; @@ -3327,7 +3080,7 @@ class IMTDumper { PrepareClass(runtime, klass, prepared); } - mirror::Class* object_class = mirror::Class::GetJavaLangClass()->GetSuperClass(); + ObjPtr object_class = GetClassRoot(); DCHECK(object_class->IsObjectClass()); bool result = klass->GetImt(pointer_size) == object_class->GetImt(pointer_size); @@ -3361,8 +3114,8 @@ class IMTDumper { Handle h_loader, const std::string& class_name, const PointerSize pointer_size, - mirror::Class** klass_out, - std::unordered_set* prepared) + /*out*/ ObjPtr* klass_out, + /*inout*/ std::unordered_set* prepared) REQUIRES_SHARED(Locks::mutator_lock_) { if (class_name.empty()) { return nullptr; @@ -3375,7 +3128,8 @@ class IMTDumper { descriptor = DotToDescriptor(class_name.c_str()); } - mirror::Class* klass = runtime->GetClassLinker()->FindClass(self, descriptor.c_str(), h_loader); + ObjPtr klass = + runtime->GetClassLinker()->FindClass(self, descriptor.c_str(), h_loader); if (klass == nullptr) { self->ClearException(); @@ -3395,7 +3149,7 @@ class IMTDumper { static ImTable* PrepareAndGetImTable(Runtime* runtime, Handle h_klass, const PointerSize pointer_size, - std::unordered_set* prepared) + /*inout*/ std::unordered_set* prepared) REQUIRES_SHARED(Locks::mutator_lock_) { PrepareClass(runtime, h_klass, prepared); return h_klass->GetImt(pointer_size); @@ -3407,7 +3161,7 @@ class IMTDumper { std::unordered_set* prepared) REQUIRES_SHARED(Locks::mutator_lock_) { const PointerSize pointer_size = runtime->GetClassLinker()->GetImagePointerSize(); - mirror::Class* klass; + ObjPtr klass; ImTable* imt = PrepareAndGetImTable(runtime, Thread::Current(), h_loader, @@ -3463,10 +3217,10 @@ class IMTDumper { const std::string& class_name, const std::string& method, Handle h_loader, - std::unordered_set* prepared) + /*inout*/ std::unordered_set* prepared) REQUIRES_SHARED(Locks::mutator_lock_) { const PointerSize pointer_size = runtime->GetClassLinker()->GetImagePointerSize(); - mirror::Class* klass; + ObjPtr klass; ImTable* imt = PrepareAndGetImTable(runtime, Thread::Current(), h_loader, @@ -3569,7 +3323,7 @@ class IMTDumper { // and note in the given set that the work was done. static void PrepareClass(Runtime* runtime, Handle h_klass, - std::unordered_set* done) + /*inout*/ std::unordered_set* done) REQUIRES_SHARED(Locks::mutator_lock_) { if (!h_klass->ShouldHaveImt()) { return; diff --git a/oatdump/oatdump_app_test.cc b/oatdump/oatdump_app_test.cc index 34b07d2ddf81b89fb88f38cb2100fb9e528fc161..a34428625948c4c1cb437f1fac08a39db1c58af6 100644 --- a/oatdump/oatdump_app_test.cc +++ b/oatdump/oatdump_app_test.cc @@ -19,31 +19,23 @@ namespace art { TEST_F(OatDumpTest, TestAppWithBootImage) { - std::string error_msg; - ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {"--runtime-arg", "-Xmx64M"}, &error_msg)) << error_msg; - ASSERT_TRUE(Exec(kDynamic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg; + ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {"--runtime-arg", "-Xmx64M"})); + ASSERT_TRUE(Exec(kDynamic, kModeOatWithBootImage, {}, kListAndCode)); } TEST_F(OatDumpTest, TestAppWithBootImageStatic) { TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); - std::string error_msg; - ASSERT_TRUE(GenerateAppOdexFile(kStatic, {"--runtime-arg", "-Xmx64M"}, &error_msg)) << error_msg; - ASSERT_TRUE(Exec(kStatic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg; + ASSERT_TRUE(GenerateAppOdexFile(kStatic, {"--runtime-arg", "-Xmx64M"})); + ASSERT_TRUE(Exec(kStatic, kModeOatWithBootImage, {}, kListAndCode)); } TEST_F(OatDumpTest, TestPicAppWithBootImage) { - std::string error_msg; - ASSERT_TRUE( - GenerateAppOdexFile(kDynamic, {"--runtime-arg", "-Xmx64M", "--compile-pic"}, &error_msg)) - << error_msg; - ASSERT_TRUE(Exec(kDynamic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg; + ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {"--runtime-arg", "-Xmx64M", "--compile-pic"})); + ASSERT_TRUE(Exec(kDynamic, kModeOatWithBootImage, {}, kListAndCode)); } TEST_F(OatDumpTest, TestPicAppWithBootImageStatic) { TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); - std::string error_msg; - ASSERT_TRUE( - GenerateAppOdexFile(kStatic, {"--runtime-arg", "-Xmx64M", "--compile-pic"}, &error_msg)) - << error_msg; - ASSERT_TRUE(Exec(kStatic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg; + ASSERT_TRUE(GenerateAppOdexFile(kStatic, {"--runtime-arg", "-Xmx64M", "--compile-pic"})); + ASSERT_TRUE(Exec(kStatic, kModeOatWithBootImage, {}, kListAndCode)); } } // namespace art diff --git a/oatdump/oatdump_image_test.cc b/oatdump/oatdump_image_test.cc index d054ecefbb698b29cfa6683ae6ecc9390765c74f..de48b04214631f31ef52c97c1cd2d580071e7838 100644 --- a/oatdump/oatdump_image_test.cc +++ b/oatdump/oatdump_image_test.cc @@ -19,25 +19,34 @@ namespace art { // Disable tests on arm and mips as they are taking too long to run. b/27824283. -#if !defined(__arm__) && !defined(__mips__) +#define TEST_DISABLED_FOR_ARM_AND_MIPS() \ + TEST_DISABLED_FOR_ARM(); \ + TEST_DISABLED_FOR_ARM64(); \ + TEST_DISABLED_FOR_MIPS(); \ + TEST_DISABLED_FOR_MIPS64(); \ + TEST_F(OatDumpTest, TestImage) { + TEST_DISABLED_FOR_ARM_AND_MIPS(); std::string error_msg; - ASSERT_TRUE(Exec(kDynamic, kModeArt, {}, kListAndCode, &error_msg)) << error_msg; + ASSERT_TRUE(Exec(kDynamic, kModeArt, {}, kListAndCode)); } TEST_F(OatDumpTest, TestImageStatic) { + TEST_DISABLED_FOR_ARM_AND_MIPS(); TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); std::string error_msg; - ASSERT_TRUE(Exec(kStatic, kModeArt, {}, kListAndCode, &error_msg)) << error_msg; + ASSERT_TRUE(Exec(kStatic, kModeArt, {}, kListAndCode)); } TEST_F(OatDumpTest, TestOatImage) { + TEST_DISABLED_FOR_ARM_AND_MIPS(); std::string error_msg; - ASSERT_TRUE(Exec(kDynamic, kModeOat, {}, kListAndCode, &error_msg)) << error_msg; + ASSERT_TRUE(Exec(kDynamic, kModeOat, {}, kListAndCode)); } TEST_F(OatDumpTest, TestOatImageStatic) { + TEST_DISABLED_FOR_ARM_AND_MIPS(); TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); std::string error_msg; - ASSERT_TRUE(Exec(kStatic, kModeOat, {}, kListAndCode, &error_msg)) << error_msg; + ASSERT_TRUE(Exec(kStatic, kModeOat, {}, kListAndCode)); } -#endif + } // namespace art diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc index b4eddb91f98143a28ef65869316a769ce2e7e4d7..bcba18208bd32ab7d56fc1318d0b3f54a2f8000b 100644 --- a/oatdump/oatdump_test.cc +++ b/oatdump/oatdump_test.cc @@ -19,75 +19,92 @@ namespace art { // Disable tests on arm and mips as they are taking too long to run. b/27824283. -#if !defined(__arm__) && !defined(__mips__) +#define TEST_DISABLED_FOR_ARM_AND_MIPS() \ + TEST_DISABLED_FOR_ARM(); \ + TEST_DISABLED_FOR_ARM64(); \ + TEST_DISABLED_FOR_MIPS(); \ + TEST_DISABLED_FOR_MIPS64(); \ + TEST_F(OatDumpTest, TestNoDumpVmap) { + TEST_DISABLED_FOR_ARM_AND_MIPS(); std::string error_msg; - ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--no-dump:vmap"}, kListAndCode, &error_msg)) << error_msg; + ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--no-dump:vmap"}, kListAndCode)); } TEST_F(OatDumpTest, TestNoDumpVmapStatic) { + TEST_DISABLED_FOR_ARM_AND_MIPS(); TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); std::string error_msg; - ASSERT_TRUE(Exec(kStatic, kModeArt, {"--no-dump:vmap"}, kListAndCode, &error_msg)) << error_msg; + ASSERT_TRUE(Exec(kStatic, kModeArt, {"--no-dump:vmap"}, kListAndCode)); } TEST_F(OatDumpTest, TestNoDisassemble) { + TEST_DISABLED_FOR_ARM_AND_MIPS(); std::string error_msg; - ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--no-disassemble"}, kListAndCode, &error_msg)) - << error_msg; + ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--no-disassemble"}, kListAndCode)); } TEST_F(OatDumpTest, TestNoDisassembleStatic) { + TEST_DISABLED_FOR_ARM_AND_MIPS(); TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); std::string error_msg; - ASSERT_TRUE(Exec(kStatic, kModeArt, {"--no-disassemble"}, kListAndCode, &error_msg)) << error_msg; + ASSERT_TRUE(Exec(kStatic, kModeArt, {"--no-disassemble"}, kListAndCode)); } TEST_F(OatDumpTest, TestListClasses) { + TEST_DISABLED_FOR_ARM_AND_MIPS(); std::string error_msg; - ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--list-classes"}, kListOnly, &error_msg)) << error_msg; + ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--list-classes"}, kListOnly)); } TEST_F(OatDumpTest, TestListClassesStatic) { + TEST_DISABLED_FOR_ARM_AND_MIPS(); TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); std::string error_msg; - ASSERT_TRUE(Exec(kStatic, kModeArt, {"--list-classes"}, kListOnly, &error_msg)) << error_msg; + ASSERT_TRUE(Exec(kStatic, kModeArt, {"--list-classes"}, kListOnly)); } TEST_F(OatDumpTest, TestListMethods) { + TEST_DISABLED_FOR_ARM_AND_MIPS(); std::string error_msg; - ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--list-methods"}, kListOnly, &error_msg)) << error_msg; + ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--list-methods"}, kListOnly)); } TEST_F(OatDumpTest, TestListMethodsStatic) { + TEST_DISABLED_FOR_ARM_AND_MIPS(); TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); std::string error_msg; - ASSERT_TRUE(Exec(kStatic, kModeArt, {"--list-methods"}, kListOnly, &error_msg)) << error_msg; + ASSERT_TRUE(Exec(kStatic, kModeArt, {"--list-methods"}, kListOnly)); } TEST_F(OatDumpTest, TestSymbolize) { + TEST_DISABLED_FOR_ARM_AND_MIPS(); std::string error_msg; - ASSERT_TRUE(Exec(kDynamic, kModeSymbolize, {}, kListOnly, &error_msg)) << error_msg; + ASSERT_TRUE(Exec(kDynamic, kModeSymbolize, {}, kListOnly)); } TEST_F(OatDumpTest, TestSymbolizeStatic) { + TEST_DISABLED_FOR_ARM_AND_MIPS(); TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); std::string error_msg; - ASSERT_TRUE(Exec(kStatic, kModeSymbolize, {}, kListOnly, &error_msg)) << error_msg; + ASSERT_TRUE(Exec(kStatic, kModeSymbolize, {}, kListOnly)); } TEST_F(OatDumpTest, TestExportDex) { + TEST_DISABLED_FOR_ARM_AND_MIPS(); // Test is failing on target, b/77469384. TEST_DISABLED_FOR_TARGET(); std::string error_msg; - ASSERT_TRUE(Exec(kDynamic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly, &error_msg)) - << error_msg; + ASSERT_TRUE(Exec(kDynamic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly)); const std::string dex_location = tmp_dir_+ "/core-oj-hostdex.jar_export.dex"; const std::string dexdump2 = GetExecutableFilePath("dexdump2", /*is_debug*/false, /*is_static*/false); - ASSERT_TRUE(ForkAndExecAndWait({dexdump2, "-d", dex_location}, &error_msg)) << error_msg; + std::string output; + auto post_fork_fn = []() { return true; }; + ForkAndExecResult res = ForkAndExec({dexdump2, "-d", dex_location}, post_fork_fn, &output); + ASSERT_TRUE(res.StandardSuccess()); } TEST_F(OatDumpTest, TestExportDexStatic) { + TEST_DISABLED_FOR_ARM_AND_MIPS(); TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); std::string error_msg; - ASSERT_TRUE(Exec(kStatic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly, &error_msg)) - << error_msg; + ASSERT_TRUE(Exec(kStatic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly)); } -#endif + } // namespace art diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h index b85730d25ecbfe7b7414678e1b417cefc160f8ab..2c28f06b2e959ac6cf5e47cda1c0db32d326f9f1 100644 --- a/oatdump/oatdump_test.h +++ b/oatdump/oatdump_test.h @@ -111,9 +111,8 @@ class OatDumpTest : public CommonRuntimeTest { return tmp_dir_ + "/" + GetAppBaseName() + ".odex"; } - bool GenerateAppOdexFile(Flavor flavor, - const std::vector& args, - /*out*/ std::string* error_msg) { + ::testing::AssertionResult GenerateAppOdexFile(Flavor flavor, + const std::vector& args) { std::string dex2oat_path = GetExecutableFilePath(flavor, "dex2oat"); std::vector exec_argv = { dex2oat_path, @@ -131,18 +130,32 @@ class OatDumpTest : public CommonRuntimeTest { }; exec_argv.insert(exec_argv.end(), args.begin(), args.end()); - return ForkAndExecAndWait(exec_argv, error_msg); + auto post_fork_fn = []() { + setpgid(0, 0); // Change process groups, so we don't get reaped by ProcessManager. + // Ignore setpgid errors. + return setenv("ANDROID_LOG_TAGS", "*:e", 1) == 0; // We're only interested in errors and + // fatal logs. + }; + + std::string error_msg; + ForkAndExecResult res = ForkAndExec(exec_argv, post_fork_fn, &error_msg); + if (res.stage != ForkAndExecResult::kFinished) { + return ::testing::AssertionFailure() << strerror(errno); + } + return res.StandardSuccess() ? ::testing::AssertionSuccess() + : (::testing::AssertionFailure() << error_msg); } // Run the test with custom arguments. - bool Exec(Flavor flavor, - Mode mode, - const std::vector& args, - Display display, - /*out*/ std::string* error_msg) { + ::testing::AssertionResult Exec(Flavor flavor, + Mode mode, + const std::vector& args, + Display display) { std::string file_path = GetExecutableFilePath(flavor, "oatdump"); - EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path"; + if (!OS::FileExists(file_path.c_str())) { + return ::testing::AssertionFailure() << file_path << " should be a valid file path"; + } // ScratchFile scratch; std::vector exec_argv = { file_path }; @@ -151,10 +164,6 @@ class OatDumpTest : public CommonRuntimeTest { exec_argv.push_back("--symbolize=" + core_oat_location_); exec_argv.push_back("--output=" + core_oat_location_ + ".symbolize"); } else { - expected_prefixes.push_back("Dex file data for"); - expected_prefixes.push_back("Num string ids:"); - expected_prefixes.push_back("Num field ids:"); - expected_prefixes.push_back("Num method ids:"); expected_prefixes.push_back("LOCATION:"); expected_prefixes.push_back("MAGIC:"); expected_prefixes.push_back("DEX FILE COUNT:"); @@ -162,8 +171,7 @@ class OatDumpTest : public CommonRuntimeTest { // Code and dex code do not show up if list only. expected_prefixes.push_back("DEX CODE:"); expected_prefixes.push_back("CODE:"); - expected_prefixes.push_back("CodeInfoEncoding"); - expected_prefixes.push_back("CodeInfoInlineInfo"); + expected_prefixes.push_back("InlineInfos"); } if (mode == kModeArt) { exec_argv.push_back("--image=" + core_art_location_); @@ -184,129 +192,130 @@ class OatDumpTest : public CommonRuntimeTest { } exec_argv.insert(exec_argv.end(), args.begin(), args.end()); - pid_t pid; - int pipe_fd; - bool result = ForkAndExec(exec_argv, &pid, &pipe_fd, error_msg); - if (result) { - static const size_t kLineMax = 256; - char line[kLineMax] = {}; - size_t line_len = 0; - size_t total = 0; - std::vector found(expected_prefixes.size(), false); - while (true) { - while (true) { + std::vector found(expected_prefixes.size(), false); + auto line_handle_fn = [&found, &expected_prefixes](const char* line, size_t line_len) { + if (line_len == 0) { + return; + } + // Check contents. + for (size_t i = 0; i < expected_prefixes.size(); ++i) { + const std::string& expected = expected_prefixes[i]; + if (!found[i] && + line_len >= expected.length() && + memcmp(line, expected.c_str(), expected.length()) == 0) { + found[i] = true; + } + } + }; + + static constexpr size_t kLineMax = 256; + char line[kLineMax] = {}; + size_t line_len = 0; + size_t total = 0; + bool ignore_next_line = false; + std::vector error_buf; // Buffer for debug output on error. Limited to 1M. + auto line_buf_fn = [&](char* buf, size_t len) { + total += len; + + if (len == 0 && line_len > 0 && !ignore_next_line) { + // Everything done, handle leftovers. + line_handle_fn(line, line_len); + } + + if (len > 0) { + size_t pos = error_buf.size(); + if (pos < MB) { + error_buf.insert(error_buf.end(), buf, buf + len); + } + } + + while (len > 0) { + // Copy buf into the free tail of the line buffer, and move input buffer along. + size_t copy = std::min(kLineMax - line_len, len); + memcpy(&line[line_len], buf, copy); + buf += copy; + len -= copy; + + // Skip spaces up to len, return count of removed spaces. Declare a lambda for reuse. + auto trim_space = [&line](size_t len) { size_t spaces = 0; - // Trim spaces at the start of the line. - for (; spaces < line_len && isspace(line[spaces]); ++spaces) {} + for (; spaces < len && isspace(line[spaces]); ++spaces) {} if (spaces > 0) { - line_len -= spaces; - memmove(&line[0], &line[spaces], line_len); + memmove(&line[0], &line[spaces], len - spaces); } - ssize_t bytes_read = - TEMP_FAILURE_RETRY(read(pipe_fd, &line[line_len], kLineMax - line_len)); - if (bytes_read <= 0) { - break; - } - line_len += bytes_read; - total += bytes_read; - } + return spaces; + }; + // There can only be spaces if we freshly started a line. if (line_len == 0) { - break; + copy -= trim_space(copy); } - // Check contents. - for (size_t i = 0; i < expected_prefixes.size(); ++i) { - const std::string& expected = expected_prefixes[i]; - if (!found[i] && - line_len >= expected.length() && - memcmp(line, expected.c_str(), expected.length()) == 0) { - found[i] = true; + + // Scan for newline characters. + size_t index = line_len; + line_len += copy; + while (index < line_len) { + if (line[index] == '\n') { + // Handle line. + if (!ignore_next_line) { + line_handle_fn(line, index); + } + // Move the rest to the front, but trim leading spaces. + line_len -= index + 1; + memmove(&line[0], &line[index + 1], line_len); + line_len -= trim_space(line_len); + index = 0; + ignore_next_line = false; + } else { + index++; } } - // Skip to next line. - size_t next_line = 0; - for (; next_line + 1 < line_len && line[next_line] != '\n'; ++next_line) {} - line_len -= next_line + 1; - memmove(&line[0], &line[next_line + 1], line_len); - } - if (mode == kModeSymbolize) { - EXPECT_EQ(total, 0u); - } else { - EXPECT_GT(total, 0u); - } - LOG(INFO) << "Processed bytes " << total; - close(pipe_fd); - int status = 0; - if (waitpid(pid, &status, 0) != -1) { - result = (status == 0); - } - for (size_t i = 0; i < expected_prefixes.size(); ++i) { - if (!found[i]) { - LOG(ERROR) << "Did not find prefix " << expected_prefixes[i]; - result = false; + // Handle a full line without newline characters. Ignore the "next" line, as it is the + // tail end of this. + if (line_len == kLineMax) { + if (!ignore_next_line) { + line_handle_fn(line, kLineMax); + } + line_len = 0; + ignore_next_line = true; } } - } + }; - return result; - } + auto post_fork_fn = []() { + setpgid(0, 0); // Change process groups, so we don't get reaped by ProcessManager. + return true; // Ignore setpgid failures. + }; - bool ForkAndExec(const std::vector& exec_argv, - /*out*/ pid_t* pid, - /*out*/ int* pipe_fd, - /*out*/ std::string* error_msg) { - int link[2]; - if (pipe(link) == -1) { - *error_msg = strerror(errno); - return false; + ForkAndExecResult res = ForkAndExec(exec_argv, post_fork_fn, line_buf_fn); + if (res.stage != ForkAndExecResult::kFinished) { + return ::testing::AssertionFailure() << strerror(errno); } - - *pid = fork(); - if (*pid == -1) { - *error_msg = strerror(errno); - close(link[0]); - close(link[1]); - return false; + if (!res.StandardSuccess()) { + return ::testing::AssertionFailure() << "Did not terminate successfully: " << res.status_code; } - if (*pid == 0) { - dup2(link[1], STDOUT_FILENO); - close(link[0]); - close(link[1]); - // change process groups, so we don't get reaped by ProcessManager - setpgid(0, 0); - // Use execv here rather than art::Exec to avoid blocking on waitpid here. - std::vector argv; - for (size_t i = 0; i < exec_argv.size(); ++i) { - argv.push_back(const_cast(exec_argv[i].c_str())); - } - argv.push_back(nullptr); - UNUSED(execv(argv[0], &argv[0])); - const std::string command_line(android::base::Join(exec_argv, ' ')); - PLOG(ERROR) << "Failed to execv(" << command_line << ")"; - // _exit to avoid atexit handlers in child. - _exit(1); - UNREACHABLE(); + if (mode == kModeSymbolize) { + EXPECT_EQ(total, 0u); } else { - close(link[1]); - *pipe_fd = link[0]; - return true; + EXPECT_GT(total, 0u); } - } - bool ForkAndExecAndWait(const std::vector& exec_argv, - /*out*/ std::string* error_msg) { - pid_t pid; - int pipe_fd; - bool result = ForkAndExec(exec_argv, &pid, &pipe_fd, error_msg); - if (result) { - close(pipe_fd); - int status = 0; - if (waitpid(pid, &status, 0) != -1) { - result = (status == 0); + bool result = true; + std::ostringstream oss; + for (size_t i = 0; i < expected_prefixes.size(); ++i) { + if (!found[i]) { + oss << "Did not find prefix " << expected_prefixes[i] << std::endl; + result = false; } } - return result; + if (!result) { + oss << "Processed bytes " << total << ":" << std::endl; + error_buf.push_back(0); // Make data a C string. + } + + return result ? ::testing::AssertionSuccess() + : (::testing::AssertionFailure() << oss.str() << error_buf.data()); } std::string tmp_dir_; diff --git a/openjdkjvm/Android.bp b/openjdkjvm/Android.bp index a17899358cb5330a3ae1b96089fa5690a5454313..907315e4e0039bc83fea5ba4f6f36e91090bfcb4 100644 --- a/openjdkjvm/Android.bp +++ b/openjdkjvm/Android.bp @@ -29,7 +29,10 @@ cc_defaults { art_cc_library { name: "libopenjdkjvm", defaults: ["libopenjdkjvm_defaults"], - shared_libs: ["libart"], + shared_libs: [ + "libart", + "libartbase", + ], } art_cc_library { @@ -38,5 +41,8 @@ art_cc_library { "art_debug_defaults", "libopenjdkjvm_defaults", ], - shared_libs: ["libartd"], + shared_libs: [ + "libartd", + "libartbased", + ], } diff --git a/openjdkjvm/OpenjdkJvm.cc b/openjdkjvm/OpenjdkJvm.cc index 975d1948fe97ca0e3f07617a075f476e2e112774..765225ae95ae39186e40910c5ad311beb210792d 100644 --- a/openjdkjvm/OpenjdkJvm.cc +++ b/openjdkjvm/OpenjdkJvm.cc @@ -48,8 +48,8 @@ #include "common_throws.h" #include "gc/heap.h" #include "handle_scope-inl.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "mirror/string-inl.h" #include "monitor.h" @@ -401,7 +401,7 @@ JNIEXPORT jboolean JVM_HoldsLock(JNIEnv* env, jclass unused ATTRIBUTE_UNUSED, jo art::ThrowNullPointerException("object == null"); return JNI_FALSE; } - return soa.Self()->HoldsLock(object.Ptr()); + return soa.Self()->HoldsLock(object); } JNIEXPORT void JVM_SetNativeThreadName(JNIEnv* env, jobject jthread, jstring java_name) { diff --git a/openjdkjvmti/Android.bp b/openjdkjvmti/Android.bp index 81b69e8c95ea92605a62dd4aa8534060be8c9158..d8902d60d1490e5ba2c3b1a05dfb5afa0c9db239 100644 --- a/openjdkjvmti/Android.bp +++ b/openjdkjvmti/Android.bp @@ -71,6 +71,7 @@ art_cc_library { "libart-compiler", "libart-dexlayout", "libdexfile", + "libartbase", ], } @@ -85,5 +86,6 @@ art_cc_library { "libartd-compiler", "libartd-dexlayout", "libdexfiled", + "libartbased", ], } diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc index ef5151990ca2b05fd5d19db8c70ee8135c3e64ba..59f61e2ee4c31862eb6af26e8af0ddd1387501e7 100644 --- a/openjdkjvmti/OpenjdkJvmTi.cc +++ b/openjdkjvmti/OpenjdkJvmTi.cc @@ -43,7 +43,7 @@ #include "base/logging.h" // For gLogVerbosity. #include "base/mutex.h" #include "events-inl.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "obj_ptr-inl.h" #include "object_tagging.h" #include "runtime.h" diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h index 73cc601e3eebf250cca9c0f9ede0ea6585bf03d5..82f3866c65f83b4ce24241abf9a67c3b10874cca 100644 --- a/openjdkjvmti/art_jvmti.h +++ b/openjdkjvmti/art_jvmti.h @@ -47,8 +47,8 @@ #include "base/strlcpy.h" #include "base/mutex.h" #include "events.h" -#include "java_vm_ext.h" -#include "jni_env_ext.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_env_ext.h" #include "jvmti.h" #include "ti_breakpoint.h" diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc index d4e5df1d67015cc664ba2c6bc98c3babb734f1d2..b444cc789f1dbca9a47b89220a77c16ac9be2e8a 100644 --- a/openjdkjvmti/deopt_manager.cc +++ b/openjdkjvmti/deopt_manager.cc @@ -40,8 +40,10 @@ #include "dex/dex_file_annotations.h" #include "dex/modifiers.h" #include "events-inl.h" +#include "intrinsics_list.h" #include "jit/jit.h" -#include "jni_internal.h" +#include "jit/jit_code_cache.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object_array-inl.h" #include "nativehelper/scoped_local_ref.h" @@ -84,11 +86,13 @@ DeoptManager::DeoptManager() deoptimization_condition_("JVMTI_DeoptimizationCondition", deoptimization_status_lock_), performing_deoptimization_(false), global_deopt_count_(0), + global_interpreter_deopt_count_(0), deopter_count_(0), breakpoint_status_lock_("JVMTI_BreakpointStatusLock", static_cast(art::LockLevel::kAbortLock + 1)), inspection_callback_(this), - set_local_variable_called_(false) { } + set_local_variable_called_(false), + already_disabled_intrinsics_(false) { } void DeoptManager::Setup() { art::ScopedThreadStateChange stsc(art::Thread::Current(), @@ -159,18 +163,18 @@ bool DeoptManager::MethodHasBreakpointsLocked(art::ArtMethod* method) { return elem != breakpoint_status_.end() && elem->second != 0; } -void DeoptManager::RemoveDeoptimizeAllMethods() { +void DeoptManager::RemoveDeoptimizeAllMethods(FullDeoptRequirement req) { art::Thread* self = art::Thread::Current(); art::ScopedThreadSuspension sts(self, art::kSuspended); deoptimization_status_lock_.ExclusiveLock(self); - RemoveDeoptimizeAllMethodsLocked(self); + RemoveDeoptimizeAllMethodsLocked(self, req); } -void DeoptManager::AddDeoptimizeAllMethods() { +void DeoptManager::AddDeoptimizeAllMethods(FullDeoptRequirement req) { art::Thread* self = art::Thread::Current(); art::ScopedThreadSuspension sts(self, art::kSuspended); deoptimization_status_lock_.ExclusiveLock(self); - AddDeoptimizeAllMethodsLocked(self); + AddDeoptimizeAllMethodsLocked(self, req); } void DeoptManager::AddMethodBreakpoint(art::ArtMethod* method) { @@ -207,7 +211,7 @@ void DeoptManager::AddMethodBreakpoint(art::ArtMethod* method) { deoptimization_status_lock_.ExclusiveUnlock(self); return; } else if (is_default) { - AddDeoptimizeAllMethodsLocked(self); + AddDeoptimizeAllMethodsLocked(self, FullDeoptRequirement::kInterpreter); } else { PerformLimitedDeoptimization(self, method); } @@ -244,7 +248,7 @@ void DeoptManager::RemoveMethodBreakpoint(art::ArtMethod* method) { return; } else if (is_last_breakpoint) { if (UNLIKELY(is_default)) { - RemoveDeoptimizeAllMethodsLocked(self); + RemoveDeoptimizeAllMethodsLocked(self, FullDeoptRequirement::kInterpreter); } else { PerformLimitedUndeoptimization(self, method); } @@ -272,13 +276,22 @@ class ScopedDeoptimizationContext : public art::ValueObject { RELEASE(deopt->deoptimization_status_lock_) ACQUIRE(art::Locks::mutator_lock_) ACQUIRE(art::Roles::uninterruptible_) - : self_(self), deopt_(deopt), uninterruptible_cause_(nullptr) { + : self_(self), + deopt_(deopt), + uninterruptible_cause_(nullptr), + jit_(art::Runtime::Current()->GetJit()) { deopt_->WaitForDeoptimizationToFinishLocked(self_); DCHECK(!deopt->performing_deoptimization_) << "Already performing deoptimization on another thread!"; // Use performing_deoptimization_ to keep track of the lock. deopt_->performing_deoptimization_ = true; deopt_->deoptimization_status_lock_.Unlock(self_); + // Stop the jit. We might need to disable all intrinsics which needs the jit disabled and this + // is the only place we can do that. Since this isn't expected to be entered too often it should + // be fine to always stop it. + if (jit_ != nullptr) { + jit_->Stop(); + } art::Runtime::Current()->GetThreadList()->SuspendAll("JMVTI Deoptimizing methods", /*long_suspend*/ false); uninterruptible_cause_ = self_->StartAssertNoThreadSuspension("JVMTI deoptimizing methods"); @@ -291,6 +304,10 @@ class ScopedDeoptimizationContext : public art::ValueObject { self_->EndAssertNoThreadSuspension(uninterruptible_cause_); // Release the mutator lock. art::Runtime::Current()->GetThreadList()->ResumeAll(); + // Let the jit start again. + if (jit_ != nullptr) { + jit_->Start(); + } // Let other threads know it's fine to proceed. art::MutexLock lk(self_, deopt_->deoptimization_status_lock_); deopt_->performing_deoptimization_ = false; @@ -301,22 +318,44 @@ class ScopedDeoptimizationContext : public art::ValueObject { art::Thread* self_; DeoptManager* deopt_; const char* uninterruptible_cause_; + art::jit::Jit* jit_; }; -void DeoptManager::AddDeoptimizeAllMethodsLocked(art::Thread* self) { +void DeoptManager::AddDeoptimizeAllMethodsLocked(art::Thread* self, FullDeoptRequirement req) { + DCHECK_GE(global_deopt_count_, global_interpreter_deopt_count_); global_deopt_count_++; + if (req == FullDeoptRequirement::kInterpreter) { + global_interpreter_deopt_count_++; + } if (global_deopt_count_ == 1) { - PerformGlobalDeoptimization(self); + PerformGlobalDeoptimization(self, + /*needs_interpreter*/ global_interpreter_deopt_count_ > 0, + /*disable_intrinsics*/ global_interpreter_deopt_count_ == 0); + } else if (req == FullDeoptRequirement::kInterpreter && global_interpreter_deopt_count_ == 1) { + // First kInterpreter request. + PerformGlobalDeoptimization(self, + /*needs_interpreter*/true, + /*disable_intrinsics*/false); } else { WaitForDeoptimizationToFinish(self); } } -void DeoptManager::RemoveDeoptimizeAllMethodsLocked(art::Thread* self) { +void DeoptManager::RemoveDeoptimizeAllMethodsLocked(art::Thread* self, FullDeoptRequirement req) { DCHECK_GT(global_deopt_count_, 0u) << "Request to remove non-existent global deoptimization!"; + DCHECK_GE(global_deopt_count_, global_interpreter_deopt_count_); global_deopt_count_--; + if (req == FullDeoptRequirement::kInterpreter) { + global_interpreter_deopt_count_--; + } if (global_deopt_count_ == 0) { - PerformGlobalUndeoptimization(self); + PerformGlobalUndeoptimization(self, + /*still_needs_stubs*/ false, + /*disable_intrinsics*/ false); + } else if (req == FullDeoptRequirement::kInterpreter && global_interpreter_deopt_count_ == 0) { + PerformGlobalUndeoptimization(self, + /*still_needs_stubs*/ global_deopt_count_ > 0, + /*disable_intrinsics*/ global_deopt_count_ > 0); } else { WaitForDeoptimizationToFinish(self); } @@ -332,18 +371,85 @@ void DeoptManager::PerformLimitedUndeoptimization(art::Thread* self, art::ArtMet art::Runtime::Current()->GetInstrumentation()->Undeoptimize(method); } -void DeoptManager::PerformGlobalDeoptimization(art::Thread* self) { +void DeoptManager::PerformGlobalDeoptimization(art::Thread* self, + bool needs_interpreter, + bool disable_intrinsics) { ScopedDeoptimizationContext sdc(self, this); - art::Runtime::Current()->GetInstrumentation()->DeoptimizeEverything( - kDeoptManagerInstrumentationKey); + art::Runtime::Current()->GetInstrumentation()->EnableMethodTracing( + kDeoptManagerInstrumentationKey, needs_interpreter); + MaybeDisableIntrinsics(disable_intrinsics); } -void DeoptManager::PerformGlobalUndeoptimization(art::Thread* self) { +void DeoptManager::PerformGlobalUndeoptimization(art::Thread* self, + bool still_needs_stubs, + bool disable_intrinsics) { ScopedDeoptimizationContext sdc(self, this); - art::Runtime::Current()->GetInstrumentation()->UndeoptimizeEverything( - kDeoptManagerInstrumentationKey); + if (still_needs_stubs) { + art::Runtime::Current()->GetInstrumentation()->EnableMethodTracing( + kDeoptManagerInstrumentationKey, /*needs_interpreter*/false); + MaybeDisableIntrinsics(disable_intrinsics); + } else { + art::Runtime::Current()->GetInstrumentation()->DisableMethodTracing( + kDeoptManagerInstrumentationKey); + // We shouldn't care about intrinsics if we don't need tracing anymore. + DCHECK(!disable_intrinsics); + } +} + +static void DisableSingleIntrinsic(const char* class_name, + const char* method_name, + const char* signature) + REQUIRES(art::Locks::mutator_lock_, art::Roles::uninterruptible_) { + // Since these intrinsics are all loaded during runtime startup this cannot fail and will not + // suspend. + art::Thread* self = art::Thread::Current(); + art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker(); + art::ObjPtr cls = class_linker->FindSystemClass(self, class_name); + + if (cls == nullptr) { + LOG(FATAL) << "Could not find class of intrinsic " + << class_name << "->" << method_name << signature; + } + + art::ArtMethod* method = cls->FindClassMethod(method_name, signature, art::kRuntimePointerSize); + if (method == nullptr || method->GetDeclaringClass() != cls) { + LOG(FATAL) << "Could not find method of intrinsic " + << class_name << "->" << method_name << signature; + } + + if (LIKELY(method->IsIntrinsic())) { + method->SetNotIntrinsic(); + } else { + LOG(WARNING) << method->PrettyMethod() << " was already marked as non-intrinsic!"; + } } +void DeoptManager::MaybeDisableIntrinsics(bool do_disable) { + if (!do_disable || already_disabled_intrinsics_) { + // Don't toggle intrinsics on and off. It will lead to too much purging of the jit and would + // require us to keep around the intrinsics status of all methods. + return; + } + already_disabled_intrinsics_ = true; + // First just mark all intrinsic methods as no longer intrinsics. +#define DISABLE_INTRINSIC(_1, _2, _3, _4, _5, decl_class_name, meth_name, meth_desc) \ + DisableSingleIntrinsic(decl_class_name, meth_name, meth_desc); + INTRINSICS_LIST(DISABLE_INTRINSIC); +#undef DISABLE_INTRINSIC + // Next tell the jit to throw away all of its code (since there might be intrinsic code in them. + // TODO it would be nice to be more selective. + art::jit::Jit* jit = art::Runtime::Current()->GetJit(); + if (jit != nullptr) { + jit->GetCodeCache()->ClearAllCompiledDexCode(); + } + art::MutexLock mu(art::Thread::Current(), *art::Locks::thread_list_lock_); + // Now make all threads go to interpreter. + art::Runtime::Current()->GetThreadList()->ForEach( + [](art::Thread* thr, void* ctx) REQUIRES(art::Locks::mutator_lock_) { + reinterpret_cast(ctx)->DeoptimizeThread(thr); + }, + this); +} void DeoptManager::RemoveDeoptimizationRequester() { art::Thread* self = art::Thread::Current(); diff --git a/openjdkjvmti/deopt_manager.h b/openjdkjvmti/deopt_manager.h index 6e991dee3d7bf08aefc6a4197c3afaf360090c73..1a13c0ff152a649a69f9f8da602daaf709a0c890 100644 --- a/openjdkjvmti/deopt_manager.h +++ b/openjdkjvmti/deopt_manager.h @@ -51,6 +51,11 @@ class Class; namespace openjdkjvmti { +enum class FullDeoptRequirement { + kStubs, + kInterpreter, +}; + class DeoptManager; struct JvmtiMethodInspectionCallback : public art::MethodInspectionCallback { @@ -94,11 +99,11 @@ class DeoptManager { REQUIRES(!deoptimization_status_lock_, !art::Roles::uninterruptible_) REQUIRES_SHARED(art::Locks::mutator_lock_); - void AddDeoptimizeAllMethods() + void AddDeoptimizeAllMethods(FullDeoptRequirement requirement) REQUIRES(!deoptimization_status_lock_, !art::Roles::uninterruptible_) REQUIRES_SHARED(art::Locks::mutator_lock_); - void RemoveDeoptimizeAllMethods() + void RemoveDeoptimizeAllMethods(FullDeoptRequirement requirement) REQUIRES(!deoptimization_status_lock_, !art::Roles::uninterruptible_) REQUIRES_SHARED(art::Locks::mutator_lock_); @@ -132,19 +137,23 @@ class DeoptManager { void WaitForDeoptimizationToFinishLocked(art::Thread* self) REQUIRES(deoptimization_status_lock_, !art::Locks::mutator_lock_); - void AddDeoptimizeAllMethodsLocked(art::Thread* self) + void AddDeoptimizeAllMethodsLocked(art::Thread* self, FullDeoptRequirement req) RELEASE(deoptimization_status_lock_) REQUIRES(!art::Roles::uninterruptible_, !art::Locks::mutator_lock_); - void RemoveDeoptimizeAllMethodsLocked(art::Thread* self) + void RemoveDeoptimizeAllMethodsLocked(art::Thread* self, FullDeoptRequirement req) RELEASE(deoptimization_status_lock_) REQUIRES(!art::Roles::uninterruptible_, !art::Locks::mutator_lock_); - void PerformGlobalDeoptimization(art::Thread* self) + void PerformGlobalDeoptimization(art::Thread* self, + bool needs_interpreter, + bool disable_intrinsics) RELEASE(deoptimization_status_lock_) REQUIRES(!art::Roles::uninterruptible_, !art::Locks::mutator_lock_); - void PerformGlobalUndeoptimization(art::Thread* self) + void PerformGlobalUndeoptimization(art::Thread* self, + bool still_needs_stubs, + bool disable_intrinsics) RELEASE(deoptimization_status_lock_) REQUIRES(!art::Roles::uninterruptible_, !art::Locks::mutator_lock_); @@ -156,15 +165,25 @@ class DeoptManager { RELEASE(deoptimization_status_lock_) REQUIRES(!art::Roles::uninterruptible_, !art::Locks::mutator_lock_); + // Disables intrinsics and purges the jit code cache if needed. + void MaybeDisableIntrinsics(bool do_disable) + REQUIRES(art::Locks::mutator_lock_, + !deoptimization_status_lock_, + art::Roles::uninterruptible_); + static constexpr const char* kDeoptManagerInstrumentationKey = "JVMTI_DeoptManager"; art::Mutex deoptimization_status_lock_ ACQUIRED_BEFORE(art::Locks::classlinker_classes_lock_); art::ConditionVariable deoptimization_condition_ GUARDED_BY(deoptimization_status_lock_); bool performing_deoptimization_ GUARDED_BY(deoptimization_status_lock_); - // Number of times we have gotten requests to deopt everything. + // Number of times we have gotten requests to deopt everything both requiring and not requiring + // interpreter. uint32_t global_deopt_count_ GUARDED_BY(deoptimization_status_lock_); + // Number of deopt-everything requests that require interpreter. + uint32_t global_interpreter_deopt_count_ GUARDED_BY(deoptimization_status_lock_); + // Number of users of deoptimization there currently are. uint32_t deopter_count_ GUARDED_BY(deoptimization_status_lock_); @@ -182,6 +201,10 @@ class DeoptManager { // OSR after this. std::atomic set_local_variable_called_; + // If we have already disabled intrinsics. Since doing this throws out all JIT code we really will + // only ever do it once and never undo it. + bool already_disabled_intrinsics_ GUARDED_BY(art::Locks::mutator_lock_); + // Helper for setting up/tearing-down for deoptimization. friend class ScopedDeoptimizationContext; }; diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h index 74ffb845798c7a2e70c3d13a6d9fc45217c8c185..6a8ba4810971433c7701b74299871951e2d50595 100644 --- a/openjdkjvmti/events-inl.h +++ b/openjdkjvmti/events-inl.h @@ -23,7 +23,7 @@ #include "base/mutex-inl.h" #include "events.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "nativehelper/scoped_local_ref.h" #include "scoped_thread_state_change-inl.h" #include "ti_breakpoint.h" diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index 4d055d4c270d181c760b3367b0280c65365cfd2e..10a49239352a3c18babdf46cb8f421d43ec8d0de 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -44,8 +44,8 @@ #include "gc/scoped_gc_critical_section.h" #include "handle_scope-inl.h" #include "instrumentation.h" -#include "jni_env_ext-inl.h" -#include "jni_internal.h" +#include "jni/jni_env_ext-inl.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/object-inl.h" #include "monitor.h" @@ -504,8 +504,9 @@ class JvmtiMethodTraceListener FINAL : public art::instrumentation::Instrumentat REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE { if (!method->IsRuntimeMethod() && event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kMethodExit)) { - DCHECK_EQ(method->GetReturnTypePrimitive(), art::Primitive::kPrimNot) - << method->PrettyMethod(); + DCHECK_EQ( + method->GetInterfaceMethodIfProxy(art::kRuntimePointerSize)->GetReturnTypePrimitive(), + art::Primitive::kPrimNot) << method->PrettyMethod(); DCHECK(!self->IsExceptionPending()); jvalue val; art::JNIEnvExt* jnienv = self->GetJniEnv(); @@ -530,8 +531,9 @@ class JvmtiMethodTraceListener FINAL : public art::instrumentation::Instrumentat REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE { if (!method->IsRuntimeMethod() && event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kMethodExit)) { - DCHECK_NE(method->GetReturnTypePrimitive(), art::Primitive::kPrimNot) - << method->PrettyMethod(); + DCHECK_NE( + method->GetInterfaceMethodIfProxy(art::kRuntimePointerSize)->GetReturnTypePrimitive(), + art::Primitive::kPrimNot) << method->PrettyMethod(); DCHECK(!self->IsExceptionPending()); jvalue val; art::JNIEnvExt* jnienv = self->GetJniEnv(); @@ -886,16 +888,29 @@ static bool EventNeedsFullDeopt(ArtJvmtiEvent event) { case ArtJvmtiEvent::kBreakpoint: case ArtJvmtiEvent::kException: return false; - // TODO We should support more of these or at least do something to make them discriminate by - // thread. + default: + return true; + } +} + +static FullDeoptRequirement GetFullDeoptRequirement(ArtJvmtiEvent event) { + switch (event) { + // TODO We should support more of these as Limited or at least do something to make them + // discriminate by thread. case ArtJvmtiEvent::kMethodEntry: - case ArtJvmtiEvent::kExceptionCatch: case ArtJvmtiEvent::kMethodExit: + // We only need MethodEntered and MethodExited for these so we can use Stubs. We will need to + // disable intrinsics. + // TODO Offer a version of this without disabling intrinsics. + return FullDeoptRequirement::kStubs; + case ArtJvmtiEvent::kExceptionCatch: case ArtJvmtiEvent::kFieldModification: case ArtJvmtiEvent::kFieldAccess: case ArtJvmtiEvent::kSingleStep: + // NB If we ever make this runnable using stubs or some other method we will need to be careful + // that it doesn't require disabling intrinsics. case ArtJvmtiEvent::kFramePop: - return true; + return FullDeoptRequirement::kInterpreter; default: LOG(FATAL) << "Unexpected event type!"; UNREACHABLE(); @@ -905,19 +920,18 @@ static bool EventNeedsFullDeopt(ArtJvmtiEvent event) { void EventHandler::SetupTraceListener(JvmtiMethodTraceListener* listener, ArtJvmtiEvent event, bool enable) { - bool needs_full_deopt = EventNeedsFullDeopt(event); // Make sure we can deopt. { art::ScopedObjectAccess soa(art::Thread::Current()); DeoptManager* deopt_manager = DeoptManager::Get(); if (enable) { deopt_manager->AddDeoptimizationRequester(); - if (needs_full_deopt) { - deopt_manager->AddDeoptimizeAllMethods(); + if (EventNeedsFullDeopt(event)) { + deopt_manager->AddDeoptimizeAllMethods(GetFullDeoptRequirement(event)); } } else { - if (needs_full_deopt) { - deopt_manager->RemoveDeoptimizeAllMethods(); + if (EventNeedsFullDeopt(event)) { + deopt_manager->RemoveDeoptimizeAllMethods(GetFullDeoptRequirement(event)); } deopt_manager->RemoveDeoptimizationRequester(); } @@ -1001,6 +1015,27 @@ bool EventHandler::OtherMonitorEventsEnabledAnywhere(ArtJvmtiEvent event) { return false; } +void EventHandler::SetupFramePopTraceListener(bool enable) { + if (enable) { + frame_pop_enabled = true; + SetupTraceListener(method_trace_listener_.get(), ArtJvmtiEvent::kFramePop, enable); + } else { + // remove the listener if we have no outstanding frames. + { + art::ReaderMutexLock mu(art::Thread::Current(), envs_lock_); + for (ArtJvmTiEnv* env : envs) { + art::ReaderMutexLock event_mu(art::Thread::Current(), env->event_info_mutex_); + if (!env->notify_frames.empty()) { + // Leaving FramePop listener since there are unsent FramePop events. + return; + } + } + frame_pop_enabled = false; + } + SetupTraceListener(method_trace_listener_.get(), ArtJvmtiEvent::kFramePop, enable); + } +} + // Handle special work for the given event type, if necessary. void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { switch (event) { @@ -1015,14 +1050,14 @@ void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { case ArtJvmtiEvent::kGarbageCollectionFinish: SetupGcPauseTracking(gc_pause_listener_.get(), event, enable); return; - // FramePop can never be disabled once it's been turned on since we would either need to deal - // with dangling pointers or have missed events. - // TODO We really need to make this not the case anymore. + // FramePop can never be disabled once it's been turned on if it was turned off with outstanding + // pop-events since we would either need to deal with dangling pointers or have missed events. case ArtJvmtiEvent::kFramePop: - if (!enable || (enable && frame_pop_enabled)) { + if (enable && frame_pop_enabled) { + // The frame-pop event was held on by pending events so we don't need to do anything. break; } else { - SetupTraceListener(method_trace_listener_.get(), event, enable); + SetupFramePopTraceListener(enable); break; } case ArtJvmtiEvent::kMethodEntry: diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h index 8141eff88c01d2df8f6a3593c8998e71a6ee86b9..bf12cb191e4522834ed9f97c3017505cd07f3bd2 100644 --- a/openjdkjvmti/events.h +++ b/openjdkjvmti/events.h @@ -247,6 +247,9 @@ class EventHandler { private: void SetupTraceListener(JvmtiMethodTraceListener* listener, ArtJvmtiEvent event, bool enable); + // Specifically handle the FramePop event which it might not always be possible to turn off. + void SetupFramePopTraceListener(bool enable); + template ALWAYS_INLINE inline std::vector> CollectEvents(art::Thread* thread, diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc index fcbafe7e714364dfa8fac442b2cfef83f6127882..a660fb56c4cea89758868631b13878816cc76fce 100644 --- a/openjdkjvmti/fixed_up_dex_file.cc +++ b/openjdkjvmti/fixed_up_dex_file.cc @@ -31,6 +31,7 @@ #include "base/leb128.h" #include "fixed_up_dex_file.h" +#include "dex/class_accessor-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" #include "dex/dex_file_verifier.h" @@ -51,14 +52,12 @@ static void RecomputeDexChecksum(art::DexFile* dex_file) { } static void UnhideApis(const art::DexFile& target_dex_file) { - for (uint32_t i = 0; i < target_dex_file.NumClassDefs(); ++i) { - const uint8_t* class_data = target_dex_file.GetClassData(target_dex_file.GetClassDef(i)); - if (class_data != nullptr) { - for (art::ClassDataItemIterator class_it(target_dex_file, class_data); - class_it.HasNext(); - class_it.Next()) { - art::DexFile::UnHideAccessFlags(class_it); - } + for (art::ClassAccessor accessor : target_dex_file.GetClasses()) { + for (const art::ClassAccessor::Field& field : accessor.GetFields()) { + field.UnHideAccessFlags(); + } + for (const art::ClassAccessor::Method& method : accessor.GetMethods()) { + method.UnHideAccessFlags(); } } } diff --git a/openjdkjvmti/jvmti_weak_table-inl.h b/openjdkjvmti/jvmti_weak_table-inl.h index 699004298e5dbfc7da813951f2a5ab0b266a580c..d9b8a84e55a5ab8f13f8ad11fd511a7cf845130f 100644 --- a/openjdkjvmti/jvmti_weak_table-inl.h +++ b/openjdkjvmti/jvmti_weak_table-inl.h @@ -41,7 +41,7 @@ #include "art_jvmti.h" #include "gc/allocation_listener.h" #include "instrumentation.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "jvmti_allocator.h" #include "mirror/class.h" #include "mirror/object.h" diff --git a/openjdkjvmti/jvmti_weak_table.h b/openjdkjvmti/jvmti_weak_table.h index 5a821c964b5e685b630e2efc4f125596f2cdbeb4..cba8ef06a778caef33e3945911cb62c7b42e2295 100644 --- a/openjdkjvmti/jvmti_weak_table.h +++ b/openjdkjvmti/jvmti_weak_table.h @@ -34,11 +34,11 @@ #include +#include "base/globals.h" #include "base/macros.h" #include "base/mutex.h" #include "gc/system_weak.h" #include "gc_root-inl.h" -#include "globals.h" #include "jvmti.h" #include "jvmti_allocator.h" #include "mirror/object.h" diff --git a/openjdkjvmti/object_tagging.h b/openjdkjvmti/object_tagging.h index b47484556691bac3d42275e6341507cb5fba9dd2..1b8366a501658829777cfa6c37d573af801708a8 100644 --- a/openjdkjvmti/object_tagging.h +++ b/openjdkjvmti/object_tagging.h @@ -34,8 +34,8 @@ #include +#include "base/globals.h" #include "base/mutex.h" -#include "globals.h" #include "jvmti.h" #include "jvmti_weak_table.h" #include "mirror/object.h" diff --git a/openjdkjvmti/ti_breakpoint.cc b/openjdkjvmti/ti_breakpoint.cc index 136b1d3e1a83a0f9063a96c0807d0fa219638af3..813aa8eb838760fe271d02728cb15fcce7332bf3 100644 --- a/openjdkjvmti/ti_breakpoint.cc +++ b/openjdkjvmti/ti_breakpoint.cc @@ -41,7 +41,7 @@ #include "dex/dex_file_annotations.h" #include "dex/modifiers.h" #include "events-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object_array-inl.h" #include "nativehelper/scoped_local_ref.h" diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc index 261fe3e8105d1bdb749ee7bdbd080e010b86802a..9bea18a7639ca77c5921f5290056a105884d3c01 100644 --- a/openjdkjvmti/ti_class.cc +++ b/openjdkjvmti/ti_class.cc @@ -54,8 +54,8 @@ #include "gc/heap.h" #include "gc_root.h" #include "handle.h" -#include "jni_env_ext-inl.h" -#include "jni_internal.h" +#include "jni/jni_env_ext-inl.h" +#include "jni/jni_internal.h" #include "mirror/array-inl.h" #include "mirror/class-inl.h" #include "mirror/class_ext.h" @@ -63,7 +63,7 @@ #include "mirror/object-refvisitor-inl.h" #include "mirror/object_array-inl.h" #include "mirror/object_reference.h" -#include "mirror/reference.h" +#include "mirror/reference-inl.h" #include "nativehelper/scoped_local_ref.h" #include "reflection.h" #include "runtime.h" @@ -71,9 +71,11 @@ #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" #include "thread_list.h" +#include "ti_class_definition.h" #include "ti_class_loader-inl.h" #include "ti_phase.h" #include "ti_redefine.h" +#include "transform.h" #include "well_known_classes.h" namespace openjdkjvmti { @@ -713,7 +715,7 @@ jvmtiError ClassUtil::GetClassSignature(jvmtiEnv* env, if (!klass->IsProxyClass() && klass->GetDexCache() != nullptr) { art::StackHandleScope<1> hs(soa.Self()); art::Handle h_klass = hs.NewHandle(klass); - art::mirror::ObjectArray* str_array = + art::ObjPtr> str_array = art::annotations::GetSignatureAnnotationForClass(h_klass); if (str_array != nullptr) { std::ostringstream oss; diff --git a/openjdkjvmti/ti_class_definition.cc b/openjdkjvmti/ti_class_definition.cc index 1b641cd905fae7004089878bf2d1665b7e0c8c21..dce2733e7e6ec98dbf280eae2c7a20e733c42497 100644 --- a/openjdkjvmti/ti_class_definition.cc +++ b/openjdkjvmti/ti_class_definition.cc @@ -33,6 +33,7 @@ #include "base/array_slice.h" #include "class_linker-inl.h" +#include "class_root.h" #include "dex/dex_file.h" #include "fixed_up_dex_file.h" #include "handle.h" @@ -162,8 +163,7 @@ static void GetDexDataForRetransformation(art::Handle klass, << "Expected java/lang/Long but found object of type " << orig_dex->GetClass()->PrettyClass(); art::ObjPtr prim_long_class( - art::Runtime::Current()->GetClassLinker()->GetClassRoot( - art::ClassLinker::kPrimitiveLong)); + art::GetClassRoot(art::ClassRoot::kPrimitiveLong)); art::JValue val; if (!art::UnboxPrimitiveForResult(orig_dex.Get(), prim_long_class, &val)) { // This should never happen. @@ -226,8 +226,7 @@ static const art::DexFile* GetQuickenedDexFile(art::Handle k << "Expected java/lang/Long but found object of type " << orig_dex->GetClass()->PrettyClass(); art::ObjPtr prim_long_class( - art::Runtime::Current()->GetClassLinker()->GetClassRoot( - art::ClassLinker::kPrimitiveLong)); + art::GetClassRoot(art::ClassRoot::kPrimitiveLong)); art::JValue val; if (!art::UnboxPrimitiveForResult(orig_dex.Ptr(), prim_long_class, &val)) { LOG(FATAL) << "Unable to unwrap a long value!"; diff --git a/openjdkjvmti/ti_class_definition.h b/openjdkjvmti/ti_class_definition.h index 31c3611e727e4c83aa98f19d8d02c328af88507d..f888a7474f66f2de879bd32dfbb6bc56961a5278 100644 --- a/openjdkjvmti/ti_class_definition.h +++ b/openjdkjvmti/ti_class_definition.h @@ -39,7 +39,7 @@ #include "art_jvmti.h" #include "base/array_ref.h" -#include "mem_map.h" +#include "base/mem_map.h" namespace openjdkjvmti { diff --git a/openjdkjvmti/ti_class_loader-inl.h b/openjdkjvmti/ti_class_loader-inl.h index 95278f4b2d59db065accd28548fc5efa9bfcf3af..9b04841eb3e76f1370c86bd39a05cef14ac1907d 100644 --- a/openjdkjvmti/ti_class_loader-inl.h +++ b/openjdkjvmti/ti_class_loader-inl.h @@ -36,7 +36,7 @@ #include "art_field-inl.h" #include "handle.h" #include "handle_scope.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/object.h" #include "mirror/object_array-inl.h" #include "well_known_classes.h" diff --git a/openjdkjvmti/ti_class_loader.cc b/openjdkjvmti/ti_class_loader.cc index 3df5de909d29805309d4cd0a63fec445f4e8422d..9a32849ed09777b43518a08eee605b88603cf70b 100644 --- a/openjdkjvmti/ti_class_loader.cc +++ b/openjdkjvmti/ti_class_loader.cc @@ -46,7 +46,7 @@ #include "instrumentation.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "jvmti_allocator.h" #include "mirror/class.h" #include "mirror/class_ext.h" diff --git a/openjdkjvmti/ti_class_loader.h b/openjdkjvmti/ti_class_loader.h index 5c9497b0b59754fb553835de793010df113d7d6e..577c28585ebcd3b1f80e6378fe822e4c68316f57 100644 --- a/openjdkjvmti/ti_class_loader.h +++ b/openjdkjvmti/ti_class_loader.h @@ -36,32 +36,19 @@ #include -#include "art_jvmti.h" -#include "art_method.h" -#include "base/array_slice.h" -#include "class_linker.h" -#include "dex/dex_file.h" -#include "dex/utf.h" -#include "gc_root-inl.h" -#include "globals.h" -#include "jni_env_ext-inl.h" +#include "base/globals.h" +#include "base/mutex.h" #include "jvmti.h" -#include "linear_alloc.h" -#include "mem_map.h" -#include "mirror/array-inl.h" #include "mirror/array.h" -#include "mirror/class-inl.h" -#include "mirror/class.h" -#include "mirror/class_loader-inl.h" -#include "mirror/string-inl.h" -#include "oat_file.h" -#include "obj_ptr.h" -#include "scoped_thread_state_change-inl.h" -#include "stack.h" -#include "thread_list.h" -#include "ti_class_definition.h" -#include "transform.h" -#include "utils/dex_cache_arrays_layout-inl.h" + +namespace art { + +class DexFile; +template class Handle; +template class ObjPtr; +class Thread; + +} // namespace art namespace openjdkjvmti { diff --git a/openjdkjvmti/ti_ddms.cc b/openjdkjvmti/ti_ddms.cc index 0b4906d798f4395d6b80b31c419c0d551738e60e..bf063faf7b5a816b2aadedf477b868ce17ed8fba 100644 --- a/openjdkjvmti/ti_ddms.cc +++ b/openjdkjvmti/ti_ddms.cc @@ -60,7 +60,7 @@ jvmtiError DDMSUtil::HandleChunk(jvmtiEnv* env, *data_out = nullptr; art::Thread* self = art::Thread::Current(); - art::ScopedThreadStateChange(self, art::ThreadState::kNative); + art::ScopedThreadStateChange stcs(self, art::ThreadState::kNative); art::ArrayRef data_arr(data_in, length_in); std::vector out_data; diff --git a/openjdkjvmti/ti_field.cc b/openjdkjvmti/ti_field.cc index c016966d216bb410d8c84d6ad7a2a1dc90f7ab91..2a860d9f431e9a3688fcad3b2786845d103166ab 100644 --- a/openjdkjvmti/ti_field.cc +++ b/openjdkjvmti/ti_field.cc @@ -36,7 +36,7 @@ #include "base/enums.h" #include "dex/dex_file_annotations.h" #include "dex/modifiers.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/object_array-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" @@ -91,7 +91,7 @@ jvmtiError FieldUtil::GetFieldName(jvmtiEnv* env, if (generic_ptr != nullptr) { *generic_ptr = nullptr; if (!art_field->GetDeclaringClass()->IsProxyClass()) { - art::mirror::ObjectArray* str_array = + art::ObjPtr> str_array = art::annotations::GetSignatureAnnotationForField(art_field); if (str_array != nullptr) { std::ostringstream oss; diff --git a/openjdkjvmti/ti_heap.cc b/openjdkjvmti/ti_heap.cc index d0a7cf0657c70abe0c753bf5bdb92784257bbc03..d23370bc5c011ca2290794aa95179b5486ed69c1 100644 --- a/openjdkjvmti/ti_heap.cc +++ b/openjdkjvmti/ti_heap.cc @@ -26,8 +26,8 @@ #include "gc/heap.h" #include "gc_root-inl.h" #include "java_frame_root_info.h" -#include "jni_env_ext.h" -#include "jni_internal.h" +#include "jni/jni_env_ext.h" +#include "jni/jni_internal.h" #include "jvmti_weak_table-inl.h" #include "mirror/class.h" #include "mirror/object-inl.h" diff --git a/openjdkjvmti/ti_jni.cc b/openjdkjvmti/ti_jni.cc index dd2dda118af176c21e2c6f1f304315f2329c0ef0..b655d6a8e1ba390903289831e6bc6cbcabc6fe78 100644 --- a/openjdkjvmti/ti_jni.cc +++ b/openjdkjvmti/ti_jni.cc @@ -35,8 +35,8 @@ #include "art_jvmti.h" #include "base/mutex.h" -#include "java_vm_ext.h" -#include "jni_env_ext.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_env_ext.h" #include "runtime.h" #include "thread-current-inl.h" diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc index b83310dc8531551decefeb57d1bf1f23dc198db5..9f9dace4f783cddb33a49a2ca26c29adc6f1f464 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -44,7 +44,7 @@ #include "events-inl.h" #include "gc_root-inl.h" #include "jit/jit.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" @@ -345,7 +345,7 @@ jvmtiError MethodUtil::GetMethodName(jvmtiEnv* env, if (generic_ptr != nullptr) { *generic_ptr = nullptr; if (!art_method->GetDeclaringClass()->IsProxyClass()) { - art::mirror::ObjectArray* str_array = + art::ObjPtr> str_array = art::annotations::GetSignatureAnnotationForMethod(art_method); if (str_array != nullptr) { std::ostringstream oss; @@ -783,8 +783,6 @@ jvmtiError MethodUtil::GetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED, return ERR(ILLEGAL_ARGUMENT); } art::Thread* self = art::Thread::Current(); - // Suspend JIT since it can get confused if we deoptimize methods getting jitted. - art::jit::ScopedJitSuspend suspend_jit; art::ScopedObjectAccess soa(self); art::Locks::thread_list_lock_->ExclusiveLock(self); art::Thread* target = nullptr; @@ -919,8 +917,6 @@ jvmtiError MethodUtil::SetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED, // TODO We should really keep track of this at the Frame granularity. DeoptManager::Get()->SetLocalsUpdated(); art::Thread* self = art::Thread::Current(); - // Suspend JIT since it can get confused if we deoptimize methods getting jitted. - art::jit::ScopedJitSuspend suspend_jit; art::ScopedObjectAccess soa(self); art::Locks::thread_list_lock_->ExclusiveLock(self); art::Thread* target = nullptr; diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc index a23baa5095b37b148a1119f53e8d836602ff82ff..50d8dfeb7060f891aa76955f73eb5c25b5420e6b 100644 --- a/openjdkjvmti/ti_redefine.cc +++ b/openjdkjvmti/ti_redefine.cc @@ -42,6 +42,7 @@ #include "base/array_ref.h" #include "base/stringpiece.h" #include "class_linker-inl.h" +#include "class_root.h" #include "debugger.h" #include "dex/art_dex_file_loader.h" #include "dex/dex_file.h" @@ -58,8 +59,9 @@ #include "jdwp/object_registry.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "jvmti_allocator.h" +#include "linear_alloc.h" #include "mirror/class-inl.h" #include "mirror/class_ext.h" #include "mirror/object.h" @@ -67,6 +69,8 @@ #include "non_debuggable_classes.h" #include "object_lock.h" #include "runtime.h" +#include "stack.h" +#include "thread_list.h" #include "ti_breakpoint.h" #include "ti_class_loader.h" #include "transform.h" @@ -485,7 +489,7 @@ art::mirror::DexCache* Redefiner::ClassRedefinition::CreateNewDexCache( art::ClassLinker* cl = driver_->runtime_->GetClassLinker(); art::Handle cache(hs.NewHandle( art::ObjPtr::DownCast( - cl->GetClassRoot(art::ClassLinker::kJavaLangDexCache)->AllocObject(driver_->self_)))); + art::GetClassRoot(cl)->AllocObject(driver_->self_)))); if (cache.IsNull()) { driver_->self_->AssertPendingOOMException(); return nullptr; @@ -522,7 +526,7 @@ art::mirror::Object* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFile( return art::mirror::ByteArray::AllocateAndFill( driver_->self_, reinterpret_cast(original_dex_file_.data()), - original_dex_file_.size()); + original_dex_file_.size()).Ptr(); } // See if we already have one set. @@ -859,12 +863,10 @@ class RedefinitionDataHolder { art::Thread* self, std::vector* redefinitions) REQUIRES_SHARED(art::Locks::mutator_lock_) : - arr_( - hs->NewHandle( - art::mirror::ObjectArray::Alloc( - self, - runtime->GetClassLinker()->GetClassRoot(art::ClassLinker::kObjectArrayClass), - redefinitions->size() * kNumSlots))), + arr_(hs->NewHandle(art::mirror::ObjectArray::Alloc( + self, + art::GetClassRoot>(runtime->GetClassLinker()), + redefinitions->size() * kNumSlots))), redefinitions_(redefinitions) {} bool IsNull() const REQUIRES_SHARED(art::Locks::mutator_lock_) { diff --git a/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h index fa7d28648dee706258f256296288cc1f111ca846..e337491ae33a2b397ca0e7630b353f32e1482d91 100644 --- a/openjdkjvmti/ti_redefine.h +++ b/openjdkjvmti/ti_redefine.h @@ -37,34 +37,18 @@ #include #include "art_jvmti.h" -#include "art_method.h" #include "base/array_ref.h" -#include "class_linker.h" +#include "base/globals.h" #include "dex/dex_file.h" -#include "dex/utf.h" -#include "gc_root-inl.h" -#include "globals.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "jvmti.h" -#include "linear_alloc.h" -#include "mem_map.h" -#include "mirror/array-inl.h" #include "mirror/array.h" -#include "mirror/class-inl.h" #include "mirror/class.h" -#include "mirror/class_loader-inl.h" -#include "mirror/string-inl.h" -#include "oat_file.h" #include "obj_ptr.h" -#include "scoped_thread_state_change-inl.h" -#include "stack.h" -#include "thread_list.h" -#include "ti_class_definition.h" -#include "transform.h" -#include "utils/dex_cache_arrays_layout-inl.h" namespace openjdkjvmti { +class ArtClassDefinition; class RedefinitionDataHolder; class RedefinitionDataIter; diff --git a/openjdkjvmti/ti_search.cc b/openjdkjvmti/ti_search.cc index cbb7b53bff2e97d115758f780ff3b67a35d3ddca..bcbab14cdd37fb1b8d8bca22dfd7c9455d701c4e 100644 --- a/openjdkjvmti/ti_search.cc +++ b/openjdkjvmti/ti_search.cc @@ -41,7 +41,7 @@ #include "dex/art_dex_file_loader.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object.h" #include "mirror/string.h" diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc index 4526be4cbe268a57570bd0834277e36c1454e79f..318d98d877065f68ef30f316b799db79cb0cf091 100644 --- a/openjdkjvmti/ti_stack.cc +++ b/openjdkjvmti/ti_stack.cc @@ -50,8 +50,8 @@ #include "dex/dex_file_types.h" #include "gc_root.h" #include "handle_scope-inl.h" -#include "jni_env_ext.h" -#include "jni_internal.h" +#include "jni/jni_env_ext.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/dex_cache.h" #include "nativehelper/scoped_local_ref.h" @@ -322,7 +322,9 @@ struct GetAllStackTracesVectorClosure : public art::Closure { }; template -static void RunCheckpointAndWait(Data* data, size_t max_frame_count) { +static void RunCheckpointAndWait(Data* data, size_t max_frame_count) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + // Note: requires the mutator lock as the checkpoint requires the mutator lock. GetAllStackTracesVectorClosure closure(max_frame_count, data); size_t barrier_count = art::Runtime::Current()->GetThreadList()->RunCheckpoint(&closure, nullptr); if (barrier_count == 0) { @@ -380,9 +382,11 @@ jvmtiError StackUtil::GetAllStackTraces(jvmtiEnv* env, }; AllStackTracesData data; - RunCheckpointAndWait(&data, static_cast(max_frame_count)); - art::Thread* current = art::Thread::Current(); + { + art::ScopedObjectAccess soa(current); + RunCheckpointAndWait(&data, static_cast(max_frame_count)); + } // Convert the data into our output format. diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc index 414139c7b4a104dc3d5d4d781259a9867849a9eb..949b56686065e08384d2b92c4824496767f3af66 100644 --- a/openjdkjvmti/ti_thread.cc +++ b/openjdkjvmti/ti_thread.cc @@ -43,7 +43,7 @@ #include "gc/gc_cause.h" #include "gc/scoped_gc_critical_section.h" #include "gc_root-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/object-inl.h" #include "mirror/string.h" @@ -60,6 +60,8 @@ namespace openjdkjvmti { +static const char* kJvmtiTlsKey = "JvmtiTlsKey"; + art::ArtField* ThreadUtil::context_class_loader_ = nullptr; struct ThreadCallback : public art::ThreadLifecycleCallback { @@ -624,14 +626,15 @@ jvmtiError ThreadUtil::GetAllThreads(jvmtiEnv* env, // The struct that we store in the art::Thread::custom_tls_ that maps the jvmtiEnvs to the data // stored with that thread. This is needed since different jvmtiEnvs are not supposed to share TLS // data but we only have a single slot in Thread objects to store data. -struct JvmtiGlobalTLSData { +struct JvmtiGlobalTLSData : public art::TLSData { std::unordered_map data GUARDED_BY(art::Locks::thread_list_lock_); }; static void RemoveTLSData(art::Thread* target, void* ctx) REQUIRES(art::Locks::thread_list_lock_) { jvmtiEnv* env = reinterpret_cast(ctx); art::Locks::thread_list_lock_->AssertHeld(art::Thread::Current()); - JvmtiGlobalTLSData* global_tls = reinterpret_cast(target->GetCustomTLS()); + JvmtiGlobalTLSData* global_tls = + reinterpret_cast(target->GetCustomTLS(kJvmtiTlsKey)); if (global_tls != nullptr) { global_tls->data.erase(env); } @@ -654,10 +657,12 @@ jvmtiError ThreadUtil::SetThreadLocalStorage(jvmtiEnv* env, jthread thread, cons return err; } - JvmtiGlobalTLSData* global_tls = reinterpret_cast(target->GetCustomTLS()); + JvmtiGlobalTLSData* global_tls = + reinterpret_cast(target->GetCustomTLS(kJvmtiTlsKey)); if (global_tls == nullptr) { - target->SetCustomTLS(new JvmtiGlobalTLSData); - global_tls = reinterpret_cast(target->GetCustomTLS()); + // Synchronized using thread_list_lock_ to prevent racing sets. + target->SetCustomTLS(kJvmtiTlsKey, new JvmtiGlobalTLSData); + global_tls = reinterpret_cast(target->GetCustomTLS(kJvmtiTlsKey)); } global_tls->data[env] = data; @@ -681,7 +686,8 @@ jvmtiError ThreadUtil::GetThreadLocalStorage(jvmtiEnv* env, return err; } - JvmtiGlobalTLSData* global_tls = reinterpret_cast(target->GetCustomTLS()); + JvmtiGlobalTLSData* global_tls = + reinterpret_cast(target->GetCustomTLS(kJvmtiTlsKey)); if (global_tls == nullptr) { *data_ptr = nullptr; return OK; @@ -812,6 +818,37 @@ jvmtiError ThreadUtil::RunAgentThread(jvmtiEnv* jvmti_env, return ERR(NONE); } +class ScopedSuspendByPeer { + public: + explicit ScopedSuspendByPeer(jthread jtarget) + : thread_list_(art::Runtime::Current()->GetThreadList()), + timeout_(false), + target_(thread_list_->SuspendThreadByPeer(jtarget, + /* suspend_thread */ true, + art::SuspendReason::kInternal, + &timeout_)) { } + ~ScopedSuspendByPeer() { + if (target_ != nullptr) { + if (!thread_list_->Resume(target_, art::SuspendReason::kInternal)) { + LOG(ERROR) << "Failed to resume " << target_ << "!"; + } + } + } + + art::Thread* GetTargetThread() const { + return target_; + } + + bool TimedOut() const { + return timeout_; + } + + private: + art::ThreadList* thread_list_; + bool timeout_; + art::Thread* target_; +}; + jvmtiError ThreadUtil::SuspendOther(art::Thread* self, jthread target_jthread) { // Loop since we need to bail out and try again if we would end up getting suspended while holding @@ -839,29 +876,27 @@ jvmtiError ThreadUtil::SuspendOther(art::Thread* self, if (!GetAliveNativeThread(target_jthread, soa, &target, &err)) { return err; } - art::ThreadState state = target->GetState(); - if (state == art::ThreadState::kStarting || target->IsStillStarting()) { - return ERR(THREAD_NOT_ALIVE); - } else { - art::MutexLock thread_suspend_count_mu(self, *art::Locks::thread_suspend_count_lock_); - if (target->GetUserCodeSuspendCount() != 0) { - return ERR(THREAD_SUSPENDED); - } - } } - bool timeout = true; - art::Thread* ret_target = art::Runtime::Current()->GetThreadList()->SuspendThreadByPeer( - target_jthread, - /* request_suspension */ true, - art::SuspendReason::kForUserCode, - &timeout); - if (ret_target == nullptr && !timeout) { + // Get the actual thread in a suspended state so we can change the user-code suspend count. + ScopedSuspendByPeer ssbp(target_jthread); + if (ssbp.GetTargetThread() == nullptr && !ssbp.TimedOut()) { // TODO It would be good to get more information about why exactly the thread failed to // suspend. return ERR(INTERNAL); - } else if (!timeout) { - // we didn't time out and got a result. - return OK; + } else if (!ssbp.TimedOut()) { + art::ThreadState state = ssbp.GetTargetThread()->GetState(); + if (state == art::ThreadState::kStarting || ssbp.GetTargetThread()->IsStillStarting()) { + return ERR(THREAD_NOT_ALIVE); + } + // we didn't time out and got a result. Suspend the thread by usercode and return. It's + // already suspended internal so we don't need to do anything but increment the count. + art::MutexLock thread_suspend_count_mu(self, *art::Locks::thread_suspend_count_lock_); + if (ssbp.GetTargetThread()->GetUserCodeSuspendCount() != 0) { + return ERR(THREAD_SUSPENDED); + } + bool res = ssbp.GetTargetThread()->ModifySuspendCount( + self, +1, nullptr, art::SuspendReason::kForUserCode); + return res ? OK : ERR(INTERNAL); } // We timed out. Just go around and try again. } while (true); @@ -870,6 +905,17 @@ jvmtiError ThreadUtil::SuspendOther(art::Thread* self, jvmtiError ThreadUtil::SuspendSelf(art::Thread* self) { CHECK(self == art::Thread::Current()); + if (!self->CanBeSuspendedByUserCode()) { + // TODO This is really undesirable. As far as I can tell this is can only come about because of + // class-loads in the jit-threads (through either VMObjectAlloc or the ClassLoad/ClassPrepare + // events that we send). It's unlikely that anyone would be suspending themselves there since + // it's almost guaranteed to cause a deadlock but it is technically allowed. Ideally we'd want + // to put a CHECK here (or in the event-dispatch code) that we are only in this situation when + // sending the GC callbacks but the jit causing events means we cannot do this. + LOG(WARNING) << "Attempt to self-suspend on a thread without suspension enabled. Thread is " + << *self; + return ERR(INTERNAL); + } { art::MutexLock mu(self, *art::Locks::user_code_suspension_lock_); art::MutexLock thread_list_mu(self, *art::Locks::thread_suspend_count_lock_); @@ -917,7 +963,6 @@ jvmtiError ThreadUtil::ResumeThread(jvmtiEnv* env ATTRIBUTE_UNUSED, return ERR(NULL_POINTER); } art::Thread* self = art::Thread::Current(); - art::Thread* target; // Retry until we know we won't get suspended by user code while resuming something. do { SuspendCheck(self); @@ -928,36 +973,37 @@ jvmtiError ThreadUtil::ResumeThread(jvmtiEnv* env ATTRIBUTE_UNUSED, continue; } // From now on we know we cannot get suspended by user-code. - { - // NB This does a SuspendCheck (during thread state change) so we need to make sure we don't - // have the 'suspend_lock' locked here. - art::ScopedObjectAccess soa(self); - art::MutexLock tll_mu(self, *art::Locks::thread_list_lock_); - jvmtiError err = ERR(INTERNAL); - if (!GetAliveNativeThread(thread, soa, &target, &err)) { - return err; - } else if (target == self) { - // We would have paused until we aren't suspended anymore due to the ScopedObjectAccess so - // we can just return THREAD_NOT_SUSPENDED. Unfortunately we cannot do any real DCHECKs - // about current state since it's all concurrent. - return ERR(THREAD_NOT_SUSPENDED); - } - // The JVMTI spec requires us to return THREAD_NOT_SUSPENDED if it is alive but we really - // cannot tell why resume failed. - { - art::MutexLock thread_suspend_count_mu(self, *art::Locks::thread_suspend_count_lock_); - if (target->GetUserCodeSuspendCount() == 0) { - return ERR(THREAD_NOT_SUSPENDED); - } - } + // NB This does a SuspendCheck (during thread state change) so we need to make sure we don't + // have the 'suspend_lock' locked here. + art::ScopedObjectAccess soa(self); + if (thread == nullptr) { + // The thread is the current thread. + return ERR(THREAD_NOT_SUSPENDED); + } else if (!soa.Env()->IsInstanceOf(thread, art::WellKnownClasses::java_lang_Thread)) { + // Not a thread object. + return ERR(INVALID_THREAD); + } else if (self->GetPeer() == soa.Decode(thread)) { + // The thread is the current thread. + return ERR(THREAD_NOT_SUSPENDED); + } + ScopedSuspendByPeer ssbp(thread); + if (ssbp.TimedOut()) { + // Unknown error. Couldn't suspend thread! + return ERR(INTERNAL); + } else if (ssbp.GetTargetThread() == nullptr) { + // Thread must not be alive. + return ERR(THREAD_NOT_ALIVE); } - // It is okay that we don't have a thread_list_lock here since we know that the thread cannot - // die since it is currently held suspended by a SuspendReason::kForUserCode suspend. - DCHECK(target != self); - if (!art::Runtime::Current()->GetThreadList()->Resume(target, - art::SuspendReason::kForUserCode)) { + // We didn't time out and got a result. Check the thread is suspended by usercode, unsuspend it + // and return. It's already suspended internal so we don't need to do anything but decrement the + // count. + art::MutexLock thread_list_mu(self, *art::Locks::thread_suspend_count_lock_); + if (ssbp.GetTargetThread()->GetUserCodeSuspendCount() == 0) { + return ERR(THREAD_NOT_SUSPENDED); + } else if (!ssbp.GetTargetThread()->ModifySuspendCount( + self, -1, nullptr, art::SuspendReason::kForUserCode)) { // TODO Give a better error. - // This is most likely THREAD_NOT_SUSPENDED but we cannot really be sure. + // This should not really be possible and is probably some race. return ERR(INTERNAL); } else { return OK; diff --git a/openjdkjvmti/ti_threadgroup.cc b/openjdkjvmti/ti_threadgroup.cc index c0597ad0cc555058d227d6b16cee8ba3593ef272..e17e61fe62d7dd4e8c8af8a6edc818daa8289fa7 100644 --- a/openjdkjvmti/ti_threadgroup.cc +++ b/openjdkjvmti/ti_threadgroup.cc @@ -37,7 +37,7 @@ #include "base/macros.h" #include "base/mutex.h" #include "handle_scope-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/object-inl.h" #include "mirror/string.h" diff --git a/openjdkjvmti/transform.cc b/openjdkjvmti/transform.cc index 62094a327decdad0b55014fcc20d2eaf138c7f76..8797553b077fba5a5c8a8f158102835b0b2018b7 100644 --- a/openjdkjvmti/transform.cc +++ b/openjdkjvmti/transform.cc @@ -39,6 +39,8 @@ #include "art_method.h" #include "base/array_ref.h" +#include "base/globals.h" +#include "base/mem_map.h" #include "class_linker.h" #include "dex/dex_file.h" #include "dex/dex_file_types.h" @@ -46,12 +48,10 @@ #include "events-inl.h" #include "fault_handler.h" #include "gc_root-inl.h" -#include "globals.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "jvalue.h" #include "jvmti.h" #include "linear_alloc.h" -#include "mem_map.h" #include "mirror/array.h" #include "mirror/class-inl.h" #include "mirror/class_ext.h" diff --git a/patchoat/Android.bp b/patchoat/Android.bp index 0e8e517cd4b2332412cf7b327ee63caae92b385a..13c8f475f551b2cf2540980465dfef91ffe33c15 100644 --- a/patchoat/Android.bp +++ b/patchoat/Android.bp @@ -25,6 +25,7 @@ cc_defaults { }, }, shared_libs: [ + "libartbase", "libbase", "libcrypto", // For computing the digest of image file ], @@ -58,7 +59,6 @@ art_cc_test { "patchoat_test.cc", ], shared_libs: [ - "libartd", "libcrypto", // For computing the digest of image file ], } diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index c742c1656f78cc223259da317523ed3e388088fb..97b315e85cef2c3102e564925ce6a0aaf6f96d60 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -43,6 +43,7 @@ #include "base/unix_file/fd_file.h" #include "base/unix_file/random_access_file_utils.h" #include "base/utils.h" +#include "class_root.h" #include "elf_file.h" #include "elf_file_impl.h" #include "elf_utils.h" @@ -538,7 +539,7 @@ bool PatchOat::Patch(const std::string& image_location, space_to_memmap_map.emplace(space, std::move(image)); PatchOat p = PatchOat(isa, - space_to_memmap_map.at(space).get(), + space_to_memmap_map[space].get(), space->GetLiveBitmap(), space->GetMemMap(), delta, @@ -614,7 +615,7 @@ bool PatchOat::Patch(const std::string& image_location, } } - 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(); @@ -694,7 +695,7 @@ bool PatchOat::Verify(const std::string& image_location, } } - 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(); @@ -704,6 +705,7 @@ bool PatchOat::Verify(const std::string& image_location, } bool PatchOat::WriteImage(File* out) { + CHECK(out != nullptr); TimingLogger::ScopedTiming t("Writing image File", timings_); std::string error_msg; @@ -713,7 +715,6 @@ bool PatchOat::WriteImage(File* out) { true /* read_only_mode */, &error_msg); CHECK(image_ != nullptr); - CHECK(out != nullptr); size_t expect = image_->Size(); if (out->WriteFully(reinterpret_cast(image_->Begin()), expect) && out->SetLength(expect) == 0) { @@ -976,7 +977,7 @@ bool PatchOat::PatchImage(bool primary_image) { ImageHeader* image_header = reinterpret_cast(image_->Begin()); CHECK_GT(image_->Size(), sizeof(ImageHeader)); // These are the roots from the original file. - auto* img_roots = image_header->GetImageRoots(); + mirror::ObjectArray* img_roots = image_header->GetImageRoots().Ptr(); image_header->RelocateImage(delta_); PatchArtFields(image_header); @@ -1057,8 +1058,8 @@ void PatchOat::VisitObject(mirror::Object* object) { native_visitor); } } - } else if (object->GetClass() == mirror::Method::StaticClass() || - object->GetClass() == mirror::Constructor::StaticClass()) { + } else if (object->GetClass() == GetClassRoot() || + object->GetClass() == GetClassRoot()) { // Need to go update the ArtMethod. auto* dest = down_cast(copy); auto* src = down_cast(object); diff --git a/patchoat/patchoat_test.cc b/patchoat/patchoat_test.cc index 4cfb0db2e531818bca47406732f8143d721ad861..08bf31c4bd3e9207c1e6ab79db207cbca583fe79 100644 --- a/patchoat/patchoat_test.cc +++ b/patchoat/patchoat_test.cc @@ -24,6 +24,7 @@ #include "android-base/stringprintf.h" #include "android-base/strings.h" +#include "base/hex_dump.h" #include "base/leb128.h" #include "dexopt_test.h" #include "runtime.h" @@ -320,6 +321,12 @@ class PatchoatTest : public DexoptTest { if (image1[i] != image2[i]) { *error_msg = StringPrintf("%s and %s differ at offset %zu", filename1.c_str(), filename2.c_str(), i); + size_t hexdump_size = std::min(16u, size - i); + HexDump dump1(&image1[i], hexdump_size, /* show_actual_addresses */ false, /* prefix */ ""); + HexDump dump2(&image2[i], hexdump_size, /* show_actual_addresses */ false, /* prefix */ ""); + std::ostringstream oss; + oss << "\n" << dump1 << "\n" << dump2; + *error_msg += oss.str(); return true; } } diff --git a/profman/Android.bp b/profman/Android.bp index 163be2b64f9e1c321792481c0c68b87b7c510db8..5aaccb0cef26cd0483539dd715c2599e892d6105 100644 --- a/profman/Android.bp +++ b/profman/Android.bp @@ -40,7 +40,9 @@ art_cc_binary { defaults: ["profman-defaults"], shared_libs: [ "libart", + "libprofile", "libdexfile", + "libartbase", ], } @@ -52,7 +54,9 @@ art_cc_binary { ], shared_libs: [ "libartd", + "libprofiled", "libdexfiled", + "libartbased", ], } @@ -61,5 +65,8 @@ art_cc_test { defaults: [ "art_gtest_defaults", ], + shared_libs: [ + "libprofiled", + ], srcs: ["profile_assistant_test.cc"], } diff --git a/profman/boot_image_profile.cc b/profman/boot_image_profile.cc index 60238e20828b332201f6e86fcc513a592f5b81ab..6715680361cd85d3229744baf2efdf71f146a477 100644 --- a/profman/boot_image_profile.cc +++ b/profman/boot_image_profile.cc @@ -18,10 +18,11 @@ #include #include "boot_image_profile.h" +#include "dex/class_accessor-inl.h" #include "dex/dex_file-inl.h" #include "dex/method_reference.h" #include "dex/type_reference.h" -#include "jit/profile_compilation_info.h" +#include "profile/profile_compilation_info.h" namespace art { @@ -74,38 +75,31 @@ void GenerateBootImageProfile( } } // Walk all of the classes and add them to the profile if they meet the requirements. - for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); - TypeReference ref(dex_file.get(), class_def.class_idx_); + for (ClassAccessor accessor : dex_file->GetClasses()) { + TypeReference ref(dex_file.get(), accessor.GetClassIdx()); bool is_clean = true; - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data != nullptr) { - ClassDataItemIterator it(*dex_file, class_data); - while (it.HasNextStaticField()) { - const uint32_t flags = it.GetFieldAccessFlags(); - if ((flags & kAccFinal) == 0) { - // Not final static field will probably dirty the class. - is_clean = false; - break; - } - it.Next(); + auto method_visitor = [&](const ClassAccessor::Method& method) { + const uint32_t flags = method.GetAccessFlags(); + if ((flags & kAccNative) != 0) { + // Native method will get dirtied. + is_clean = false; } - it.SkipInstanceFields(); - while (it.HasNextMethod()) { - const uint32_t flags = it.GetMethodAccessFlags(); - if ((flags & kAccNative) != 0) { - // Native method will get dirtied. - is_clean = false; - break; - } - if ((flags & kAccConstructor) != 0 && (flags & kAccStatic) != 0) { - // Class initializer, may get dirtied (not sure). - is_clean = false; - break; - } - it.Next(); + if ((flags & kAccConstructor) != 0 && (flags & kAccStatic) != 0) { + // Class initializer, may get dirtied (not sure). + is_clean = false; } - } + }; + accessor.VisitFieldsAndMethods( + [&](const ClassAccessor::Field& field) { + if (!field.IsFinal()) { + // Not final static field will probably dirty the class. + is_clean = false; + } + }, + /*instance_fields*/ VoidFunctor(), + method_visitor, + method_visitor); + ++(is_clean ? clean_count : dirty_count); // This counter is how many profiles contain the class. size_t counter = 0; diff --git a/profman/profile_assistant.h b/profman/profile_assistant.h index ee555840d7ad173e1373325ea646dcc8a98c2eb0..c1d6f8e7a91f3fa3b23877952dfdb0c0180a0f69 100644 --- a/profman/profile_assistant.h +++ b/profman/profile_assistant.h @@ -21,7 +21,7 @@ #include #include "base/scoped_flock.h" -#include "jit/profile_compilation_info.h" +#include "profile/profile_compilation_info.h" namespace art { diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc index c6661855bea0707419ea2804d298fd03056ce012..370f59dc8a04da86579ceaf71f4cf73938631811 100644 --- a/profman/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -22,17 +22,19 @@ #include "base/utils.h" #include "common_runtime_test.h" #include "dex/descriptors_names.h" +#include "dex/type_reference.h" #include "exec_utils.h" -#include "jit/profile_compilation_info.h" #include "linear_alloc.h" #include "mirror/class-inl.h" #include "obj_ptr-inl.h" +#include "profile/profile_compilation_info.h" #include "profile_assistant.h" #include "scoped_thread_state_change-inl.h" namespace art { using Hotness = ProfileCompilationInfo::MethodHotness; +using TypeReferenceSet = std::set; static constexpr size_t kMaxMethodIds = 65535; @@ -308,25 +310,24 @@ class ProfileAssistantTest : public CommonRuntimeTest { return true; } - mirror::Class* GetClass(jobject class_loader, const std::string& clazz) { + ObjPtr GetClass(ScopedObjectAccess& soa, + jobject class_loader, + const std::string& clazz) REQUIRES_SHARED(Locks::mutator_lock_) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - Thread* self = Thread::Current(); - ScopedObjectAccess soa(self); - StackHandleScope<1> hs(self); - Handle h_loader( - hs.NewHandle(ObjPtr::DownCast(self->DecodeJObject(class_loader)))); - return class_linker->FindClass(self, clazz.c_str(), h_loader); + StackHandleScope<1> hs(soa.Self()); + Handle h_loader(hs.NewHandle( + ObjPtr::DownCast(soa.Self()->DecodeJObject(class_loader)))); + return class_linker->FindClass(soa.Self(), clazz.c_str(), h_loader); } ArtMethod* GetVirtualMethod(jobject class_loader, const std::string& clazz, const std::string& name) { - mirror::Class* klass = GetClass(class_loader, clazz); + ScopedObjectAccess soa(Thread::Current()); + ObjPtr klass = GetClass(soa, class_loader, clazz); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); const auto pointer_size = class_linker->GetImagePointerSize(); ArtMethod* method = nullptr; - Thread* self = Thread::Current(); - ScopedObjectAccess soa(self); for (auto& m : klass->GetVirtualMethods(pointer_size)) { if (name == m.GetName()) { EXPECT_TRUE(method == nullptr); @@ -336,9 +337,14 @@ class ProfileAssistantTest : public CommonRuntimeTest { return method; } + static TypeReference MakeTypeReference(ObjPtr klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + return TypeReference(&klass->GetDexFile(), klass->GetDexTypeIndex()); + } + // Verify that given method has the expected inline caches and nothing else. void AssertInlineCaches(ArtMethod* method, - const std::set& expected_clases, + const TypeReferenceSet& expected_clases, const ProfileCompilationInfo& info, bool is_megamorphic, bool is_missing_types) @@ -355,12 +361,11 @@ class ProfileAssistantTest : public CommonRuntimeTest { ASSERT_EQ(dex_pc_data.is_missing_types, is_missing_types); ASSERT_EQ(expected_clases.size(), dex_pc_data.classes.size()); size_t found = 0; - for (mirror::Class* it : expected_clases) { + for (const TypeReference& type_ref : expected_clases) { for (const auto& class_ref : dex_pc_data.classes) { ProfileCompilationInfo::DexReference dex_ref = pmi->dex_references[class_ref.dex_profile_index]; - if (dex_ref.MatchesDex(&(it->GetDexFile())) && - class_ref.type_index == it->GetDexTypeIndex()) { + if (dex_ref.MatchesDex(type_ref.dex_file) && class_ref.type_index == type_ref.TypeIndex()) { found++; } } @@ -715,7 +720,7 @@ TEST_F(ProfileAssistantTest, TestProfileCreationGenerateMethods) { ASSERT_TRUE(info.Load(GetFd(profile_file))); // Verify that the profile has matching methods. ScopedObjectAccess soa(Thread::Current()); - ObjPtr klass = GetClass(nullptr, "Ljava/lang/Math;"); + ObjPtr klass = GetClass(soa, /* class_loader */ nullptr, "Ljava/lang/Math;"); ASSERT_TRUE(klass != nullptr); size_t method_count = 0; for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) { @@ -907,9 +912,10 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) { jobject class_loader = LoadDex("ProfileTestMultiDex"); ASSERT_NE(class_loader, nullptr); - mirror::Class* sub_a = GetClass(class_loader, "LSubA;"); - mirror::Class* sub_b = GetClass(class_loader, "LSubB;"); - mirror::Class* sub_c = GetClass(class_loader, "LSubC;"); + StackHandleScope<3> hs(soa.Self()); + Handle sub_a = hs.NewHandle(GetClass(soa, class_loader, "LSubA;")); + Handle sub_b = hs.NewHandle(GetClass(soa, class_loader, "LSubB;")); + Handle sub_c = hs.NewHandle(GetClass(soa, class_loader, "LSubC;")); ASSERT_TRUE(sub_a != nullptr); ASSERT_TRUE(sub_b != nullptr); @@ -921,8 +927,8 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) { "LTestInline;", "inlineMonomorphic"); ASSERT_TRUE(inline_monomorphic != nullptr); - std::set expected_monomorphic; - expected_monomorphic.insert(sub_a); + TypeReferenceSet expected_monomorphic; + expected_monomorphic.insert(MakeTypeReference(sub_a.Get())); AssertInlineCaches(inline_monomorphic, expected_monomorphic, info, @@ -936,10 +942,10 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) { "LTestInline;", "inlinePolymorphic"); ASSERT_TRUE(inline_polymorhic != nullptr); - std::set expected_polymorphic; - expected_polymorphic.insert(sub_a); - expected_polymorphic.insert(sub_b); - expected_polymorphic.insert(sub_c); + TypeReferenceSet expected_polymorphic; + expected_polymorphic.insert(MakeTypeReference(sub_a.Get())); + expected_polymorphic.insert(MakeTypeReference(sub_b.Get())); + expected_polymorphic.insert(MakeTypeReference(sub_c.Get())); AssertInlineCaches(inline_polymorhic, expected_polymorphic, info, @@ -953,7 +959,7 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) { "LTestInline;", "inlineMegamorphic"); ASSERT_TRUE(inline_megamorphic != nullptr); - std::set expected_megamorphic; + TypeReferenceSet expected_megamorphic; AssertInlineCaches(inline_megamorphic, expected_megamorphic, info, @@ -967,7 +973,7 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) { "LTestInline;", "inlineMissingTypes"); ASSERT_TRUE(inline_missing_types != nullptr); - std::set expected_missing_Types; + TypeReferenceSet expected_missing_Types; AssertInlineCaches(inline_missing_types, expected_missing_Types, info, @@ -1137,18 +1143,18 @@ TEST_F(ProfileAssistantTest, DumpOnly) { // Check the actual contents of the dump by looking at the offsets of the methods. for (uint32_t m : hot_methods) { const size_t pos = output.find(std::to_string(m) + "[],", hot_offset); - ASSERT_NE(pos, std::string::npos); - EXPECT_LT(pos, startup_offset); + ASSERT_NE(pos, std::string::npos) << output; + EXPECT_LT(pos, startup_offset) << output; } for (uint32_t m : startup_methods) { const size_t pos = output.find(std::to_string(m) + ",", startup_offset); - ASSERT_NE(pos, std::string::npos); - EXPECT_LT(pos, post_startup_offset); + ASSERT_NE(pos, std::string::npos) << output; + EXPECT_LT(pos, post_startup_offset) << output; } for (uint32_t m : post_startup_methods) { const size_t pos = output.find(std::to_string(m) + ",", post_startup_offset); - ASSERT_NE(pos, std::string::npos); - EXPECT_LT(pos, classes_offset); + ASSERT_NE(pos, std::string::npos) << output; + EXPECT_LT(pos, classes_offset) << output; } } diff --git a/profman/profman.cc b/profman/profman.cc index 72c59acaf428ee1e2977ddb7b8797714fe3e5736..9b470973c6f7774eab2202cc68e6f10f23280613 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -34,24 +33,24 @@ #include "base/dumpable.h" #include "base/logging.h" // For InitLogging. -#include "base/mutex.h" +#include "base/mem_map.h" #include "base/scoped_flock.h" #include "base/stringpiece.h" #include "base/time_utils.h" #include "base/unix_file/fd_file.h" #include "base/utils.h" +#include "base/zip_archive.h" #include "boot_image_profile.h" #include "dex/art_dex_file_loader.h" #include "dex/bytecode_utils.h" +#include "dex/class_accessor-inl.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "dex/dex_file_types.h" #include "dex/type_reference.h" -#include "jit/profile_compilation_info.h" +#include "profile/profile_compilation_info.h" #include "profile_assistant.h" -#include "runtime.h" -#include "zip_archive.h" namespace art { @@ -179,6 +178,11 @@ static constexpr char kMethodFlagStringHot = 'H'; static constexpr char kMethodFlagStringStartup = 'S'; static constexpr char kMethodFlagStringPostStartup = 'P'; +NO_RETURN static void Abort(const char* msg) { + LOG(ERROR) << msg; + exit(1); +} + // TODO(calin): This class has grown too much from its initial design. Split the functionality // into smaller, more contained pieces. class ProfMan FINAL { @@ -204,8 +208,8 @@ class ProfMan FINAL { original_argc = argc; original_argv = argv; - Locks::Init(); - InitLogging(argv, Runtime::Abort); + MemMap::Init(); + InitLogging(argv, Abort); // Skip over the command name. argv++; @@ -385,7 +389,7 @@ class ProfMan FINAL { } bool OpenApkFilesFromLocations( - std::function&&)> process_fn) { + const std::function&&)>& process_fn) { bool use_apk_fd_list = !apks_fd_.empty(); if (use_apk_fd_list) { // Get the APKs from the collection of FDs. @@ -836,7 +840,8 @@ class ProfMan FINAL { bool found_invoke = false; for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(*dex_file, code_item)) { - if (inst->Opcode() == Instruction::INVOKE_VIRTUAL) { + if (inst->Opcode() == Instruction::INVOKE_VIRTUAL || + inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE) { if (found_invoke) { LOG(ERROR) << "Multiple invoke INVOKE_VIRTUAL found: " << dex_file->PrettyMethod(method_index); @@ -925,19 +930,13 @@ class ProfMan FINAL { dex_resolved_classes.first->AddClass(class_ref.TypeIndex()); std::vector methods; if (method_str == kClassAllMethods) { - // Add all of the methods. - const DexFile::ClassDef* class_def = dex_file->FindClassDef(class_ref.TypeIndex()); - const uint8_t* class_data = dex_file->GetClassData(*class_def); - if (class_data != nullptr) { - ClassDataItemIterator it(*dex_file, class_data); - it.SkipAllFields(); - while (it.HasNextMethod()) { - if (it.GetMethodCodeItemOffset() != 0) { - // Add all of the methods that have code to the profile. - const uint32_t method_idx = it.GetMemberIndex(); - methods.push_back(ProfileMethodInfo(MethodReference(dex_file, method_idx))); - } - it.Next(); + 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. + methods.push_back(ProfileMethodInfo(method.GetReference())); } } } @@ -1308,4 +1307,3 @@ static int profman(int argc, char** argv) { int main(int argc, char **argv) { return art::profman(argc, argv); } - diff --git a/runtime/Android.bp b/runtime/Android.bp index 9f0cf127b9231a68c44d530b5c4c06acc541e692..6ec626591a24ff95a36549149024f330f47140db 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -23,7 +23,7 @@ JIT_DEBUG_REGISTER_CODE_LDFLAGS = [ "-Wl,--keep-unique,__dex_debug_register_code" ] -cc_defaults { +libart_cc_defaults { name: "libart_defaults", defaults: ["art_defaults"], host_supported: true, @@ -31,25 +31,23 @@ cc_defaults { "aot_class_linker.cc", "art_field.cc", "art_method.cc", + "backtrace_helper.cc", "barrier.cc", - "base/arena_allocator.cc", - "base/arena_bit_vector.cc", - "base/file_utils.cc", + "base/mem_map_arena_pool.cc", "base/mutex.cc", "base/quasi_atomic.cc", - "base/scoped_arena_allocator.cc", "base/timing_logger.cc", "cha.cc", - "check_jni.cc", "class_linker.cc", "class_loader_context.cc", + "class_root.cc", "class_table.cc", "common_throws.cc", "compiler_filter.cc", "debug_print.cc", "debugger.cc", - "dex/art_dex_file_loader.cc", "dex/dex_file_annotations.cc", + "dex_register_location.cc", "dex_to_dex_decompiler.cc", "elf_file.cc", "exec_utils.cc", @@ -67,7 +65,6 @@ cc_defaults { "gc/collector/garbage_collector.cc", "gc/collector/immune_region.cc", "gc/collector/immune_spaces.cc", - "gc/collector/mark_compact.cc", "gc/collector/mark_sweep.cc", "gc/collector/partial_mark_sweep.cc", "gc/collector/semi_space.cc", @@ -103,7 +100,6 @@ cc_defaults { "interpreter/shadow_frame.cc", "interpreter/unstarted_runtime.cc", "java_frame_root_info.cc", - "java_vm_ext.cc", "jdwp/jdwp_event.cc", "jdwp/jdwp_expand_buf.cc", "jdwp/jdwp_handler.cc", @@ -111,19 +107,17 @@ cc_defaults { "jdwp/jdwp_request.cc", "jdwp/jdwp_socket.cc", "jdwp/object_registry.cc", - "jni_env_ext.cc", "jit/debugger_interface.cc", "jit/jit.cc", "jit/jit_code_cache.cc", - "jit/profile_compilation_info.cc", "jit/profiling_info.cc", "jit/profile_saver.cc", - "jni_internal.cc", - "jobject_comparator.cc", + "jni/check_jni.cc", + "jni/java_vm_ext.cc", + "jni/jni_env_ext.cc", + "jni/jni_internal.cc", "linear_alloc.cc", "managed_stack.cc", - "mem_map.cc", - "memory_region.cc", "method_handles.cc", "mirror/array.cc", "mirror/call_site.cc", @@ -138,7 +132,6 @@ cc_defaults { "mirror/method_handles_lookup.cc", "mirror/method_type.cc", "mirror/object.cc", - "mirror/reference.cc", "mirror/stack_trace_element.cc", "mirror/string.cc", "mirror/throwable.cc", @@ -203,7 +196,7 @@ cc_defaults { "ti/agent.cc", "trace.cc", "transaction.cc", - "type_lookup_table.cc", + "var_handles.cc", "vdex_file.cc", "verifier/instruction_flags.cc", "verifier/method_verifier.cc", @@ -213,10 +206,8 @@ cc_defaults { "verifier/verifier_deps.cc", "verify_object.cc", "well_known_classes.cc", - "zip_archive.cc", "arch/context.cc", - "arch/instruction_set.cc", "arch/instruction_set_features.cc", "arch/memcmp16.cc", "arch/arm/instruction_set_features_arm.cc", @@ -248,9 +239,11 @@ cc_defaults { "entrypoints/quick/quick_trampoline_entrypoints.cc", ], + // b/77976998, clang lld does not recognize the --keep-unique flag. + use_clang_lld: false, + arch: { arm: { - clang_asflags: ["-no-integrated-as"], srcs: [ "interpreter/mterp/mterp.cc", "interpreter/mterp/out/mterp_arm.S", @@ -345,14 +338,10 @@ cc_defaults { "thread_android.cc", ], shared_libs: [ - // For android::FileMap used by libziparchive. - "libutils", "libtombstoned_client", ], static_libs: [ - // ZipArchive support, the order matters here to get all symbols. - "libziparchive", - "libz", + "libz", // For adler32. ], }, android_arm: { @@ -374,8 +363,7 @@ cc_defaults { "thread_linux.cc", ], shared_libs: [ - "libziparchive", - "libz", + "libz", // For adler32. ], }, }, @@ -402,7 +390,6 @@ cc_defaults { "libbacktrace", "liblz4", "liblog", - "libmetricslogger", // For atrace, properties, ashmem, set_sched_policy and socket_peer_is_trusted. "libcutils", // For common macros. @@ -426,8 +413,8 @@ gensrcs { cmd: "$(location generate_operator_out) art/runtime $(in) > $(out)", tools: ["generate_operator_out"], srcs: [ - "arch/instruction_set.h", "base/mutex.h", + "class_loader_context.h", "class_status.h", "debugger.h", "gc_root.h", @@ -454,6 +441,7 @@ gensrcs { "thread.h", "thread_state.h", "ti/agent.h", + "trace.h", "verifier/verifier_enums.h", ], output_extension: "operator_out.cc", @@ -471,10 +459,11 @@ art_cc_library { keep_symbols: true, }, whole_static_libs: [ - "libartbase", ], shared_libs: [ + "libartbase", "libdexfile", + "libprofile", ], export_shared_lib_headers: [ "libdexfile", @@ -495,10 +484,11 @@ art_cc_library { "libart_defaults", ], whole_static_libs: [ - "libartbased", ], shared_libs: [ + "libartbased", "libdexfiled", + "libprofiled", ], export_shared_lib_headers: [ "libdexfiled", @@ -514,6 +504,7 @@ art_cc_library { ], shared_libs: [ "libartd", + "libartbase-art-gtest", "libbase", "libbacktrace", ], @@ -532,7 +523,6 @@ art_cc_test { ], srcs: [ "arch/arch_test.cc", - "arch/instruction_set_test.cc", "arch/instruction_set_features_test.cc", "arch/memcmp16_test.cc", "arch/stub_test.cc", @@ -543,8 +533,6 @@ art_cc_test { "arch/x86/instruction_set_features_x86_test.cc", "arch/x86_64/instruction_set_features_x86_64_test.cc", "barrier_test.cc", - "base/arena_allocator_test.cc", - "base/file_utils_test.cc", "base/mutex_test.cc", "base/timing_logger_test.cc", "cha_test.cc", @@ -552,7 +540,6 @@ art_cc_test { "class_loader_context_test.cc", "class_table_test.cc", "compiler_filter_test.cc", - "dex/art_dex_file_loader_test.cc", "entrypoints/math_entrypoints_test.cc", "entrypoints/quick/quick_trampoline_entrypoints_test.cc", "entrypoints_order_test.cc", @@ -577,17 +564,14 @@ art_cc_test { "handle_scope_test.cc", "hidden_api_test.cc", "imtable_test.cc", - "indenter_test.cc", "indirect_reference_table_test.cc", "instrumentation_test.cc", "intern_table_test.cc", "interpreter/safe_math_test.cc", "interpreter/unstarted_runtime_test.cc", "jdwp/jdwp_options_test.cc", - "java_vm_ext_test.cc", - "jit/profile_compilation_info_test.cc", - "mem_map_test.cc", - "memory_region_test.cc", + "jit/profiling_info_test.cc", + "jni/java_vm_ext_test.cc", "method_handles_test.cc", "mirror/dex_cache_test.cc", "mirror/method_type_test.cc", @@ -605,15 +589,12 @@ art_cc_test { "subtype_check_test.cc", "thread_pool_test.cc", "transaction_test.cc", - "type_lookup_table_test.cc", "vdex_file_test.cc", "verifier/method_verifier_test.cc", "verifier/reg_type_test.cc", - "zip_archive_test.cc", ], shared_libs: [ "libbacktrace", - "libziparchive", ], header_libs: [ "art_cmdlineparser_headers", // For parsed_options_test. @@ -629,7 +610,7 @@ art_cc_test { "art_gtest_defaults", ], srcs: [ - "jni_internal_test.cc", + "jni/jni_internal_test.cc", "proxy_test.cc", "reflection_test.cc", ], diff --git a/runtime/arch/arch_test.cc b/runtime/arch/arch_test.cc index 1ba4070056c14a19f59612ca0fe4057899b5d59d..d4ceede07a799f9fb6d602d141a2ed9f8cffec8d 100644 --- a/runtime/arch/arch_test.cc +++ b/runtime/arch/arch_test.cc @@ -18,6 +18,7 @@ #include "art_method-inl.h" #include "base/callee_save_type.h" +#include "entrypoints/quick/callee_save_frame.h" #include "common_runtime_test.h" #include "quick/quick_method_frame_info.h" @@ -57,21 +58,6 @@ class ArchTest : public CommonRuntimeTest { void FinalizeSetup() OVERRIDE { ASSERT_EQ(InstructionSet::kX86_64, Runtime::Current()->GetInstructionSet()); } - - static void CheckFrameSize(InstructionSet isa, CalleeSaveType type, uint32_t save_size) - NO_THREAD_SAFETY_ANALYSIS { - Runtime* const runtime = Runtime::Current(); - Thread* const self = Thread::Current(); - ScopedObjectAccess soa(self); // So we can create callee-save methods. - - runtime->SetInstructionSet(isa); - ArtMethod* save_method = runtime->CreateCalleeSaveMethod(); - runtime->SetCalleeSaveMethod(save_method, type); - QuickMethodFrameInfo frame_info = runtime->GetRuntimeMethodFrameInfo(save_method); - EXPECT_EQ(frame_info.FrameSizeInBytes(), save_size) << "Expected and real size differs for " - << type << " core spills=" << std::hex << frame_info.CoreSpillMask() << " fp spills=" - << frame_info.FpSpillMask() << std::dec; - } }; TEST_F(ArchTest, CheckCommonOffsetsAndSizes) { @@ -205,26 +191,20 @@ static constexpr size_t kFrameSizeSaveEverything = FRAME_SIZE_SAVE_EVERYTHING; } // namespace x86_64 // Check architecture specific constants are sound. -#define TEST_ARCH(Arch, arch) \ - TEST_F(ArchTest, Arch) { \ - CheckFrameSize(InstructionSet::k##Arch, \ - CalleeSaveType::kSaveAllCalleeSaves, \ - arch::kFrameSizeSaveAllCalleeSaves); \ - CheckFrameSize(InstructionSet::k##Arch, \ - CalleeSaveType::kSaveRefsOnly, \ - arch::kFrameSizeSaveRefsOnly); \ - CheckFrameSize(InstructionSet::k##Arch, \ - CalleeSaveType::kSaveRefsAndArgs, \ - arch::kFrameSizeSaveRefsAndArgs); \ - CheckFrameSize(InstructionSet::k##Arch, \ - CalleeSaveType::kSaveEverything, \ - arch::kFrameSizeSaveEverything); \ - CheckFrameSize(InstructionSet::k##Arch, \ - CalleeSaveType::kSaveEverythingForClinit, \ - arch::kFrameSizeSaveEverythingForClinit); \ - CheckFrameSize(InstructionSet::k##Arch, \ - CalleeSaveType::kSaveEverythingForSuspendCheck, \ - arch::kFrameSizeSaveEverythingForSuspendCheck); \ +// We expect the return PC to be stored at the highest address slot in the frame. +#define TEST_ARCH_TYPE(Arch, arch, type) \ + EXPECT_EQ(arch::Arch##CalleeSaveFrame::GetFrameSize(CalleeSaveType::k##type), \ + arch::kFrameSize##type); \ + EXPECT_EQ(arch::Arch##CalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::k##type), \ + arch::kFrameSize##type - static_cast(k##Arch##PointerSize)) +#define TEST_ARCH(Arch, arch) \ + TEST_F(ArchTest, Arch) { \ + TEST_ARCH_TYPE(Arch, arch, SaveAllCalleeSaves); \ + TEST_ARCH_TYPE(Arch, arch, SaveRefsOnly); \ + TEST_ARCH_TYPE(Arch, arch, SaveRefsAndArgs); \ + TEST_ARCH_TYPE(Arch, arch, SaveEverything); \ + TEST_ARCH_TYPE(Arch, arch, SaveEverythingForClinit); \ + TEST_ARCH_TYPE(Arch, arch, SaveEverythingForSuspendCheck); \ } TEST_ARCH(Arm, arm) TEST_ARCH(Arm64, arm64) diff --git a/runtime/arch/arm/asm_support_arm.h b/runtime/arch/arm/asm_support_arm.h index ac17303cf9a6c732b7ec871722c3022340adc255..7123ae73b40bef14e85cdf483e6569ecdf632df8 100644 --- a/runtime/arch/arm/asm_support_arm.h +++ b/runtime/arch/arm/asm_support_arm.h @@ -32,8 +32,8 @@ #define BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET 0x20 // The offsets from art_quick_read_barrier_mark_introspection to the GC root entrypoints, // i.e. art_quick_read_barrier_mark_introspection_gc_roots_{wide,narrow}. -#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET 0x80 -#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET 0xc0 +#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET 0xc0 +#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET 0xe0 // The offset from art_quick_read_barrier_mark_introspection to the array switch cases, // i.e. art_quick_read_barrier_mark_introspection_arrays. #define BAKER_MARK_INTROSPECTION_ARRAY_SWITCH_OFFSET 0x100 diff --git a/runtime/arch/arm/quick_method_frame_info_arm.h b/runtime/arch/arm/callee_save_frame_arm.h similarity index 52% rename from runtime/arch/arm/quick_method_frame_info_arm.h rename to runtime/arch/arm/callee_save_frame_arm.h index 5c5b81baaef9e84126827ccf40297582ca928d97..11eefb92838f1d3c1a73fb35139c7ca934d4cf6f 100644 --- a/runtime/arch/arm/quick_method_frame_info_arm.h +++ b/runtime/arch/arm/callee_save_frame_arm.h @@ -14,13 +14,14 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_ARCH_ARM_QUICK_METHOD_FRAME_INFO_ARM_H_ -#define ART_RUNTIME_ARCH_ARM_QUICK_METHOD_FRAME_INFO_ARM_H_ +#ifndef ART_RUNTIME_ARCH_ARM_CALLEE_SAVE_FRAME_ARM_H_ +#define ART_RUNTIME_ARCH_ARM_CALLEE_SAVE_FRAME_ARM_H_ #include "arch/instruction_set.h" #include "base/bit_utils.h" #include "base/callee_save_type.h" #include "base/enums.h" +#include "base/globals.h" #include "quick/quick_method_frame_info.h" #include "registers_arm.h" @@ -55,56 +56,56 @@ static constexpr uint32_t kArmCalleeSaveFpAllSpills = static constexpr uint32_t kArmCalleeSaveFpEverythingSpills = kArmCalleeSaveFpArgSpills | kArmCalleeSaveFpAllSpills; -constexpr uint32_t ArmCalleeSaveCoreSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kArmCalleeSaveAlwaysSpills | kArmCalleeSaveRefSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kArmCalleeSaveArgSpills : 0) | - (type == CalleeSaveType::kSaveAllCalleeSaves ? kArmCalleeSaveAllSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kArmCalleeSaveEverythingSpills : 0); -} +class ArmCalleeSaveFrame { + public: + static constexpr uint32_t GetCoreSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kArmCalleeSaveAlwaysSpills | kArmCalleeSaveRefSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kArmCalleeSaveArgSpills : 0) | + (type == CalleeSaveType::kSaveAllCalleeSaves ? kArmCalleeSaveAllSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kArmCalleeSaveEverythingSpills : 0); + } -constexpr uint32_t ArmCalleeSaveFpSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kArmCalleeSaveFpAlwaysSpills | kArmCalleeSaveFpRefSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kArmCalleeSaveFpArgSpills : 0) | - (type == CalleeSaveType::kSaveAllCalleeSaves ? kArmCalleeSaveFpAllSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kArmCalleeSaveFpEverythingSpills : 0); -} + static constexpr uint32_t GetFpSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kArmCalleeSaveFpAlwaysSpills | kArmCalleeSaveFpRefSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kArmCalleeSaveFpArgSpills : 0) | + (type == CalleeSaveType::kSaveAllCalleeSaves ? kArmCalleeSaveFpAllSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kArmCalleeSaveFpEverythingSpills : 0); + } -constexpr uint32_t ArmCalleeSaveFrameSize(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return RoundUp((POPCOUNT(ArmCalleeSaveCoreSpills(type)) /* gprs */ + - POPCOUNT(ArmCalleeSaveFpSpills(type)) /* fprs */ + - 1 /* Method* */) * static_cast(kArmPointerSize), kStackAlignment); -} + static constexpr uint32_t GetFrameSize(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return RoundUp((POPCOUNT(GetCoreSpills(type)) /* gprs */ + + POPCOUNT(GetFpSpills(type)) /* fprs */ + + 1 /* Method* */) * static_cast(kArmPointerSize), kStackAlignment); + } -constexpr QuickMethodFrameInfo ArmCalleeSaveMethodFrameInfo(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return QuickMethodFrameInfo(ArmCalleeSaveFrameSize(type), - ArmCalleeSaveCoreSpills(type), - ArmCalleeSaveFpSpills(type)); -} + static constexpr QuickMethodFrameInfo GetMethodFrameInfo(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return QuickMethodFrameInfo(GetFrameSize(type), GetCoreSpills(type), GetFpSpills(type)); + } -constexpr size_t ArmCalleeSaveFpr1Offset(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return ArmCalleeSaveFrameSize(type) - - (POPCOUNT(ArmCalleeSaveCoreSpills(type)) + - POPCOUNT(ArmCalleeSaveFpSpills(type))) * static_cast(kArmPointerSize); -} + static constexpr size_t GetFpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + (POPCOUNT(GetCoreSpills(type)) + + POPCOUNT(GetFpSpills(type))) * static_cast(kArmPointerSize); + } -constexpr size_t ArmCalleeSaveGpr1Offset(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return ArmCalleeSaveFrameSize(type) - - POPCOUNT(ArmCalleeSaveCoreSpills(type)) * static_cast(kArmPointerSize); -} + static constexpr size_t GetGpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + POPCOUNT(GetCoreSpills(type)) * static_cast(kArmPointerSize); + } -constexpr size_t ArmCalleeSaveLrOffset(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return ArmCalleeSaveFrameSize(type) - - POPCOUNT(ArmCalleeSaveCoreSpills(type) & (-(1 << LR))) * static_cast(kArmPointerSize); -} + static constexpr size_t GetReturnPcOffset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - static_cast(kArmPointerSize); + } +}; } // namespace arm } // namespace art -#endif // ART_RUNTIME_ARCH_ARM_QUICK_METHOD_FRAME_INFO_ARM_H_ +#endif // ART_RUNTIME_ARCH_ARM_CALLEE_SAVE_FRAME_ARM_H_ diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc index 80080e98329d4cc02f4886c086f8182b19a46ab7..b4e90360843f945c8043142768da4b1d5f22a681 100644 --- a/runtime/arch/arm/entrypoints_init_arm.cc +++ b/runtime/arch/arm/entrypoints_init_arm.cc @@ -90,30 +90,34 @@ void UpdateReadBarrierEntrypoints(QuickEntryPoints* qpoints, bool is_active) { qpoints->pReadBarrierMarkReg10 = is_active ? art_quick_read_barrier_mark_reg10 : nullptr; qpoints->pReadBarrierMarkReg11 = is_active ? art_quick_read_barrier_mark_reg11 : nullptr; - // For the alignment check, strip the Thumb mode bit. - DCHECK_ALIGNED(reinterpret_cast(art_quick_read_barrier_mark_introspection) - 1u, 256u); - // Check the field narrow entrypoint offset from the introspection entrypoint. - intptr_t narrow_diff = - reinterpret_cast(art_quick_read_barrier_mark_introspection_narrow) - - reinterpret_cast(art_quick_read_barrier_mark_introspection); - DCHECK_EQ(BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET, narrow_diff); - // Check array switch cases offsets from the introspection entrypoint. - intptr_t array_diff = - reinterpret_cast(art_quick_read_barrier_mark_introspection_arrays) - - reinterpret_cast(art_quick_read_barrier_mark_introspection); - DCHECK_EQ(BAKER_MARK_INTROSPECTION_ARRAY_SWITCH_OFFSET, array_diff); - // Check the GC root entrypoint offsets from the introspection entrypoint. - intptr_t gc_roots_wide_diff = - reinterpret_cast(art_quick_read_barrier_mark_introspection_gc_roots_wide) - - reinterpret_cast(art_quick_read_barrier_mark_introspection); - DCHECK_EQ(BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET, gc_roots_wide_diff); - intptr_t gc_roots_narrow_diff = - reinterpret_cast(art_quick_read_barrier_mark_introspection_gc_roots_narrow) - - reinterpret_cast(art_quick_read_barrier_mark_introspection); - DCHECK_EQ(BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET, gc_roots_narrow_diff); - // The register 12, i.e. IP, is reserved, so there is no art_quick_read_barrier_mark_reg12. - // We're using the entry to hold a pointer to the introspection entrypoint instead. - qpoints->pReadBarrierMarkReg12 = is_active ? art_quick_read_barrier_mark_introspection : nullptr; + if (kUseReadBarrier && kUseBakerReadBarrier) { + // For the alignment check, strip the Thumb mode bit. + DCHECK_ALIGNED(reinterpret_cast(art_quick_read_barrier_mark_introspection) - 1u, + 256u); + // Check the field narrow entrypoint offset from the introspection entrypoint. + intptr_t narrow_diff = + reinterpret_cast(art_quick_read_barrier_mark_introspection_narrow) - + reinterpret_cast(art_quick_read_barrier_mark_introspection); + DCHECK_EQ(BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET, narrow_diff); + // Check array switch cases offsets from the introspection entrypoint. + intptr_t array_diff = + reinterpret_cast(art_quick_read_barrier_mark_introspection_arrays) - + reinterpret_cast(art_quick_read_barrier_mark_introspection); + DCHECK_EQ(BAKER_MARK_INTROSPECTION_ARRAY_SWITCH_OFFSET, array_diff); + // Check the GC root entrypoint offsets from the introspection entrypoint. + intptr_t gc_roots_wide_diff = + reinterpret_cast(art_quick_read_barrier_mark_introspection_gc_roots_wide) - + reinterpret_cast(art_quick_read_barrier_mark_introspection); + DCHECK_EQ(BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET, gc_roots_wide_diff); + intptr_t gc_roots_narrow_diff = + reinterpret_cast(art_quick_read_barrier_mark_introspection_gc_roots_narrow) - + reinterpret_cast(art_quick_read_barrier_mark_introspection); + DCHECK_EQ(BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET, gc_roots_narrow_diff); + // The register 12, i.e. IP, is reserved, so there is no art_quick_read_barrier_mark_reg12. + // We're using the entry to hold a pointer to the introspection entrypoint instead. + qpoints->pReadBarrierMarkReg12 = + is_active ? art_quick_read_barrier_mark_introspection : nullptr; + } } void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc index 315bf957ccaa0de6b1b60143898d7d507759f434..bb33a273b895cd3ed3f4acceab10b70557fca290 100644 --- a/runtime/arch/arm/fault_handler_arm.cc +++ b/runtime/arch/arm/fault_handler_arm.cc @@ -20,10 +20,10 @@ #include "art_method.h" #include "base/enums.h" +#include "base/globals.h" #include "base/hex_dump.h" #include "base/logging.h" // For VLOG. #include "base/macros.h" -#include "globals.h" #include "thread-current-inl.h" // diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 98214fb6848c9a2d2b0fac64e115b01013f34406..96d7c9629d53ecfe495e6552ae8b883678fd7d5b 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -55,7 +55,7 @@ @ Load kSaveAllCalleeSaves Method* into rTemp. ldr \rTemp, [\rTemp, #RUNTIME_SAVE_ALL_CALLEE_SAVES_METHOD_OFFSET] str \rTemp, [sp, #0] @ Place Method* at bottom of stack. - str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. + str sp, [rSELF, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. // Ugly compile-time check, but we only have the preprocessor. #if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVES != 36 + 64 + 12) @@ -86,7 +86,7 @@ @ Load kSaveRefsOnly Method* into rTemp. ldr \rTemp, [\rTemp, #RUNTIME_SAVE_REFS_ONLY_METHOD_OFFSET] str \rTemp, [sp, #0] @ Place Method* at bottom of stack. - str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. + str sp, [rSELF, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. // Ugly compile-time check, but we only have the preprocessor. #if (FRAME_SIZE_SAVE_REFS_ONLY != 28 + 4) @@ -147,13 +147,13 @@ @ Load kSaveRefsAndArgs Method* into rTemp. ldr \rTemp, [\rTemp, #RUNTIME_SAVE_REFS_AND_ARGS_METHOD_OFFSET] str \rTemp, [sp, #0] @ Place Method* at bottom of stack. - str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. + str sp, [rSELF, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. .endm .macro SETUP_SAVE_REFS_AND_ARGS_FRAME_WITH_METHOD_IN_R0 SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY str r0, [sp, #0] @ Store ArtMethod* to bottom of stack. - str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. + str sp, [rSELF, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. .endm .macro RESTORE_SAVE_REFS_AND_ARGS_FRAME @@ -193,7 +193,7 @@ @ Load kSaveEverything Method* into rTemp. ldr \rTemp, [\rTemp, #\runtime_method_offset] str \rTemp, [sp, #0] @ Place Method* at bottom of stack. - str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. + str sp, [rSELF, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. // Ugly compile-time check, but we only have the preprocessor. #if (FRAME_SIZE_SAVE_EVERYTHING != 56 + 128 + 8) @@ -301,7 +301,7 @@ * exception is Thread::Current()->exception_ when the runtime method frame is ready. */ .macro DELIVER_PENDING_EXCEPTION_FRAME_READY - mov r0, r9 @ pass Thread::Current + mov r0, rSELF @ pass Thread::Current bl artDeliverPendingExceptionFromCode @ artDeliverPendingExceptionFromCode(Thread*) .endm @@ -318,7 +318,7 @@ .extern \cxx_name ENTRY \c_name SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r0 @ save all registers as basis for long jump context - mov r0, r9 @ pass Thread::Current + mov r0, rSELF @ pass Thread::Current bl \cxx_name @ \cxx_name(Thread*) END \c_name .endm @@ -327,7 +327,7 @@ END \c_name .extern \cxx_name ENTRY \c_name SETUP_SAVE_EVERYTHING_FRAME r0 @ save all registers as basis for long jump context - mov r0, r9 @ pass Thread::Current + mov r0, rSELF @ pass Thread::Current bl \cxx_name @ \cxx_name(Thread*) END \c_name .endm @@ -336,7 +336,7 @@ END \c_name .extern \cxx_name ENTRY \c_name SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r1 @ save all registers as basis for long jump context - mov r1, r9 @ pass Thread::Current + mov r1, rSELF @ pass Thread::Current bl \cxx_name @ \cxx_name(Thread*) END \c_name .endm @@ -345,13 +345,13 @@ END \c_name .extern \cxx_name ENTRY \c_name SETUP_SAVE_EVERYTHING_FRAME r2 @ save all registers as basis for long jump context - mov r2, r9 @ pass Thread::Current + mov r2, rSELF @ pass Thread::Current bl \cxx_name @ \cxx_name(Thread*) END \c_name .endm .macro RETURN_OR_DELIVER_PENDING_EXCEPTION_REG reg - ldr \reg, [r9, #THREAD_EXCEPTION_OFFSET] // Get exception field. + ldr \reg, [rSELF, #THREAD_EXCEPTION_OFFSET] @ Get exception field. cbnz \reg, 1f bx lr 1: @@ -377,7 +377,7 @@ END \c_name .extern \entrypoint ENTRY \name SETUP_SAVE_REFS_ONLY_FRAME r1 @ save callee saves in case of GC - mov r1, r9 @ pass Thread::Current + mov r1, rSELF @ pass Thread::Current bl \entrypoint @ (uint32_t field_idx, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER @@ -389,7 +389,7 @@ END \name .extern \entrypoint ENTRY \name SETUP_SAVE_REFS_ONLY_FRAME r2 @ save callee saves in case of GC - mov r2, r9 @ pass Thread::Current + mov r2, rSELF @ pass Thread::Current bl \entrypoint @ (field_idx, Object*, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER @@ -401,7 +401,7 @@ END \name .extern \entrypoint ENTRY \name SETUP_SAVE_REFS_ONLY_FRAME r3 @ save callee saves in case of GC - mov r3, r9 @ pass Thread::Current + mov r3, rSELF @ pass Thread::Current bl \entrypoint @ (field_idx, Object*, new_val, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME @ TODO: we can clearly save an add here REFRESH_MARKING_REGISTER @@ -448,7 +448,7 @@ ENTRY art_quick_throw_null_pointer_exception_from_signal @ save all registers as basis for long jump context SETUP_SAVE_EVERYTHING_FRAME_CORE_REGS_SAVED r1 mov r0, lr @ pass the fault address stored in LR by the fault handler. - mov r1, r9 @ pass Thread::Current + mov r1, rSELF @ pass Thread::Current bl artThrowNullPointerExceptionFromSignal @ (Thread*) END art_quick_throw_null_pointer_exception_from_signal @@ -494,7 +494,7 @@ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFr .macro INVOKE_TRAMPOLINE_BODY cxx_name .extern \cxx_name SETUP_SAVE_REFS_AND_ARGS_FRAME r2 @ save callee saves in case allocation triggers GC - mov r2, r9 @ pass Thread::Current + mov r2, rSELF @ pass Thread::Current mov r3, sp bl \cxx_name @ (method_idx, this, Thread*, SP) mov r12, r1 @ save Method*->code_ @@ -682,50 +682,48 @@ TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, */ .extern artLockObjectFromCode ENTRY art_quick_lock_object + ldr r1, [rSELF, #THREAD_ID_OFFSET] cbz r0, .Lslow_lock .Lretry_lock: - ldr r2, [r9, #THREAD_ID_OFFSET] - ldrex r1, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] - mov r3, r1 - and r3, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED @ zero the gc bits - cbnz r3, .Lnot_unlocked @ already thin locked - @ unlocked case - r1: original lock word that's zero except for the read barrier bits. - orr r2, r1, r2 @ r2 holds thread id with count of 0 with preserved read barrier bits - strex r3, r2, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] - cbnz r3, .Llock_strex_fail @ store failed, retry - dmb ish @ full (LoadLoad|LoadStore) memory barrier + ldrex r2, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] + eor r3, r2, r1 @ Prepare the value to store if unlocked + @ (thread id, count of 0 and preserved read barrier bits), + @ or prepare to compare thread id for recursive lock check + @ (lock_word.ThreadId() ^ self->ThreadId()). + ands ip, r2, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED @ Test the non-gc bits. + bne .Lnot_unlocked @ Check if unlocked. + @ unlocked case - store r3: original lock word plus thread id, preserved read barrier bits. + strex r2, r3, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] + cbnz r2, .Llock_strex_fail @ If store failed, retry. + dmb ish @ Full (LoadLoad|LoadStore) memory barrier. bx lr -.Lnot_unlocked: @ r1: original lock word, r2: thread_id with count of 0 and zero read barrier bits - lsr r3, r1, LOCK_WORD_STATE_SHIFT - cbnz r3, .Lslow_lock @ if either of the top two bits are set, go slow path - eor r2, r1, r2 @ lock_word.ThreadId() ^ self->ThreadId() - uxth r2, r2 @ zero top 16 bits - cbnz r2, .Lslow_lock @ lock word and self thread id's match -> recursive lock - @ else contention, go to slow path - mov r3, r1 @ copy the lock word to check count overflow. - and r3, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED @ zero the gc bits. - add r2, r3, #LOCK_WORD_THIN_LOCK_COUNT_ONE @ increment count in lock word placing in r2 to check overflow - lsr r3, r2, #LOCK_WORD_GC_STATE_SHIFT @ if the first gc state bit is set, we overflowed. - cbnz r3, .Lslow_lock @ if we overflow the count go slow path - add r2, r1, #LOCK_WORD_THIN_LOCK_COUNT_ONE @ increment count for real - strex r3, r2, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] @ strex necessary for read barrier bits - cbnz r3, .Llock_strex_fail @ strex failed, retry +.Lnot_unlocked: @ r2: original lock word, r1: thread_id, r3: r2 ^ r1 +#if LOCK_WORD_THIN_LOCK_COUNT_SHIFT + LOCK_WORD_THIN_LOCK_COUNT_SIZE != LOCK_WORD_GC_STATE_SHIFT +#error "Expecting thin lock count and gc state in consecutive bits." +#endif + @ Check lock word state and thread id together, + bfc r3, #LOCK_WORD_THIN_LOCK_COUNT_SHIFT, #(LOCK_WORD_THIN_LOCK_COUNT_SIZE + LOCK_WORD_GC_STATE_SIZE) + cbnz r3, .Lslow_lock @ if either of the top two bits are set, or the lock word's + @ thread id did not match, go slow path. + add r3, r2, #LOCK_WORD_THIN_LOCK_COUNT_ONE @ Increment the recursive lock count. + @ Extract the new thin lock count for overflow check. + ubfx r2, r3, #LOCK_WORD_THIN_LOCK_COUNT_SHIFT, #LOCK_WORD_THIN_LOCK_COUNT_SIZE + cbz r2, .Lslow_lock @ Zero as the new count indicates overflow, go slow path. + strex r2, r3, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] @ strex necessary for read barrier bits. + cbnz r2, .Llock_strex_fail @ If strex failed, retry. bx lr .Llock_strex_fail: b .Lretry_lock @ retry -.Lslow_lock: - SETUP_SAVE_REFS_ONLY_FRAME r1 @ save callee saves in case we block - mov r1, r9 @ pass Thread::Current - bl artLockObjectFromCode @ (Object* obj, Thread*) - RESTORE_SAVE_REFS_ONLY_FRAME - REFRESH_MARKING_REGISTER - RETURN_IF_RESULT_IS_ZERO - DELIVER_PENDING_EXCEPTION +// Note: the slow path is actually the art_quick_lock_object_no_inline (tail call). END art_quick_lock_object ENTRY art_quick_lock_object_no_inline + // This is also the slow path for art_quick_lock_object. Note that we + // need a local label, the assembler complains about target being out of + // range if we try to jump to `art_quick_lock_object_no_inline`. +.Lslow_lock: SETUP_SAVE_REFS_ONLY_FRAME r1 @ save callee saves in case we block - mov r1, r9 @ pass Thread::Current + mov r1, rSELF @ pass Thread::Current bl artLockObjectFromCode @ (Object* obj, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER @@ -739,62 +737,59 @@ END art_quick_lock_object_no_inline */ .extern artUnlockObjectFromCode ENTRY art_quick_unlock_object + ldr r1, [rSELF, #THREAD_ID_OFFSET] cbz r0, .Lslow_unlock .Lretry_unlock: #ifndef USE_READ_BARRIER - ldr r1, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] + ldr r2, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] #else - ldrex r1, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] @ Need to use atomic instructions for read barrier + @ Need to use atomic instructions for read barrier. + ldrex r2, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] #endif - lsr r2, r1, #LOCK_WORD_STATE_SHIFT - cbnz r2, .Lslow_unlock @ if either of the top two bits are set, go slow path - ldr r2, [r9, #THREAD_ID_OFFSET] - mov r3, r1 @ copy lock word to check thread id equality - and r3, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED @ zero the gc bits - eor r3, r3, r2 @ lock_word.ThreadId() ^ self->ThreadId() - uxth r3, r3 @ zero top 16 bits - cbnz r3, .Lslow_unlock @ do lock word and self thread id's match? - mov r3, r1 @ copy lock word to detect transition to unlocked - and r3, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED @ zero the gc bits - cmp r3, #LOCK_WORD_THIN_LOCK_COUNT_ONE - bpl .Lrecursive_thin_unlock - @ transition to unlocked - mov r3, r1 - and r3, #LOCK_WORD_GC_STATE_MASK_SHIFTED @ r3: zero except for the preserved gc bits - dmb ish @ full (LoadStore|StoreStore) memory barrier + eor r3, r2, r1 @ Prepare the value to store if simply locked + @ (mostly 0s, and preserved read barrier bits), + @ or prepare to compare thread id for recursive lock check + @ (lock_word.ThreadId() ^ self->ThreadId()). + ands ip, r3, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED @ Test the non-gc bits. + bne .Lnot_simply_locked @ Locked recursively or by other thread? + @ Transition to unlocked. + dmb ish @ Full (LoadStore|StoreStore) memory barrier. #ifndef USE_READ_BARRIER str r3, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] #else strex r2, r3, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] @ strex necessary for read barrier bits - cbnz r2, .Lunlock_strex_fail @ store failed, retry + cbnz r2, .Lunlock_strex_fail @ If the store failed, retry. #endif bx lr -.Lrecursive_thin_unlock: @ r1: original lock word - sub r1, r1, #LOCK_WORD_THIN_LOCK_COUNT_ONE @ decrement count +.Lnot_simply_locked: @ r2: original lock word, r1: thread_id, r3: r2 ^ r1 +#if LOCK_WORD_THIN_LOCK_COUNT_SHIFT + LOCK_WORD_THIN_LOCK_COUNT_SIZE != LOCK_WORD_GC_STATE_SHIFT +#error "Expecting thin lock count and gc state in consecutive bits." +#endif + @ Check lock word state and thread id together, + bfc r3, #LOCK_WORD_THIN_LOCK_COUNT_SHIFT, #(LOCK_WORD_THIN_LOCK_COUNT_SIZE + LOCK_WORD_GC_STATE_SIZE) + cbnz r3, .Lslow_unlock @ if either of the top two bits are set, or the lock word's + @ thread id did not match, go slow path. + sub r3, r2, #LOCK_WORD_THIN_LOCK_COUNT_ONE @ Decrement recursive lock count. #ifndef USE_READ_BARRIER - str r1, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] + str r3, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] #else - strex r2, r1, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] @ strex necessary for read barrier bits - cbnz r2, .Lunlock_strex_fail @ store failed, retry + strex r2, r3, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] @ strex necessary for read barrier bits. + cbnz r2, .Lunlock_strex_fail @ If the store failed, retry. #endif bx lr .Lunlock_strex_fail: b .Lretry_unlock @ retry -.Lslow_unlock: - @ save callee saves in case exception allocation triggers GC - SETUP_SAVE_REFS_ONLY_FRAME r1 - mov r1, r9 @ pass Thread::Current - bl artUnlockObjectFromCode @ (Object* obj, Thread*) - RESTORE_SAVE_REFS_ONLY_FRAME - REFRESH_MARKING_REGISTER - RETURN_IF_RESULT_IS_ZERO - DELIVER_PENDING_EXCEPTION +// Note: the slow path is actually the art_quick_unlock_object_no_inline (tail call). END art_quick_unlock_object ENTRY art_quick_unlock_object_no_inline + // This is also the slow path for art_quick_unlock_object. Note that we + // need a local label, the assembler complains about target being out of + // range if we try to jump to `art_quick_unlock_object_no_inline`. +.Lslow_unlock: @ save callee saves in case exception allocation triggers GC SETUP_SAVE_REFS_ONLY_FRAME r1 - mov r1, r9 @ pass Thread::Current + mov r1, rSELF @ pass Thread::Current bl artUnlockObjectFromCode @ (Object* obj, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER @@ -809,6 +804,9 @@ END art_quick_unlock_object_no_inline .extern artInstanceOfFromCode .extern artThrowClassCastExceptionForObject ENTRY art_quick_check_instance_of + // Type check using the bit string passes null as the target class. In that case just throw. + cbz r1, .Lthrow_class_cast_exception_for_bitstring_check + push {r0-r2, lr} @ save arguments, padding (r2) and link register .cfi_adjust_cfa_offset 16 .cfi_rel_offset r0, 0 @@ -827,8 +825,9 @@ ENTRY art_quick_check_instance_of .cfi_restore r2 .cfi_restore lr +.Lthrow_class_cast_exception_for_bitstring_check: SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r2 @ save all registers as basis for long jump context - mov r2, r9 @ pass Thread::Current + mov r2, rSELF @ pass Thread::Current bl artThrowClassCastExceptionForObject @ (Object*, Class*, Thread*) bkpt END art_quick_check_instance_of @@ -913,7 +912,7 @@ ENTRY art_quick_aput_obj add r3, r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET POISON_HEAP_REF r2 str r2, [r3, r1, lsl #2] - ldr r3, [r9, #THREAD_CARD_TABLE_OFFSET] + ldr r3, [rSELF, #THREAD_CARD_TABLE_OFFSET] lsr r0, r0, #CARD_TABLE_CARD_SHIFT strb r3, [r3, r0] blx lr @@ -941,7 +940,7 @@ ENTRY art_quick_aput_obj add r3, r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET POISON_HEAP_REF r2 str r2, [r3, r1, lsl #2] - ldr r3, [r9, #THREAD_CARD_TABLE_OFFSET] + ldr r3, [rSELF, #THREAD_CARD_TABLE_OFFSET] lsr r0, r0, #CARD_TABLE_CARD_SHIFT strb r3, [r3, r0] blx lr @@ -950,7 +949,7 @@ ENTRY art_quick_aput_obj /* No need to repeat restore cfi directives, the ones above apply here. */ SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r3 mov r1, r2 - mov r2, r9 @ pass Thread::Current + mov r2, rSELF @ pass Thread::Current bl artThrowArrayStoreException @ (Class*, Class*, Thread*) bkpt @ unreached END art_quick_aput_obj @@ -960,7 +959,7 @@ END art_quick_aput_obj .extern \entrypoint ENTRY \name SETUP_SAVE_REFS_ONLY_FRAME r1 @ save callee saves in case of GC - mov r1, r9 @ pass Thread::Current + mov r1, rSELF @ pass Thread::Current bl \entrypoint @ (uint32_t type_idx, Method* method, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER @@ -973,7 +972,7 @@ END \name .extern \entrypoint ENTRY \name SETUP_SAVE_REFS_ONLY_FRAME r2 @ save callee saves in case of GC - mov r2, r9 @ pass Thread::Current + mov r2, rSELF @ pass Thread::Current bl \entrypoint @ (uint32_t type_idx, Method* method, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER @@ -986,7 +985,7 @@ END \name .extern \entrypoint ENTRY \name SETUP_SAVE_REFS_ONLY_FRAME r3 @ save callee saves in case of GC - mov r3, r9 @ pass Thread::Current + mov r3, rSELF @ pass Thread::Current @ (uint32_t type_idx, Method* method, int32_t component_count, Thread*) bl \entrypoint RESTORE_SAVE_REFS_ONLY_FRAME @@ -1000,7 +999,7 @@ END \name .extern \entrypoint ENTRY \name SETUP_SAVE_REFS_ONLY_FRAME r12 @ save callee saves in case of GC - str r9, [sp, #-16]! @ expand the frame and pass Thread::Current + str rSELF, [sp, #-16]! @ expand the frame and pass Thread::Current .cfi_adjust_cfa_offset 16 bl \entrypoint add sp, #16 @ strip the extra frame @@ -1011,12 +1010,15 @@ ENTRY \name END \name .endm -// Macro for string and type resolution and initialization. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. + */ .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET .extern \entrypoint ENTRY \name SETUP_SAVE_EVERYTHING_FRAME r1, \runtime_method_offset @ save everything in case of GC - mov r1, r9 @ pass Thread::Current + mov r1, rSELF @ pass Thread::Current bl \entrypoint @ (uint32_t index, Thread*) cbz r0, 1f @ If result is null, deliver the OOME. .cfi_remember_state @@ -1036,6 +1038,8 @@ END \name ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode // Note: Functions `art{Get,Set}{Static,Instance}FromCompiledCode` are @@ -1056,9 +1060,9 @@ ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCompiledCode, .extern artGet64StaticFromCompiledCode ENTRY art_quick_get64_static SETUP_SAVE_REFS_ONLY_FRAME r2 @ save callee saves in case of GC - mov r1, r9 @ pass Thread::Current - bl artGet64StaticFromCompiledCode @ (uint32_t field_idx, Thread*) - ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ + mov r1, rSELF @ pass Thread::Current + bl artGet64StaticFromCompiledCode @ (uint32_t field_idx, Thread*) + ldr r2, [rSELF, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER cbnz r2, 1f @ success if no exception pending @@ -1082,9 +1086,9 @@ TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCompiledCo .extern artGet64InstanceFromCompiledCode ENTRY art_quick_get64_instance SETUP_SAVE_REFS_ONLY_FRAME r2 @ save callee saves in case of GC - mov r2, r9 @ pass Thread::Current - bl artGet64InstanceFromCompiledCode @ (field_idx, Object*, Thread*) - ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ + mov r2, rSELF @ pass Thread::Current + bl artGet64InstanceFromCompiledCode @ (field_idx, Object*, Thread*) + ldr r2, [rSELF, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER cbnz r2, 1f @ success if no exception pending @@ -1116,7 +1120,7 @@ THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCompiled ENTRY art_quick_set64_instance SETUP_SAVE_REFS_ONLY_FRAME r12 @ save callee saves in case of GC @ r2:r3 contain the wide argument - str r9, [sp, #-16]! @ expand the frame and pass Thread::Current + str rSELF, [sp, #-16]! @ expand the frame and pass Thread::Current .cfi_adjust_cfa_offset 16 bl artSet64InstanceFromCompiledCode @ (field_idx, Object*, new_val, Thread*) add sp, #16 @ release out args @@ -1131,7 +1135,7 @@ END art_quick_set64_instance ENTRY art_quick_set64_static SETUP_SAVE_REFS_ONLY_FRAME r12 @ save callee saves in case of GC @ r2:r3 contain the wide argument - str r9, [sp, #-16]! @ expand the frame and pass Thread::Current + str rSELF, [sp, #-16]! @ expand the frame and pass Thread::Current .cfi_adjust_cfa_offset 16 bl artSet64StaticFromCompiledCode @ (field_idx, new_val, Thread*) add sp, #16 @ release out args @@ -1148,6 +1152,7 @@ GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_region_tlab, RegionTLAB) @@ -1160,6 +1165,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_tlab, TLAB) @@ -1176,12 +1182,12 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB) .macro ART_QUICK_ALLOC_OBJECT_ROSALLOC c_name, cxx_name, isInitialized ENTRY \c_name // Fast path rosalloc allocation. - // r0: type/return value, r9: Thread::Current + // r0: type/return value, rSELF (r9): Thread::Current // r1, r2, r3, r12: free. - ldr r3, [r9, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET] // Check if the thread local + ldr r3, [rSELF, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET] // Check if the thread local // allocation stack has room. // TODO: consider using ldrd. - ldr r12, [r9, #THREAD_LOCAL_ALLOC_STACK_END_OFFSET] + ldr r12, [rSELF, #THREAD_LOCAL_ALLOC_STACK_END_OFFSET] cmp r3, r12 bhs .Lslow_path\c_name @@ -1199,7 +1205,7 @@ ENTRY \c_name // from the size. Since the size is // already aligned we can combine the // two shifts together. - add r12, r9, r3, lsr #(ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT - POINTER_SIZE_SHIFT) + add r12, rSELF, r3, lsr #(ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT - POINTER_SIZE_SHIFT) // Subtract pointer size since ther // are no runs for 0 byte allocations // and the size is already aligned. @@ -1227,9 +1233,9 @@ ENTRY \c_name // local allocation stack and // increment the thread local // allocation stack top. - ldr r1, [r9, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET] + ldr r1, [rSELF, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET] str r3, [r1], #COMPRESSED_REFERENCE_SIZE // (Increment r1 as a side effect.) - str r1, [r9, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET] + str r1, [rSELF, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET] // Decrement the size of the free list // After this "STR" the object is published to the thread local allocation stack, @@ -1278,7 +1284,7 @@ ENTRY \c_name .Lslow_path\c_name: SETUP_SAVE_REFS_ONLY_FRAME r2 @ save callee saves in case of GC - mov r1, r9 @ pass Thread::Current + mov r1, rSELF @ pass Thread::Current bl \cxx_name @ (mirror::Class* cls, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER @@ -1292,7 +1298,7 @@ ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_initialized_rosalloc, art // The common fast path code for art_quick_alloc_object_resolved/initialized_tlab // and art_quick_alloc_object_resolved/initialized_region_tlab. // -// r0: type r9: Thread::Current, r1, r2, r3, r12: free. +// r0: type, rSELF (r9): Thread::Current, r1, r2, r3, r12: free. // Need to preserve r0 to the slow path. // // If isInitialized=1 then the compiler assumes the object's class has already been initialized. @@ -1304,7 +1310,7 @@ ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_initialized_rosalloc, art #if !((THREAD_LOCAL_POS_OFFSET + 4 == THREAD_LOCAL_END_OFFSET) && (THREAD_LOCAL_POS_OFFSET % 8 == 0)) #error "Thread::thread_local_pos/end must be consecutive and are 8 byte aligned for performance" #endif - ldrd r12, r3, [r9, #THREAD_LOCAL_POS_OFFSET] + ldrd r12, r3, [rSELF, #THREAD_LOCAL_POS_OFFSET] sub r12, r3, r12 // Compute the remaining buf size. ldr r3, [r0, #MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET] // Load the object size (r3). cmp r3, r12 // Check if it fits. @@ -1317,9 +1323,9 @@ ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_initialized_rosalloc, art // "Point of no slow path". Won't go to the slow path from here on. OK to clobber r0 and r1. // Reload old thread_local_pos (r0) // for the return value. - ldr r2, [r9, #THREAD_LOCAL_POS_OFFSET] + ldr r2, [rSELF, #THREAD_LOCAL_POS_OFFSET] add r1, r2, r3 - str r1, [r9, #THREAD_LOCAL_POS_OFFSET] // Store new thread_local_pos. + str r1, [rSELF, #THREAD_LOCAL_POS_OFFSET] // Store new thread_local_pos. // After this "STR" the object is published to the thread local allocation stack, // and it will be observable from a runtime internal (eg. Heap::VisitObjects) point of view. // It is not yet visible to the running (user) compiled code until after the return. @@ -1337,9 +1343,9 @@ ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_initialized_rosalloc, art // // (Note: The actual check is done by checking that the object's class pointer is non-null. // Also, unlike rosalloc, the object can never be observed as null). - ldr r1, [r9, #THREAD_LOCAL_OBJECTS_OFFSET] // Increment thread_local_objects. + ldr r1, [rSELF, #THREAD_LOCAL_OBJECTS_OFFSET] // Increment thread_local_objects. add r1, r1, #1 - str r1, [r9, #THREAD_LOCAL_OBJECTS_OFFSET] + str r1, [rSELF, #THREAD_LOCAL_OBJECTS_OFFSET] POISON_HEAP_REF r0 str r0, [r2, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer. // Fence. This is "ish" not "ishst" so @@ -1366,12 +1372,12 @@ ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_initialized_rosalloc, art .macro GENERATE_ALLOC_OBJECT_RESOLVED_TLAB name, entrypoint, isInitialized ENTRY \name // Fast path tlab allocation. - // r0: type, r9: Thread::Current + // r0: type, rSELF (r9): Thread::Current // r1, r2, r3, r12: free. ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lslow_path\name, \isInitialized .Lslow_path\name: SETUP_SAVE_REFS_ONLY_FRAME r2 // Save callee saves in case of GC. - mov r1, r9 // Pass Thread::Current. + mov r1, rSELF // Pass Thread::Current. bl \entrypoint // (mirror::Class* klass, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER @@ -1388,7 +1394,7 @@ GENERATE_ALLOC_OBJECT_RESOLVED_TLAB art_quick_alloc_object_initialized_tlab, art // The common fast path code for art_quick_alloc_array_resolved/initialized_tlab // and art_quick_alloc_array_resolved/initialized_region_tlab. // -// r0: type r1: component_count r2: total_size r9: Thread::Current, r3, r12: free. +// r0: type, r1: component_count, r2: total_size, rSELF (r9): Thread::Current, r3, r12: free. // Need to preserve r0 and r1 to the slow path. .macro ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED_WITH_SIZE slowPathLabel and r2, r2, #OBJECT_ALIGNMENT_MASK_TOGGLED // Apply alignment mask @@ -1400,7 +1406,7 @@ GENERATE_ALLOC_OBJECT_RESOLVED_TLAB art_quick_alloc_object_initialized_tlab, art #if !((THREAD_LOCAL_POS_OFFSET + 4 == THREAD_LOCAL_END_OFFSET) && (THREAD_LOCAL_POS_OFFSET % 8 == 0)) #error "Thread::thread_local_pos/end must be consecutive and are 8 byte aligned for performance" #endif - ldrd r3, r12, [r9, #THREAD_LOCAL_POS_OFFSET] + ldrd r3, r12, [rSELF, #THREAD_LOCAL_POS_OFFSET] sub r12, r12, r3 // Compute the remaining buf size. cmp r2, r12 // Check if the total_size fits. // The array class is always initialized here. Unlike new-instance, @@ -1408,10 +1414,10 @@ GENERATE_ALLOC_OBJECT_RESOLVED_TLAB art_quick_alloc_object_initialized_tlab, art bhi \slowPathLabel // "Point of no slow path". Won't go to the slow path from here on. OK to clobber r0 and r1. add r2, r2, r3 - str r2, [r9, #THREAD_LOCAL_POS_OFFSET] // Store new thread_local_pos. - ldr r2, [r9, #THREAD_LOCAL_OBJECTS_OFFSET] // Increment thread_local_objects. + str r2, [rSELF, #THREAD_LOCAL_POS_OFFSET] // Store new thread_local_pos. + ldr r2, [rSELF, #THREAD_LOCAL_OBJECTS_OFFSET] // Increment thread_local_objects. add r2, r2, #1 - str r2, [r9, #THREAD_LOCAL_OBJECTS_OFFSET] + str r2, [rSELF, #THREAD_LOCAL_OBJECTS_OFFSET] POISON_HEAP_REF r0 str r0, [r3, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer. str r1, [r3, #MIRROR_ARRAY_LENGTH_OFFSET] // Store the array length. @@ -1434,7 +1440,7 @@ ENTRY \name // Fast path array allocation for region tlab allocation. // r0: mirror::Class* type // r1: int32_t component_count - // r9: thread + // rSELF (r9): thread // r2, r3, r12: free. \size_setup .Lslow_path\name ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED_WITH_SIZE .Lslow_path\name @@ -1443,7 +1449,7 @@ ENTRY \name // r1: int32_t component_count // r2: Thread* self SETUP_SAVE_REFS_ONLY_FRAME r2 // save callee saves in case of GC - mov r2, r9 // pass Thread::Current + mov r2, rSELF // pass Thread::Current bl \entrypoint RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER @@ -1566,10 +1572,10 @@ END art_quick_implicit_suspend .extern artQuickProxyInvokeHandler ENTRY art_quick_proxy_invoke_handler SETUP_SAVE_REFS_AND_ARGS_FRAME_WITH_METHOD_IN_R0 - mov r2, r9 @ pass Thread::Current + mov r2, rSELF @ pass Thread::Current mov r3, sp @ pass SP blx artQuickProxyInvokeHandler @ (Method* proxy method, receiver, Thread*, SP) - ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ + ldr r2, [rSELF, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ // Tear down the callee-save frame. Skip arg registers. add sp, #(FRAME_SIZE_SAVE_REFS_AND_ARGS - FRAME_SIZE_SAVE_REFS_ONLY) .cfi_adjust_cfa_offset -(FRAME_SIZE_SAVE_REFS_AND_ARGS - FRAME_SIZE_SAVE_REFS_ONLY) @@ -1697,7 +1703,7 @@ END art_quick_imt_conflict_trampoline .extern artQuickResolutionTrampoline ENTRY art_quick_resolution_trampoline SETUP_SAVE_REFS_AND_ARGS_FRAME r2 - mov r2, r9 @ pass Thread::Current + mov r2, rSELF @ pass Thread::Current mov r3, sp @ pass SP blx artQuickResolutionTrampoline @ (Method* called, receiver, Thread*, SP) cbz r0, 1f @ is code pointer null? goto exception @@ -1771,10 +1777,10 @@ ENTRY art_quick_generic_jni_trampoline blx artQuickGenericJniEndTrampoline // Restore self pointer. - mov r9, r11 + mov rSELF, r11 // Pending exceptions possible. - ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ + ldr r2, [rSELF, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ cbnz r2, .Lexception_in_native // Tear down the alloca. @@ -1795,7 +1801,7 @@ ENTRY art_quick_generic_jni_trampoline .cfi_adjust_cfa_offset FRAME_SIZE_SAVE_REFS_AND_ARGS-FRAME_SIZE_SAVE_REFS_ONLY .Lexception_in_native: - ldr ip, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] + ldr ip, [rSELF, #THREAD_TOP_QUICK_FRAME_OFFSET] add ip, ip, #-1 // Remove the GenericJNI tag. ADD/SUB writing directly to SP is UNPREDICTABLE. mov sp, ip .cfi_def_cfa_register sp @@ -1806,10 +1812,10 @@ END art_quick_generic_jni_trampoline .extern artQuickToInterpreterBridge ENTRY art_quick_to_interpreter_bridge SETUP_SAVE_REFS_AND_ARGS_FRAME r1 - mov r1, r9 @ pass Thread::Current + mov r1, rSELF @ pass Thread::Current mov r2, sp @ pass SP blx artQuickToInterpreterBridge @ (Method* method, Thread*, SP) - ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ + ldr r2, [rSELF, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ // Tear down the callee-save frame. Skip arg registers. add sp, #(FRAME_SIZE_SAVE_REFS_AND_ARGS - FRAME_SIZE_SAVE_REFS_ONLY) .cfi_adjust_cfa_offset -(FRAME_SIZE_SAVE_REFS_AND_ARGS - FRAME_SIZE_SAVE_REFS_ONLY) @@ -1837,7 +1843,7 @@ ENTRY art_quick_instrumentation_entry SETUP_SAVE_REFS_AND_ARGS_FRAME r2 @ preserve r0 (not normally an arg) knowing there is a spare slot in kSaveRefsAndArgs. str r0, [sp, #4] - mov r2, r9 @ pass Thread::Current + mov r2, rSELF @ pass Thread::Current mov r3, sp @ pass SP blx artInstrumentationMethodEntryFromCode @ (Method*, Object*, Thread*, SP) cbz r0, .Ldeliver_instrumentation_entry_exception @@ -1863,7 +1869,7 @@ ENTRY art_quick_instrumentation_exit add r3, sp, #8 @ store fpr_res pointer, in kSaveEverything frame add r2, sp, #136 @ store gpr_res pointer, in kSaveEverything frame mov r1, sp @ pass SP - mov r0, r9 @ pass Thread::Current + mov r0, rSELF @ pass Thread::Current blx artInstrumentationMethodExitFromCode @ (Thread*, SP, gpr_res*, fpr_res*) cbz r0, .Ldo_deliver_instrumentation_exception @@ -1892,7 +1898,7 @@ END art_quick_instrumentation_exit .extern artDeoptimize ENTRY art_quick_deoptimize SETUP_SAVE_EVERYTHING_FRAME r0 - mov r0, r9 @ pass Thread::Current + mov r0, rSELF @ pass Thread::Current blx artDeoptimize @ (Thread*) END art_quick_deoptimize @@ -1903,7 +1909,7 @@ END art_quick_deoptimize .extern artDeoptimizeFromCompiledCode ENTRY art_quick_deoptimize_from_compiled_code SETUP_SAVE_EVERYTHING_FRAME r1 - mov r1, r9 @ pass Thread::Current + mov r1, rSELF @ pass Thread::Current blx artDeoptimizeFromCompiledCode @ (DeoptimizationKind, Thread*) END art_quick_deoptimize_from_compiled_code @@ -2358,23 +2364,19 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg10, r10 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg11, r11 // Helper macros for Baker CC read barrier mark introspection (BRBMI). -.macro BRBMI_FOR_12_REGISTERS macro_for_register, macro_for_reserved_register +.macro BRBMI_FOR_REGISTERS macro_for_register, macro_for_reserved_register \macro_for_register r0 \macro_for_register r1 \macro_for_register r2 \macro_for_register r3 - \macro_for_reserved_register // R4 is reserved for the entrypoint address. + \macro_for_register r4 \macro_for_register r5 \macro_for_register r6 \macro_for_register r7 - \macro_for_register r8 + \macro_for_reserved_register // r8 (rMR) is the marking register. \macro_for_register r9 \macro_for_register r10 \macro_for_register r11 -.endm - -.macro BRBMI_FOR_REGISTERS macro_for_register, macro_for_reserved_register - BRBMI_FOR_12_REGISTERS \macro_for_register, \macro_for_reserved_register \macro_for_reserved_register // IP is reserved. \macro_for_reserved_register // SP is reserved. \macro_for_reserved_register // LR is reserved. @@ -2382,16 +2384,13 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg11, r11 .endm .macro BRBMI_RETURN_SWITCH_CASE reg + .balign 8 .Lmark_introspection_return_switch_case_\reg: + mov rMR, #1 mov \reg, ip bx lr .endm -.macro BRBMI_BAD_RETURN_SWITCH_CASE -.Lmark_introspection_return_switch_case_bad: - BRBMI_BKPT_FILL_4B -.endm - .macro BRBMI_RETURN_SWITCH_CASE_OFFSET reg .byte (.Lmark_introspection_return_switch_case_\reg - .Lmark_introspection_return_table) / 2 .endm @@ -2454,9 +2453,9 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg11, r11 // If reference is null, just return it in the right register. cmp ip, #0 beq .Lmark_introspection_return\label_suffix - // Use R4 as temp and check the mark bit of the reference. - ldr r4, [ip, #MIRROR_OBJECT_LOCK_WORD_OFFSET] - tst r4, #LOCK_WORD_MARK_BIT_MASK_SHIFTED + // Use rMR as temp and check the mark bit of the reference. + ldr rMR, [ip, #MIRROR_OBJECT_LOCK_WORD_OFFSET] + tst rMR, #LOCK_WORD_MARK_BIT_MASK_SHIFTED beq .Lmark_introspection_unmarked\label_suffix .Lmark_introspection_return\label_suffix: .endm @@ -2469,7 +2468,7 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg11, r11 // the highest bits and the "forwarding address" state to have all bits set. #error "Unexpected lock word state shift or forwarding address state value." #endif - cmp r4, #(LOCK_WORD_STATE_FORWARDING_ADDRESS << LOCK_WORD_STATE_SHIFT) + cmp rMR, #(LOCK_WORD_STATE_FORWARDING_ADDRESS << LOCK_WORD_STATE_SHIFT) bhs .Lmark_introspection_forwarding_address\label_suffix .endm @@ -2479,41 +2478,49 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg11, r11 // Shift left by the forwarding address shift. This clears out the state bits since they are // in the top 2 bits of the lock word. - lsl ip, r4, #LOCK_WORD_STATE_FORWARDING_ADDRESS_SHIFT + lsl ip, rMR, #LOCK_WORD_STATE_FORWARDING_ADDRESS_SHIFT b .Lmark_introspection_return\label_suffix .endm .macro BRBMI_LOAD_RETURN_REG_FROM_CODE_wide ldr_offset // Load the half of the instruction that contains Rt. Adjust for the thumb state in LR. - ldrh r4, [lr, #(-1 + \ldr_offset + 2)] + ldrh rMR, [lr, #(-1 + \ldr_offset + 2)] .endm .macro BRBMI_LOAD_RETURN_REG_FROM_CODE_narrow ldr_offset // Load the 16-bit instruction. Adjust for the thumb state in LR. - ldrh r4, [lr, #(-1 + \ldr_offset)] + ldrh rMR, [lr, #(-1 + \ldr_offset)] +.endm + +.macro BRBMI_EXTRACT_RETURN_REG_wide + lsr rMR, rMR, #12 // Extract `ref_reg`. .endm -.macro BRBMI_GC_ROOT_AND_FIELD_SLOW_PATH gc_root_ldr_offset, label_suffix - .balign 64 +.macro BRBMI_EXTRACT_RETURN_REG_narrow + and rMR, rMR, #7 // Extract `ref_reg`. +.endm + +.macro BRBMI_LOAD_AND_EXTRACT_RETURN_REG ldr_offset, label_suffix + BRBMI_LOAD_RETURN_REG_FROM_CODE\label_suffix \ldr_offset + BRBMI_EXTRACT_RETURN_REG\label_suffix +.endm + +.macro BRBMI_GC_ROOT gc_root_ldr_offset, label_suffix + .balign 32 .thumb_func .type art_quick_read_barrier_mark_introspection_gc_roots\label_suffix, #function .hidden art_quick_read_barrier_mark_introspection_gc_roots\label_suffix .global art_quick_read_barrier_mark_introspection_gc_roots\label_suffix art_quick_read_barrier_mark_introspection_gc_roots\label_suffix: - BRBMI_RUNTIME_CALL - // Load the LDR (or the half of it) that contains Rt. - BRBMI_LOAD_RETURN_REG_FROM_CODE\label_suffix \gc_root_ldr_offset - b .Lmark_introspection_extract_register_and_return\label_suffix - // We've used 28 bytes since the "gc_roots" entrypoint (22 bytes for - // BRBMI_RUNTIME_CALL, 4 bytes for LDRH and 2 bytes for the branch). Squeeze - // the 6 byte forwarding address extraction here across the 32-byte boundary. - BRBMI_EXTRACT_FORWARDING_ADDRESS \label_suffix - // And the slow path taking exactly 30 bytes (6 bytes for the forwarding - // address check, 22 bytes for BRBMI_RUNTIME_CALL and 2 bytes for the near - // branch) shall take the rest of the 32-byte section (within a cache line). + BRBMI_LOAD_AND_EXTRACT_RETURN_REG \gc_root_ldr_offset, \label_suffix +.endm + +.macro BRBMI_FIELD_SLOW_PATH ldr_offset, label_suffix + .balign 16 + // Note: Generates exactly 16 bytes of code. BRBMI_UNMARKED_FORWARDING_ADDRESS_CHECK \label_suffix - BRBMI_RUNTIME_CALL - b .Lmark_introspection_return\label_suffix + BRBMI_LOAD_AND_EXTRACT_RETURN_REG \ldr_offset, \label_suffix + b .Lmark_introspection_runtime_call .endm /* @@ -2536,9 +2543,12 @@ art_quick_read_barrier_mark_introspection_gc_roots\label_suffix: * not do the gray bit check. * * For field accesses and array loads with a constant index the thunk loads - * the reference into IP using introspection and calls the main entrypoint, - * art_quick_read_barrier_mark_introspection. With heap poisoning enabled, - * the passed reference is poisoned. + * the reference into IP using introspection and calls the main entrypoint + * ("wide", for 32-bit LDR) art_quick_read_barrier_mark_introspection or + * the "narrow" entrypoint (for 16-bit LDR). The latter is at a known + * offset (BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET) + * from the main entrypoint and the thunk adjusts the entrypoint pointer. + * With heap poisoning enabled, the passed reference is poisoned. * * For array accesses with non-constant index, the thunk inserts the bits * 0-5 of the LDR instruction to the entrypoint address, effectively @@ -2556,53 +2566,62 @@ art_quick_read_barrier_mark_introspection_gc_roots\label_suffix: * (And even with heap poisoning enabled, GC roots are not poisoned.) * To re-use the same entrypoint pointer in generated code, we make sure * that the gc root entrypoint (a copy of the entrypoint with a different - * offset for introspection loads) is located at a known offset (128 bytes, - * or BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRYPOINT_OFFSET) from the main - * entrypoint and the GC root thunk adjusts the entrypoint pointer, moves - * the root register to IP and jumps to the customized entrypoint, - * art_quick_read_barrier_mark_introspection_gc_roots. The thunk also - * performs all the fast-path checks, so we need just the slow path. + * offset for introspection loads) is located at a known offset (0xc0/0xe0 + * bytes, or BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET/ + * BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET) from the + * main entrypoint and the GC root thunk adjusts the entrypoint pointer, + * moves the root register to IP and jumps to the customized entrypoint, + * art_quick_read_barrier_mark_introspection_gc_roots_{wide,narrow}. + * The thunk also performs all the fast-path checks, so we need just the + * slow path. * * The code structure is - * art_quick_read_barrier_mark_introspection: + * art_quick_read_barrier_mark_introspection: // @0x00 * Up to 32 bytes code for main entrypoint fast-path code for fields * (and array elements with constant offset) with LDR encoding T3; * jumps to the switch in the "narrow" entrypoint. - * Padding to 32 bytes if needed. - * art_quick_read_barrier_mark_introspection_narrow: + * art_quick_read_barrier_mark_introspection_narrow: // @0x20 * Up to 48 bytes code for fast path code for fields (and array * elements with constant offset) with LDR encoding T1, ending in the * return switch instruction TBB and the table with switch offsets. - * Padding to 80 bytes if needed. - * .Lmark_introspection_return_switch_case_r0: - * Exactly 48 bytes of code for the return switch cases (12 cases, - * including BKPT for the reserved registers). - * Ends at 128 bytes total. - * art_quick_read_barrier_mark_introspection_gc_roots_wide: - * GC root entrypoint code for LDR encoding T3 (28 bytes). - * Forwarding address extraction for LDR encoding T3 (6 bytes). - * Slow path for main entrypoint for LDR encoding T3 (30 bytes). - * Ends at 192 bytes total. - * art_quick_read_barrier_mark_introspection_gc_roots_narrow: - * GC root entrypoint code for LDR encoding T1 (28 bytes). - * Forwarding address extraction for LDR encoding T1 (6 bytes). - * Slow path for main entrypoint for LDR encoding T1 (30 bytes). - * Ends at 256 bytes total. - * art_quick_read_barrier_mark_introspection_arrays: + * .Lmark_introspection_return_switch_case_r0: // @0x50 + * Exactly 88 bytes of code for the return switch cases (8 bytes per + * case, 11 cases; no code for reserved registers). + * .Lmark_introspection_forwarding_address_narrow: // @0xa8 + * Exactly 6 bytes to extract the forwarding address and jump to the + * "narrow" entrypoint fast path. + * .Lmark_introspection_return_switch_case_bad: // @0xae + * Exactly 2 bytes, bkpt for unexpected return register. + * .Lmark_introspection_unmarked_narrow: // @0xb0 + * Exactly 16 bytes for "narrow" entrypoint slow path. + * art_quick_read_barrier_mark_introspection_gc_roots_wide: // @0xc0 + * GC root entrypoint code for LDR encoding T3 (10 bytes); loads and + * extracts the return register and jumps to the runtime call. + * .Lmark_introspection_forwarding_address_wide: // @0xca + * Exactly 6 bytes to extract the forwarding address and jump to the + * "wide" entrypoint fast path. + * .Lmark_introspection_unmarked_wide: // @0xd0 + * Exactly 16 bytes for "wide" entrypoint slow path. + * art_quick_read_barrier_mark_introspection_gc_roots_narrow: // @0xe0 + * GC root entrypoint code for LDR encoding T1 (8 bytes); loads and + * extracts the return register and falls through to the runtime call. + * .Lmark_introspection_runtime_call: // @0xe8 + * Exactly 24 bytes for the runtime call to MarkReg() and jump to the + * return switch. + * art_quick_read_barrier_mark_introspection_arrays: // @0x100 * Exactly 128 bytes for array load switch cases (16x2 instructions). */ +#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER) .balign 512 ENTRY art_quick_read_barrier_mark_introspection - // At this point, IP contains the reference, R4 can be freely used. - // (R4 is reserved for the entrypoint address.) + // At this point, IP contains the reference, rMR is clobbered by the thunk + // and can be freely used as it will be set back to 1 before returning. // For heap poisoning, the reference is poisoned, so unpoison it first. UNPOISON_HEAP_REF ip - // Check for null or marked, lock word is loaded into IP. + // Check for null or marked, lock word is loaded into rMR. BRBMI_CHECK_NULL_AND_MARKED _wide - // Load the half of the instruction that contains Rt. - BRBMI_LOAD_RETURN_REG_FROM_CODE_wide BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET -.Lmark_introspection_extract_register_and_return_wide: - lsr r4, r4, #12 // Extract `ref_reg`. + // Load and extract the return register from the instruction. + BRBMI_LOAD_AND_EXTRACT_RETURN_REG BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET, _wide b .Lmark_introspection_return_switch .balign 32 @@ -2611,25 +2630,45 @@ ENTRY art_quick_read_barrier_mark_introspection .hidden art_quick_read_barrier_mark_introspection_narrow .global art_quick_read_barrier_mark_introspection_narrow art_quick_read_barrier_mark_introspection_narrow: - // At this point, IP contains the reference, R4 can be freely used. - // (R4 is reserved for the entrypoint address.) + // At this point, IP contains the reference, rMR is clobbered by the thunk + // and can be freely used as it will be set back to 1 before returning. // For heap poisoning, the reference is poisoned, so unpoison it first. UNPOISON_HEAP_REF ip - // Check for null or marked, lock word is loaded into R4. + // Check for null or marked, lock word is loaded into rMR. BRBMI_CHECK_NULL_AND_MARKED _narrow - // Load the 16-bit instruction. - BRBMI_LOAD_RETURN_REG_FROM_CODE_narrow BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET -.Lmark_introspection_extract_register_and_return_narrow: - and r4, r4, #7 // Extract `ref_reg`. + // Load and extract the return register from the instruction. + BRBMI_LOAD_AND_EXTRACT_RETURN_REG BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET, _narrow .Lmark_introspection_return_switch: - tbb [pc, r4] // Jump to the switch case. + tbb [pc, rMR] // Jump to the switch case. .Lmark_introspection_return_table: BRBMI_FOR_REGISTERS BRBMI_RETURN_SWITCH_CASE_OFFSET, BRBMI_BAD_RETURN_SWITCH_CASE_OFFSET - .balign 16 - BRBMI_FOR_12_REGISTERS BRBMI_RETURN_SWITCH_CASE, BRBMI_BAD_RETURN_SWITCH_CASE + BRBMI_FOR_REGISTERS BRBMI_RETURN_SWITCH_CASE, /* no code */ + + .balign 8 + BRBMI_EXTRACT_FORWARDING_ADDRESS _narrow // 6 bytes +.Lmark_introspection_return_switch_case_bad: + bkpt // 2 bytes + + BRBMI_FIELD_SLOW_PATH BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET, _narrow + + // 8 bytes for the loading and extracting of the return register. + BRBMI_GC_ROOT BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_OFFSET, _wide + // 2 bytes for near branch to the runtime call. + b .Lmark_introspection_runtime_call + + BRBMI_EXTRACT_FORWARDING_ADDRESS _wide // Not even 4-byte aligned. + + BRBMI_FIELD_SLOW_PATH BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET, _wide + + // 8 bytes for the loading and extracting of the return register. + BRBMI_GC_ROOT BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET, _narrow + // And the runtime call and branch to the switch taking exactly 24 bytes + // (22 bytes for BRBMI_RUNTIME_CALL and 2 bytes for the near branch) + // shall take the rest of the 32-byte section (within a cache line). +.Lmark_introspection_runtime_call: + BRBMI_RUNTIME_CALL + b .Lmark_introspection_return_switch - BRBMI_GC_ROOT_AND_FIELD_SLOW_PATH BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_OFFSET, _wide - BRBMI_GC_ROOT_AND_FIELD_SLOW_PATH BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET, _narrow .balign 256 .thumb_func @@ -2639,88 +2678,40 @@ art_quick_read_barrier_mark_introspection_narrow: art_quick_read_barrier_mark_introspection_arrays: BRBMI_FOR_REGISTERS BRBMI_ARRAY_LOAD, BRBMI_BKPT_FILL_8B END art_quick_read_barrier_mark_introspection +#else // defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER) +ENTRY art_quick_read_barrier_mark_introspection + bkpt // Unreachable. +END art_quick_read_barrier_mark_introspection +#endif // defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER) .extern artInvokePolymorphic ENTRY art_quick_invoke_polymorphic SETUP_SAVE_REFS_AND_ARGS_FRAME r2 - mov r2, r9 @ pass Thread::Current - mov 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_method_frame_info_arm64.h b/runtime/arch/arm64/callee_save_frame_arm64.h similarity index 61% rename from runtime/arch/arm64/quick_method_frame_info_arm64.h rename to runtime/arch/arm64/callee_save_frame_arm64.h index 3e6f6c6e3b7ff5e855e06fc8294b5c2d534640f4..bc36bfabec16cfe5f230a3e63d9747196a8eb371 100644 --- a/runtime/arch/arm64/quick_method_frame_info_arm64.h +++ b/runtime/arch/arm64/callee_save_frame_arm64.h @@ -14,14 +14,14 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_ARCH_ARM64_QUICK_METHOD_FRAME_INFO_ARM64_H_ -#define ART_RUNTIME_ARCH_ARM64_QUICK_METHOD_FRAME_INFO_ARM64_H_ +#ifndef ART_RUNTIME_ARCH_ARM64_CALLEE_SAVE_FRAME_ARM64_H_ +#define ART_RUNTIME_ARCH_ARM64_CALLEE_SAVE_FRAME_ARM64_H_ #include "arch/instruction_set.h" #include "base/bit_utils.h" #include "base/callee_save_type.h" #include "base/enums.h" -#include "globals.h" +#include "base/globals.h" #include "quick/quick_method_frame_info.h" #include "registers_arm64.h" @@ -79,57 +79,56 @@ static constexpr uint32_t kArm64CalleeSaveFpEverythingSpills = (1 << art::arm64::D27) | (1 << art::arm64::D28) | (1 << art::arm64::D29) | (1 << art::arm64::D30) | (1 << art::arm64::D31); -constexpr uint32_t Arm64CalleeSaveCoreSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kArm64CalleeSaveAlwaysSpills | kArm64CalleeSaveRefSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kArm64CalleeSaveArgSpills : 0) | - (type == CalleeSaveType::kSaveAllCalleeSaves ? kArm64CalleeSaveAllSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kArm64CalleeSaveEverythingSpills : 0); -} +class Arm64CalleeSaveFrame { + public: + static constexpr uint32_t GetCoreSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kArm64CalleeSaveAlwaysSpills | kArm64CalleeSaveRefSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kArm64CalleeSaveArgSpills : 0) | + (type == CalleeSaveType::kSaveAllCalleeSaves ? kArm64CalleeSaveAllSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kArm64CalleeSaveEverythingSpills : 0); + } -constexpr uint32_t Arm64CalleeSaveFpSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kArm64CalleeSaveFpAlwaysSpills | kArm64CalleeSaveFpRefSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kArm64CalleeSaveFpArgSpills : 0) | - (type == CalleeSaveType::kSaveAllCalleeSaves ? kArm64CalleeSaveFpAllSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kArm64CalleeSaveFpEverythingSpills : 0); -} + static constexpr uint32_t GetFpSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kArm64CalleeSaveFpAlwaysSpills | kArm64CalleeSaveFpRefSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kArm64CalleeSaveFpArgSpills : 0) | + (type == CalleeSaveType::kSaveAllCalleeSaves ? kArm64CalleeSaveFpAllSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kArm64CalleeSaveFpEverythingSpills : 0); + } -constexpr uint32_t Arm64CalleeSaveFrameSize(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return RoundUp((POPCOUNT(Arm64CalleeSaveCoreSpills(type)) /* gprs */ + - POPCOUNT(Arm64CalleeSaveFpSpills(type)) /* fprs */ + - 1 /* Method* */) * static_cast(kArm64PointerSize), kStackAlignment); -} + static constexpr uint32_t GetFrameSize(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return RoundUp((POPCOUNT(GetCoreSpills(type)) /* gprs */ + + POPCOUNT(GetFpSpills(type)) /* fprs */ + + 1 /* Method* */) * static_cast(kArm64PointerSize), kStackAlignment); + } -constexpr QuickMethodFrameInfo Arm64CalleeSaveMethodFrameInfo(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return QuickMethodFrameInfo(Arm64CalleeSaveFrameSize(type), - Arm64CalleeSaveCoreSpills(type), - Arm64CalleeSaveFpSpills(type)); -} + static constexpr QuickMethodFrameInfo GetMethodFrameInfo(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return QuickMethodFrameInfo(GetFrameSize(type), GetCoreSpills(type), GetFpSpills(type)); + } -constexpr size_t Arm64CalleeSaveFpr1Offset(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return Arm64CalleeSaveFrameSize(type) - - (POPCOUNT(Arm64CalleeSaveCoreSpills(type)) + - POPCOUNT(Arm64CalleeSaveFpSpills(type))) * static_cast(kArm64PointerSize); -} + static constexpr size_t GetFpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + (POPCOUNT(GetCoreSpills(type)) + + POPCOUNT(GetFpSpills(type))) * static_cast(kArm64PointerSize); + } -constexpr size_t Arm64CalleeSaveGpr1Offset(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return Arm64CalleeSaveFrameSize(type) - - POPCOUNT(Arm64CalleeSaveCoreSpills(type)) * static_cast(kArm64PointerSize); -} + static constexpr size_t GetGpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + POPCOUNT(GetCoreSpills(type)) * static_cast(kArm64PointerSize); + } -constexpr size_t Arm64CalleeSaveLrOffset(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return Arm64CalleeSaveFrameSize(type) - - POPCOUNT(Arm64CalleeSaveCoreSpills(type) & (-(1 << LR))) * - static_cast(kArm64PointerSize); -} + static constexpr size_t GetReturnPcOffset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - static_cast(kArm64PointerSize); + } +}; } // namespace arm64 } // namespace art -#endif // ART_RUNTIME_ARCH_ARM64_QUICK_METHOD_FRAME_INFO_ARM64_H_ +#endif // ART_RUNTIME_ARCH_ARM64_CALLEE_SAVE_FRAME_ARM64_H_ diff --git a/runtime/arch/arm64/fault_handler_arm64.cc b/runtime/arch/arm64/fault_handler_arm64.cc index d282c8cfc018d5723f3935d817e5d012fa6d588a..e8b4627f86707dd2d5f6c640117ea528f3c54b5d 100644 --- a/runtime/arch/arm64/fault_handler_arm64.cc +++ b/runtime/arch/arm64/fault_handler_arm64.cc @@ -20,10 +20,10 @@ #include "art_method.h" #include "base/enums.h" +#include "base/globals.h" #include "base/hex_dump.h" #include "base/logging.h" // For VLOG. #include "base/macros.h" -#include "globals.h" #include "registers_arm64.h" #include "thread-current-inl.h" diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index e2fce1f79093dd8377019142b5096684a673db77..6b0de4848b43019d9ea34c5e423562867dca7477 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -614,56 +614,18 @@ INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvo .macro INVOKE_STUB_CREATE_FRAME +SAVE_SIZE=6*8 // x4, x5, x19, x20, FP, LR saved. + SAVE_TWO_REGS_INCREASE_FRAME x4, x5, SAVE_SIZE + SAVE_TWO_REGS x19, x20, 16 + SAVE_TWO_REGS xFP, xLR, 32 -SAVE_SIZE=15*8 // x4, x5, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, SP, LR, FP saved. -SAVE_SIZE_AND_METHOD=SAVE_SIZE+8 - - - mov x9, sp // Save stack pointer. - .cfi_register sp,x9 - - add x10, x2, # SAVE_SIZE_AND_METHOD // calculate size of frame. - sub x10, sp, x10 // Calculate SP position - saves + ArtMethod* + args - and x10, x10, # ~0xf // Enforce 16 byte stack alignment. - mov sp, x10 // Set new SP. - - sub x10, x9, #SAVE_SIZE // Calculate new FP (later). Done here as we must move SP - .cfi_def_cfa_register x10 // before this. - .cfi_adjust_cfa_offset SAVE_SIZE - - str x28, [x10, #112] - .cfi_rel_offset x28, 112 - - stp x26, x27, [x10, #96] - .cfi_rel_offset x26, 96 - .cfi_rel_offset x27, 104 - - stp x24, x25, [x10, #80] - .cfi_rel_offset x24, 80 - .cfi_rel_offset x25, 88 - - stp x22, x23, [x10, #64] - .cfi_rel_offset x22, 64 - .cfi_rel_offset x23, 72 - - stp x20, x21, [x10, #48] - .cfi_rel_offset x20, 48 - .cfi_rel_offset x21, 56 - - stp x9, x19, [x10, #32] // Save old stack pointer and x19. - .cfi_rel_offset sp, 32 - .cfi_rel_offset x19, 40 - - stp x4, x5, [x10, #16] // Save result and shorty addresses. - .cfi_rel_offset x4, 16 - .cfi_rel_offset x5, 24 + mov xFP, sp // Use xFP for frame pointer, as it's callee-saved. + .cfi_def_cfa_register xFP - stp xFP, xLR, [x10] // Store LR & FP. - .cfi_rel_offset x29, 0 - .cfi_rel_offset x30, 8 + add x10, x2, #(__SIZEOF_POINTER__ + 0xf) // Reserve space for ArtMethod*, arguments and + and x10, x10, # ~0xf // round up for 16-byte stack alignment. + sub sp, sp, x10 // Adjust SP for ArtMethod*, args and alignment padding. - mov xFP, x10 // Use xFP now, as it's callee-saved. - .cfi_def_cfa_register x29 mov xSELF, x3 // Move thread pointer into SELF register. // Copy arguments into stack frame. @@ -678,12 +640,10 @@ SAVE_SIZE_AND_METHOD=SAVE_SIZE+8 // Copy parameters into the stack. Use numeric label as this is a macro and Clang's assembler // does not have unique-id variables. 1: - cmp w2, #0 - beq 2f + cbz w2, 2f sub w2, w2, #4 // Need 65536 bytes of range. ldr w10, [x1, x2] str w10, [x9, x2] - b 1b 2: @@ -700,29 +660,14 @@ SAVE_SIZE_AND_METHOD=SAVE_SIZE+8 // Branch to method. blr x9 - // Restore return value address and shorty address. - ldp x4, x5, [xFP, #16] - .cfi_restore x4 - .cfi_restore x5 - - ldr x28, [xFP, #112] - .cfi_restore x28 - - ldp x26, x27, [xFP, #96] - .cfi_restore x26 - .cfi_restore x27 - - ldp x24, x25, [xFP, #80] - .cfi_restore x24 - .cfi_restore x25 - - ldp x22, x23, [xFP, #64] - .cfi_restore x22 - .cfi_restore x23 + // Pop the ArtMethod* (null), arguments and alignment padding from the stack. + mov sp, xFP + .cfi_def_cfa_register sp - ldp x20, x21, [xFP, #48] - .cfi_restore x20 - .cfi_restore x21 + // Restore saved registers including value address and shorty address. + RESTORE_TWO_REGS x19, x20, 16 + RESTORE_TWO_REGS xFP, xLR, 32 + RESTORE_TWO_REGS_DECREASE_FRAME x4, x5, SAVE_SIZE // Store result (w0/x0/s0/d0) appropriately, depending on resultType. ldrb w10, [x5] @@ -732,33 +677,28 @@ SAVE_SIZE_AND_METHOD=SAVE_SIZE+8 // Don't set anything for a void type. cmp w10, #'V' - beq 3f + beq 1f // Is it a double? cmp w10, #'D' - bne 1f - str d0, [x4] - b 3f + beq 2f -1: // Is it a float? + // Is it a float? cmp w10, #'F' - bne 2f - str s0, [x4] - b 3f + beq 3f -2: // Just store x0. Doesn't matter if it is 64 or 32 bits. + // Just store x0. Doesn't matter if it is 64 or 32 bits. str x0, [x4] -3: // Finish up. - ldp x2, x19, [xFP, #32] // Restore stack pointer and x19. - .cfi_restore x19 - mov sp, x2 - .cfi_restore sp +1: // Finish up. + ret - ldp xFP, xLR, [xFP] // Restore old frame pointer and link register. - .cfi_restore x29 - .cfi_restore x30 +2: // Store double. + str d0, [x4] + ret +3: // Store float. + str s0, [x4] ret .endm @@ -1057,7 +997,7 @@ END art_quick_invoke_static_stub /* extern"C" void art_quick_osr_stub(void** stack, x0 * size_t stack_size_in_bytes, x1 - * const uin8_t* native_pc, x2 + * const uint8_t* native_pc, x2 * JValue *result, x3 * char *shorty, x4 * Thread *self) x5 @@ -1211,45 +1151,36 @@ END art_quick_do_long_jump */ .extern artLockObjectFromCode ENTRY art_quick_lock_object - cbz w0, .Lslow_lock - add x4, x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET // exclusive load/store has no immediate anymore + ldr w1, [xSELF, #THREAD_ID_OFFSET] + cbz w0, art_quick_lock_object_no_inline + // Exclusive load/store has no immediate anymore. + add x4, x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET .Lretry_lock: - ldr w2, [xSELF, #THREAD_ID_OFFSET] // TODO: Can the thread ID really change during the loop? - ldaxr w1, [x4] // acquire needed only in most common case - and w3, w1, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED // zero the gc bits - cbnz w3, .Lnot_unlocked // already thin locked - // unlocked case - x1: original lock word that's zero except for the read barrier bits. - orr x2, x1, x2 // x2 holds thread id with count of 0 with preserved read barrier bits - stxr w3, w2, [x4] - cbnz w3, .Llock_stxr_fail // store failed, retry + ldaxr w2, [x4] // Acquire needed only in most common case. + eor w3, w2, w1 // Prepare the value to store if unlocked + // (thread id, count of 0 and preserved read barrier bits), + // or prepare to compare thread id for recursive lock check + // (lock_word.ThreadId() ^ self->ThreadId()). + tst w2, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED // Test the non-gc bits. + b.ne .Lnot_unlocked // Check if unlocked. + // unlocked case - store w3: original lock word plus thread id, preserved read barrier bits. + stxr w2, w3, [x4] + cbnz w2, .Lretry_lock // If the store failed, retry. ret -.Lnot_unlocked: // x1: original lock word - lsr w3, w1, LOCK_WORD_STATE_SHIFT - cbnz w3, .Lslow_lock // if either of the top two bits are set, go slow path - eor w2, w1, w2 // lock_word.ThreadId() ^ self->ThreadId() - uxth w2, w2 // zero top 16 bits - cbnz w2, .Lslow_lock // lock word and self thread id's match -> recursive lock - // else contention, go to slow path - and w3, w1, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED // zero the gc bits. - add w2, w3, #LOCK_WORD_THIN_LOCK_COUNT_ONE // increment count in lock word placing in w2 to check overflow - lsr w3, w2, #LOCK_WORD_GC_STATE_SHIFT // if the first gc state bit is set, we overflowed. - cbnz w3, .Lslow_lock // if we overflow the count go slow path - add w2, w1, #LOCK_WORD_THIN_LOCK_COUNT_ONE // increment count for real - stxr w3, w2, [x4] - cbnz w3, .Llock_stxr_fail // store failed, retry +.Lnot_unlocked: // w2: original lock word, w1: thread id, w3: w2 ^ w1 + // Check lock word state and thread id together, + tst w3, #(LOCK_WORD_STATE_MASK_SHIFTED | LOCK_WORD_THIN_LOCK_OWNER_MASK_SHIFTED) + b.ne art_quick_lock_object_no_inline + add w3, w2, #LOCK_WORD_THIN_LOCK_COUNT_ONE // Increment the recursive lock count. + tst w3, #LOCK_WORD_THIN_LOCK_COUNT_MASK_SHIFTED // Test the new thin lock count. + b.eq art_quick_lock_object_no_inline // Zero as the new count indicates overflow, go slow path. + stxr w2, w3, [x4] + cbnz w2, .Lretry_lock // If the store failed, retry. ret -.Llock_stxr_fail: - b .Lretry_lock // retry -.Lslow_lock: - SETUP_SAVE_REFS_ONLY_FRAME // save callee saves in case we block - mov x1, xSELF // pass Thread::Current - bl artLockObjectFromCode // (Object* obj, Thread*) - RESTORE_SAVE_REFS_ONLY_FRAME - REFRESH_MARKING_REGISTER - RETURN_IF_W0_IS_ZERO_OR_DELIVER END art_quick_lock_object ENTRY art_quick_lock_object_no_inline + // This is also the slow path for art_quick_lock_object. SETUP_SAVE_REFS_ONLY_FRAME // save callee saves in case we block mov x1, xSELF // pass Thread::Current bl artLockObjectFromCode // (Object* obj, Thread*) @@ -1266,54 +1197,46 @@ END art_quick_lock_object_no_inline */ .extern artUnlockObjectFromCode ENTRY art_quick_unlock_object - cbz x0, .Lslow_unlock - add x4, x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET // exclusive load/store has no immediate anymore + ldr w1, [xSELF, #THREAD_ID_OFFSET] + cbz x0, art_quick_unlock_object_no_inline + // Exclusive load/store has no immediate anymore. + add x4, x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET .Lretry_unlock: #ifndef USE_READ_BARRIER - ldr w1, [x4] + ldr w2, [x4] #else - ldxr w1, [x4] // Need to use atomic instructions for read barrier + ldxr w2, [x4] // Need to use atomic instructions for read barrier. #endif - lsr w2, w1, LOCK_WORD_STATE_SHIFT - cbnz w2, .Lslow_unlock // if either of the top two bits are set, go slow path - ldr w2, [xSELF, #THREAD_ID_OFFSET] - and w3, w1, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED // zero the gc bits - eor w3, w3, w2 // lock_word.ThreadId() ^ self->ThreadId() - uxth w3, w3 // zero top 16 bits - cbnz w3, .Lslow_unlock // do lock word and self thread id's match? - and w3, w1, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED // zero the gc bits - cmp w3, #LOCK_WORD_THIN_LOCK_COUNT_ONE - bpl .Lrecursive_thin_unlock - // transition to unlocked - and w3, w1, #LOCK_WORD_GC_STATE_MASK_SHIFTED // w3: zero except for the preserved read barrier bits + eor w3, w2, w1 // Prepare the value to store if simply locked + // (mostly 0s, and preserved read barrier bits), + // or prepare to compare thread id for recursive lock check + // (lock_word.ThreadId() ^ self->ThreadId()). + tst w3, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED // Test the non-gc bits. + b.ne .Lnot_simply_locked // Locked recursively or by other thread? + // Transition to unlocked. #ifndef USE_READ_BARRIER stlr w3, [x4] #else - stlxr w2, w3, [x4] // Need to use atomic instructions for read barrier - cbnz w2, .Lunlock_stxr_fail // store failed, retry + stlxr w2, w3, [x4] // Need to use atomic instructions for read barrier. + cbnz w2, .Lretry_unlock // If the store failed, retry. #endif ret -.Lrecursive_thin_unlock: // w1: original lock word - sub w1, w1, #LOCK_WORD_THIN_LOCK_COUNT_ONE // decrement count +.Lnot_simply_locked: + // Check lock word state and thread id together, + tst w3, #(LOCK_WORD_STATE_MASK_SHIFTED | LOCK_WORD_THIN_LOCK_OWNER_MASK_SHIFTED) + b.ne art_quick_unlock_object_no_inline + sub w3, w2, #LOCK_WORD_THIN_LOCK_COUNT_ONE // decrement count #ifndef USE_READ_BARRIER - str w1, [x4] + str w3, [x4] #else - stxr w2, w1, [x4] // Need to use atomic instructions for read barrier - cbnz w2, .Lunlock_stxr_fail // store failed, retry + stxr w2, w3, [x4] // Need to use atomic instructions for read barrier. + cbnz w2, .Lretry_unlock // If the store failed, retry. #endif ret -.Lunlock_stxr_fail: - b .Lretry_unlock // retry -.Lslow_unlock: - SETUP_SAVE_REFS_ONLY_FRAME // save callee saves in case exception allocation triggers GC - mov x1, xSELF // pass Thread::Current - bl artUnlockObjectFromCode // (Object* obj, Thread*) - RESTORE_SAVE_REFS_ONLY_FRAME - REFRESH_MARKING_REGISTER - RETURN_IF_W0_IS_ZERO_OR_DELIVER END art_quick_unlock_object ENTRY art_quick_unlock_object_no_inline + // This is also the slow path for art_quick_unlock_object. SETUP_SAVE_REFS_ONLY_FRAME // save callee saves in case exception allocation triggers GC mov x1, xSELF // pass Thread::Current bl artUnlockObjectFromCode // (Object* obj, Thread*) @@ -1329,6 +1252,9 @@ END art_quick_unlock_object_no_inline .extern artInstanceOfFromCode .extern artThrowClassCastExceptionForObject ENTRY art_quick_check_instance_of + // Type check using the bit string passes null as the target class. In that case just throw. + cbz x1, .Lthrow_class_cast_exception_for_bitstring_check + // Store arguments and link register // Stack needs to be 16B aligned on calls. SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 32 @@ -1354,6 +1280,7 @@ ENTRY art_quick_check_instance_of // Restore RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32 +.Lthrow_class_cast_exception_for_bitstring_check: SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // save all registers as basis for long jump context mov x2, xSELF // pass Thread::Current bl artThrowClassCastExceptionForObject // (Object*, Class*, Thread*) @@ -1589,7 +1516,10 @@ ENTRY \name END \name .endm -// Macro for string and type resolution and initialization. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. + */ .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET .extern \entrypoint ENTRY \name @@ -1633,6 +1563,8 @@ TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode // Note: Functions `art{Get,Set}{Static,Instance}FromCompiledCode` are @@ -1672,6 +1604,7 @@ GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_region_tlab, RegionTLAB) @@ -1684,6 +1617,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_tlab, TLAB) @@ -2912,84 +2846,30 @@ END art_quick_read_barrier_mark_introspection .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/code_offset.h b/runtime/arch/code_offset.h deleted file mode 100644 index 8e8dde4c4c6f2f161bce748b6d8f952b351e7f24..0000000000000000000000000000000000000000 --- a/runtime/arch/code_offset.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_ARCH_CODE_OFFSET_H_ -#define ART_RUNTIME_ARCH_CODE_OFFSET_H_ - -#include - -#include - -#include "base/bit_utils.h" -#include "base/macros.h" -#include "instruction_set.h" - -namespace art { - -// CodeOffset is a holder for compressed code offsets. Since some architectures have alignment -// requirements it is possible to compress code offsets to reduce stack map sizes. -class CodeOffset { - public: - ALWAYS_INLINE static CodeOffset FromOffset(uint32_t offset, InstructionSet isa = kRuntimeISA) { - return CodeOffset(offset / GetInstructionSetInstructionAlignment(isa)); - } - - ALWAYS_INLINE static CodeOffset FromCompressedOffset(uint32_t offset) { - return CodeOffset(offset); - } - - ALWAYS_INLINE uint32_t Uint32Value(InstructionSet isa = kRuntimeISA) const { - uint32_t decoded = value_ * GetInstructionSetInstructionAlignment(isa); - DCHECK_GE(decoded, value_) << "Integer overflow"; - return decoded; - } - - // Return compressed internal value. - ALWAYS_INLINE uint32_t CompressedValue() const { - return value_; - } - - ALWAYS_INLINE CodeOffset() = default; - ALWAYS_INLINE CodeOffset(const CodeOffset&) = default; - ALWAYS_INLINE CodeOffset& operator=(const CodeOffset&) = default; - ALWAYS_INLINE CodeOffset& operator=(CodeOffset&&) = default; - - private: - ALWAYS_INLINE explicit CodeOffset(uint32_t value) : value_(value) {} - - uint32_t value_ = 0u; -}; - -inline bool operator==(const CodeOffset& a, const CodeOffset& b) { - return a.CompressedValue() == b.CompressedValue(); -} - -inline bool operator!=(const CodeOffset& a, const CodeOffset& b) { - return !(a == b); -} - -inline bool operator<(const CodeOffset& a, const CodeOffset& b) { - return a.CompressedValue() < b.CompressedValue(); -} - -inline bool operator<=(const CodeOffset& a, const CodeOffset& b) { - return a.CompressedValue() <= b.CompressedValue(); -} - -inline bool operator>(const CodeOffset& a, const CodeOffset& b) { - return a.CompressedValue() > b.CompressedValue(); -} - -inline bool operator>=(const CodeOffset& a, const CodeOffset& b) { - return a.CompressedValue() >= b.CompressedValue(); -} - -inline std::ostream& operator<<(std::ostream& os, const CodeOffset& offset) { - return os << offset.Uint32Value(); -} - -} // namespace art - -#endif // ART_RUNTIME_ARCH_CODE_OFFSET_H_ diff --git a/runtime/arch/instruction_set_features.h b/runtime/arch/instruction_set_features.h index 5f1a507f7ac58ec76c2d213e35b11710b3c96e5a..c31c9276687d141baf1f3a6eea240e2c49849ced 100644 --- a/runtime/arch/instruction_set_features.h +++ b/runtime/arch/instruction_set_features.h @@ -21,8 +21,8 @@ #include #include +#include "arch/instruction_set.h" #include "base/macros.h" -#include "instruction_set.h" namespace art { diff --git a/runtime/arch/mips/quick_method_frame_info_mips.h b/runtime/arch/mips/callee_save_frame_mips.h similarity index 66% rename from runtime/arch/mips/quick_method_frame_info_mips.h rename to runtime/arch/mips/callee_save_frame_mips.h index 8c86252152d6220f4f26c1a5b9f47898b4c04f38..6e88d0843222858a79bcbcd26346a5f604e71dc4 100644 --- a/runtime/arch/mips/quick_method_frame_info_mips.h +++ b/runtime/arch/mips/callee_save_frame_mips.h @@ -14,13 +14,14 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_ARCH_MIPS_QUICK_METHOD_FRAME_INFO_MIPS_H_ -#define ART_RUNTIME_ARCH_MIPS_QUICK_METHOD_FRAME_INFO_MIPS_H_ +#ifndef ART_RUNTIME_ARCH_MIPS_CALLEE_SAVE_FRAME_MIPS_H_ +#define ART_RUNTIME_ARCH_MIPS_CALLEE_SAVE_FRAME_MIPS_H_ #include "arch/instruction_set.h" #include "base/bit_utils.h" #include "base/callee_save_type.h" #include "base/enums.h" +#include "base/globals.h" #include "quick/quick_method_frame_info.h" #include "registers_mips.h" @@ -80,37 +81,56 @@ static constexpr uint32_t kMipsCalleeSaveFpEverythingSpills = (1 << art::mips::F24) | (1 << art::mips::F25) | (1 << art::mips::F26) | (1 << art::mips::F27) | (1 << art::mips::F28) | (1 << art::mips::F29) | (1 << art::mips::F30) | (1u << art::mips::F31); -constexpr uint32_t MipsCalleeSaveCoreSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kMipsCalleeSaveAlwaysSpills | kMipsCalleeSaveRefSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kMipsCalleeSaveArgSpills : 0) | - (type == CalleeSaveType::kSaveAllCalleeSaves ? kMipsCalleeSaveAllSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kMipsCalleeSaveEverythingSpills : 0); -} +class MipsCalleeSaveFrame { + public: + static constexpr uint32_t GetCoreSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kMipsCalleeSaveAlwaysSpills | kMipsCalleeSaveRefSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kMipsCalleeSaveArgSpills : 0) | + (type == CalleeSaveType::kSaveAllCalleeSaves ? kMipsCalleeSaveAllSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kMipsCalleeSaveEverythingSpills : 0); + } -constexpr uint32_t MipsCalleeSaveFPSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kMipsCalleeSaveFpAlwaysSpills | kMipsCalleeSaveFpRefSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kMipsCalleeSaveFpArgSpills : 0) | - (type == CalleeSaveType::kSaveAllCalleeSaves ? kMipsCalleeSaveAllFPSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kMipsCalleeSaveFpEverythingSpills : 0); -} + static constexpr uint32_t GetFpSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kMipsCalleeSaveFpAlwaysSpills | kMipsCalleeSaveFpRefSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kMipsCalleeSaveFpArgSpills : 0) | + (type == CalleeSaveType::kSaveAllCalleeSaves ? kMipsCalleeSaveAllFPSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kMipsCalleeSaveFpEverythingSpills : 0); + } -constexpr uint32_t MipsCalleeSaveFrameSize(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return RoundUp((POPCOUNT(MipsCalleeSaveCoreSpills(type)) /* gprs */ + - POPCOUNT(MipsCalleeSaveFPSpills(type)) /* fprs */ + - 1 /* Method* */) * static_cast(kMipsPointerSize), kStackAlignment); -} + static constexpr uint32_t GetFrameSize(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return RoundUp((POPCOUNT(GetCoreSpills(type)) /* gprs */ + + POPCOUNT(GetFpSpills(type)) /* fprs */ + + 1 /* Method* */) * static_cast(kMipsPointerSize), kStackAlignment); + } -constexpr QuickMethodFrameInfo MipsCalleeSaveMethodFrameInfo(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return QuickMethodFrameInfo(MipsCalleeSaveFrameSize(type), - MipsCalleeSaveCoreSpills(type), - MipsCalleeSaveFPSpills(type)); -} + static constexpr QuickMethodFrameInfo GetMethodFrameInfo(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return QuickMethodFrameInfo(GetFrameSize(type), GetCoreSpills(type), GetFpSpills(type)); + } + + static constexpr size_t GetFpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + (POPCOUNT(GetCoreSpills(type)) + + POPCOUNT(GetFpSpills(type))) * static_cast(kMipsPointerSize); + } + + static constexpr size_t GetGpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + POPCOUNT(GetCoreSpills(type)) * static_cast(kMipsPointerSize); + } + + static constexpr size_t GetReturnPcOffset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - static_cast(kMipsPointerSize); + } +}; } // namespace mips } // namespace art -#endif // ART_RUNTIME_ARCH_MIPS_QUICK_METHOD_FRAME_INFO_MIPS_H_ +#endif // ART_RUNTIME_ARCH_MIPS_CALLEE_SAVE_FRAME_MIPS_H_ diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index 9418caf98cbd7935ce125ccc9564f397306dad26..2b69c1753bcf198fedf445ae3afefdee57437a4f 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -203,6 +203,10 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { static_assert(!IsDirectEntrypoint(kQuickInitializeType), "Non-direct C stub marked direct."); qpoints->pResolveString = art_quick_resolve_string; static_assert(!IsDirectEntrypoint(kQuickResolveString), "Non-direct C stub marked direct."); + qpoints->pResolveMethodHandle = art_quick_resolve_method_handle; + static_assert(!IsDirectEntrypoint(kQuickResolveMethodHandle), "Non-direct C stub marked direct."); + qpoints->pResolveMethodType = art_quick_resolve_method_type; + static_assert(!IsDirectEntrypoint(kQuickResolveMethodType), "Non-direct C stub marked direct."); // Field qpoints->pSet8Instance = art_quick_set8_instance; @@ -406,6 +410,9 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { 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/fault_handler_mips.cc b/runtime/arch/mips/fault_handler_mips.cc index f82dc08cb25bea19d632e073af6f39136030993d..7c8ac288c305229000a794e74967278b4987ee55 100644 --- a/runtime/arch/mips/fault_handler_mips.cc +++ b/runtime/arch/mips/fault_handler_mips.cc @@ -17,13 +17,13 @@ #include #include "fault_handler.h" +#include "arch/mips/callee_save_frame_mips.h" #include "art_method.h" #include "base/callee_save_type.h" +#include "base/globals.h" #include "base/hex_dump.h" #include "base/logging.h" // For VLOG. #include "base/macros.h" -#include "globals.h" -#include "quick_method_frame_info_mips.h" #include "registers_mips.h" #include "thread-current-inl.h" diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index b2f7e10f520684e1ef96294eb577e7ef0b5b76cd..303333cd0e1a98265c16128eeff09488392d4c6d 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -1423,6 +1423,10 @@ END art_quick_unlock_object_no_inline .extern artInstanceOfFromCode .extern artThrowClassCastExceptionForObject ENTRY art_quick_check_instance_of + // Type check using the bit string passes null as the target class. In that case just throw. + beqz $a1, .Lthrow_class_cast_exception_for_bitstring_check + nop + addiu $sp, $sp, -32 .cfi_adjust_cfa_offset 32 sw $gp, 16($sp) @@ -1441,12 +1445,15 @@ ENTRY art_quick_check_instance_of jalr $zero, $ra addiu $sp, $sp, 32 .cfi_adjust_cfa_offset -32 + .Lthrow_class_cast_exception: lw $t9, 8($sp) lw $a1, 4($sp) lw $a0, 0($sp) addiu $sp, $sp, 32 .cfi_adjust_cfa_offset -32 + +.Lthrow_class_cast_exception_for_bitstring_check: SETUP_SAVE_ALL_CALLEE_SAVES_FRAME la $t9, artThrowClassCastExceptionForObject jalr $zero, $t9 # artThrowClassCastException (Object*, Class*, Thread*) @@ -1717,6 +1724,7 @@ GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_region_tlab, RegionTLAB) @@ -1729,6 +1737,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_tlab, TLAB) @@ -2020,8 +2029,11 @@ GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved16_tlab, artAllocArrayFr GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_32 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_64 -// Macro for string and type resolution and initialization. -// $a0 is both input and output. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. $a0 is both input and + * output. + */ .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET .extern \entrypoint ENTRY_NO_GP \name @@ -2045,6 +2057,18 @@ END \name ONE_ARG_SAVE_EVERYTHING_DOWNCALL \name, \entrypoint, RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET .endm + /* + * Entry from managed code to resolve a method handle. On entry, A0 holds the method handle + * index. On success the MethodHandle is returned, otherwise an exception is raised. + */ +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode + + /* + * Entry from managed code to resolve a method type. On entry, A0 holds the method type index. + * On success the MethodType is returned, otherwise an exception is raised. + */ +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode + /* * Entry from managed code to resolve a string, this stub will allocate a String and deliver an * exception on error. On success the String is returned. A0 holds the string index. The fast @@ -3224,59 +3248,48 @@ art_quick_read_barrier_mark_introspection_end_of_entries: 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/mips/registers_mips.h b/runtime/arch/mips/registers_mips.h index c7f9a3e74eccc3f8644dfc86da823e3da36c7f9c..34f2f9684d5156f6471f15ae0bc99819e598de17 100644 --- a/runtime/arch/mips/registers_mips.h +++ b/runtime/arch/mips/registers_mips.h @@ -21,8 +21,8 @@ #include +#include "base/globals.h" #include "base/macros.h" -#include "globals.h" namespace art { namespace mips { diff --git a/runtime/arch/mips64/quick_method_frame_info_mips64.h b/runtime/arch/mips64/callee_save_frame_mips64.h similarity index 61% rename from runtime/arch/mips64/quick_method_frame_info_mips64.h rename to runtime/arch/mips64/callee_save_frame_mips64.h index 520f6319d52de70d0da5ba66ee7d0f28b509b13c..59529a0c7b94b68ba74152dbf9927db9d0af9dcc 100644 --- a/runtime/arch/mips64/quick_method_frame_info_mips64.h +++ b/runtime/arch/mips64/callee_save_frame_mips64.h @@ -14,13 +14,14 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_ARCH_MIPS64_QUICK_METHOD_FRAME_INFO_MIPS64_H_ -#define ART_RUNTIME_ARCH_MIPS64_QUICK_METHOD_FRAME_INFO_MIPS64_H_ +#ifndef ART_RUNTIME_ARCH_MIPS64_CALLEE_SAVE_FRAME_MIPS64_H_ +#define ART_RUNTIME_ARCH_MIPS64_CALLEE_SAVE_FRAME_MIPS64_H_ #include "arch/instruction_set.h" #include "base/bit_utils.h" #include "base/callee_save_type.h" #include "base/enums.h" +#include "base/globals.h" #include "quick/quick_method_frame_info.h" #include "registers_mips64.h" @@ -71,37 +72,56 @@ static constexpr uint32_t kMips64CalleeSaveFpEverythingSpills = (1 << art::mips64::F27) | (1 << art::mips64::F28) | (1 << art::mips64::F29) | (1 << art::mips64::F30) | (1 << art::mips64::F31); -constexpr uint32_t Mips64CalleeSaveCoreSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kMips64CalleeSaveAlwaysSpills | kMips64CalleeSaveRefSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kMips64CalleeSaveArgSpills : 0) | - (type == CalleeSaveType::kSaveAllCalleeSaves ? kMips64CalleeSaveAllSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kMips64CalleeSaveEverythingSpills : 0); -} +class Mips64CalleeSaveFrame { + public: + static constexpr uint32_t GetCoreSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kMips64CalleeSaveAlwaysSpills | kMips64CalleeSaveRefSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kMips64CalleeSaveArgSpills : 0) | + (type == CalleeSaveType::kSaveAllCalleeSaves ? kMips64CalleeSaveAllSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kMips64CalleeSaveEverythingSpills : 0); + } -constexpr uint32_t Mips64CalleeSaveFpSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kMips64CalleeSaveFpRefSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kMips64CalleeSaveFpArgSpills : 0) | - (type == CalleeSaveType::kSaveAllCalleeSaves ? kMips64CalleeSaveFpAllSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kMips64CalleeSaveFpEverythingSpills : 0); -} + static constexpr uint32_t GetFpSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kMips64CalleeSaveFpRefSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kMips64CalleeSaveFpArgSpills : 0) | + (type == CalleeSaveType::kSaveAllCalleeSaves ? kMips64CalleeSaveFpAllSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kMips64CalleeSaveFpEverythingSpills : 0); + } -constexpr uint32_t Mips64CalleeSaveFrameSize(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return RoundUp((POPCOUNT(Mips64CalleeSaveCoreSpills(type)) /* gprs */ + - POPCOUNT(Mips64CalleeSaveFpSpills(type)) /* fprs */ + - + 1 /* Method* */) * static_cast(kMips64PointerSize), kStackAlignment); -} + static constexpr uint32_t GetFrameSize(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return RoundUp((POPCOUNT(GetCoreSpills(type)) /* gprs */ + + POPCOUNT(GetFpSpills(type)) /* fprs */ + + + 1 /* Method* */) * static_cast(kMips64PointerSize), kStackAlignment); + } -constexpr QuickMethodFrameInfo Mips64CalleeSaveMethodFrameInfo(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return QuickMethodFrameInfo(Mips64CalleeSaveFrameSize(type), - Mips64CalleeSaveCoreSpills(type), - Mips64CalleeSaveFpSpills(type)); -} + static constexpr QuickMethodFrameInfo GetMethodFrameInfo(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return QuickMethodFrameInfo(GetFrameSize(type), GetCoreSpills(type), GetFpSpills(type)); + } + + static constexpr size_t GetFpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + (POPCOUNT(GetCoreSpills(type)) + + POPCOUNT(GetFpSpills(type))) * static_cast(kMips64PointerSize); + } + + static constexpr size_t GetGpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + POPCOUNT(GetCoreSpills(type)) * static_cast(kMips64PointerSize); + } + + static constexpr size_t GetReturnPcOffset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - static_cast(kMips64PointerSize); + } +}; } // namespace mips64 } // namespace art -#endif // ART_RUNTIME_ARCH_MIPS64_QUICK_METHOD_FRAME_INFO_MIPS64_H_ +#endif // ART_RUNTIME_ARCH_MIPS64_CALLEE_SAVE_FRAME_MIPS64_H_ diff --git a/runtime/arch/mips64/fault_handler_mips64.cc b/runtime/arch/mips64/fault_handler_mips64.cc index ba6fff05adfa452ee7e2e2741eed5cf93624bdb7..85f3528ec429a319e91be6105f74bc6a15e64a1c 100644 --- a/runtime/arch/mips64/fault_handler_mips64.cc +++ b/runtime/arch/mips64/fault_handler_mips64.cc @@ -18,13 +18,13 @@ #include +#include "arch/mips64/callee_save_frame_mips64.h" #include "art_method.h" #include "base/callee_save_type.h" +#include "base/globals.h" #include "base/hex_dump.h" #include "base/logging.h" // For VLOG. #include "base/macros.h" -#include "globals.h" -#include "quick_method_frame_info_mips64.h" #include "registers_mips64.h" #include "thread-current-inl.h" diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 58e0e4481314e1010399bdc0d3308dde8bb197e8..f35cb16b0301c675f9334cf13cc6d1a5b24293c4 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1364,6 +1364,9 @@ END art_quick_unlock_object_no_inline .extern artInstanceOfFromCode .extern artThrowClassCastExceptionForObject ENTRY art_quick_check_instance_of + // Type check using the bit string passes null as the target class. In that case just throw. + beqzc $a1, .Lthrow_class_cast_exception_for_bitstring_check + daddiu $sp, $sp, -32 .cfi_adjust_cfa_offset 32 sd $ra, 24($sp) @@ -1379,12 +1382,15 @@ ENTRY art_quick_check_instance_of jalr $zero, $ra daddiu $sp, $sp, 32 .cfi_adjust_cfa_offset -32 + .Lthrow_class_cast_exception: ld $t9, 16($sp) ld $a1, 8($sp) ld $a0, 0($sp) daddiu $sp, $sp, 32 .cfi_adjust_cfa_offset -32 + +.Lthrow_class_cast_exception_for_bitstring_check: SETUP_GP SETUP_SAVE_ALL_CALLEE_SAVES_FRAME dla $t9, artThrowClassCastExceptionForObject @@ -1642,6 +1648,7 @@ GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_region_tlab, RegionTLAB) @@ -1654,6 +1661,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_tlab, TLAB) @@ -1924,8 +1932,11 @@ GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved16_tlab, artAllocArrayFr GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_32 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_64 -// Macro for string and type resolution and initialization. -// $a0 is both input and output. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. $a0 is both input and + * output. + */ .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET .extern \entrypoint ENTRY_NO_GP \name @@ -1946,6 +1957,18 @@ END \name ONE_ARG_SAVE_EVERYTHING_DOWNCALL \name, \entrypoint, RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET .endm + /* + * Entry from managed code to resolve a method handle. On entry, A0 holds the method handle + * index. On success the MethodHandle is returned, otherwise an exception is raised. + */ +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode + + /* + * Entry from managed code to resolve a method type. On entry, A0 holds the method type index. + * On success the MethodType is returned, otherwise an exception is raised. + */ +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode + /* * Entry from managed code to resolve a string, this stub will allocate a String and deliver an * exception on error. On success the String is returned. A0 holds the string index. The fast @@ -3025,61 +3048,49 @@ art_quick_read_barrier_mark_introspection_end_of_entries: 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/mips64/registers_mips64.h b/runtime/arch/mips64/registers_mips64.h index d3a24b6202b0f6218f44288be1478bfd8bda2466..a3fa2ac426968a95022d06feda7f20cae80c6db6 100644 --- a/runtime/arch/mips64/registers_mips64.h +++ b/runtime/arch/mips64/registers_mips64.h @@ -21,8 +21,8 @@ #include +#include "base/globals.h" #include "base/macros.h" -#include "globals.h" namespace art { namespace mips64 { diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S index c091b0e0d544725f94e40508a1d36ea5137e9b6e..32888edf7ba9b027705f69eb326f642d6a5f0d3c 100644 --- a/runtime/arch/quick_alloc_entrypoints.S +++ b/runtime/arch/quick_alloc_entrypoints.S @@ -22,6 +22,8 @@ ONE_ARG_DOWNCALL art_quick_alloc_object_initialized\c_suffix, artAllocObjectFrom // Called by managed code to allocate an object when the caller doesn't know whether it has access // to the created type. ONE_ARG_DOWNCALL art_quick_alloc_object_with_checks\c_suffix, artAllocObjectFromCodeWithChecks\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER +// Called by managed code to allocate a string if it could not be removed by any optimizations +ONE_ARG_DOWNCALL art_quick_alloc_string_object\c_suffix, artAllocStringObject\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // Called by managed code to allocate an array of a resolve class. TWO_ARG_DOWNCALL art_quick_alloc_array_resolved\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // Called by managed code to allocate a string from bytes @@ -61,6 +63,8 @@ GENERATE_ALLOC_ENTRYPOINTS _region_tlab_instrumented, RegionTLABInstrumented ONE_ARG_DOWNCALL art_quick_alloc_object_initialized ## c_suffix, artAllocObjectFromCodeInitialized ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \ ONE_ARG_DOWNCALL art_quick_alloc_object_with_checks ## c_suffix, artAllocObjectFromCodeWithChecks ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER +#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(c_suffix, cxx_suffix) \ + ONE_ARG_DOWNCALL art_quick_alloc_string_object ## c_suffix, artAllocStringObject ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(c_suffix, cxx_suffix) \ FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes ## c_suffix, artAllocStringFromBytesFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(c_suffix, cxx_suffix) \ @@ -83,6 +87,7 @@ GENERATE_ALLOC_ENTRYPOINTS _region_tlab_instrumented, RegionTLABInstrumented // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_region_tlab, RegionTLAB) @@ -98,6 +103,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_tlab, TLAB) @@ -117,6 +123,7 @@ GENERATE_ALLOC_ENTRYPOINTS_FOR_TLAB_ALLOCATOR GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_dlmalloc, DlMalloc) @@ -129,6 +136,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_dlmalloc_instrumented, DlMallocInstrumented) @@ -142,6 +150,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc_instrumented, DlMa // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_rosalloc, RosAlloc) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_rosalloc, RosAlloc) @@ -154,6 +163,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_rosalloc_instrumented, RosAllocInstrumented) @@ -166,6 +176,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc_instrumented, RosA GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_bump_pointer, BumpPointer) @@ -178,6 +189,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_bump_pointer_instrumented, BumpPointerInstrumented) @@ -190,6 +202,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer_instrumented, GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_tlab_instrumented, TLABInstrumented) @@ -202,6 +215,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab_instrumented, TLABInst GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region, Region) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_region, Region) @@ -214,6 +228,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_region_instrumented, RegionInstrumented) @@ -226,6 +241,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_instrumented, Region GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_region_tlab_instrumented, RegionTLABInstrumented) diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index 4be4b126119859dd9270f33a4a2b377cbda9fb22..b0c0e43e35ad115e65a8279fb2c82050e8a468df 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -21,10 +21,11 @@ #include "base/callee_save_type.h" #include "base/enums.h" #include "class_linker-inl.h" +#include "class_root.h" #include "common_runtime_test.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "imt_conflict_table.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "linear_alloc.h" #include "mirror/class-inl.h" #include "mirror/string-inl.h" @@ -2096,7 +2097,7 @@ TEST_F(StubTest, ReadBarrierForRoot) { EXPECT_FALSE(self->IsExceptionPending()); - GcRoot& root = mirror::String::java_lang_String_; + GcRoot root(GetClassRoot()); size_t result = Invoke3(reinterpret_cast(&root), 0U, 0U, readBarrierForRootSlow, self); EXPECT_FALSE(self->IsExceptionPending()); diff --git a/runtime/arch/x86/callee_save_frame_x86.h b/runtime/arch/x86/callee_save_frame_x86.h new file mode 100644 index 0000000000000000000000000000000000000000..f336f43aa3ff95f95b4739b1e413420cec967e02 --- /dev/null +++ b/runtime/arch/x86/callee_save_frame_x86.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_ARCH_X86_CALLEE_SAVE_FRAME_X86_H_ +#define ART_RUNTIME_ARCH_X86_CALLEE_SAVE_FRAME_X86_H_ + +#include "arch/instruction_set.h" +#include "base/bit_utils.h" +#include "base/callee_save_type.h" +#include "base/enums.h" +#include "base/globals.h" +#include "quick/quick_method_frame_info.h" +#include "registers_x86.h" + +namespace art { +namespace x86 { + +static constexpr uint32_t kX86CalleeSaveAlwaysSpills = + (1 << art::x86::kNumberOfCpuRegisters); // Fake return address callee save. +static constexpr uint32_t kX86CalleeSaveRefSpills = + (1 << art::x86::EBP) | (1 << art::x86::ESI) | (1 << art::x86::EDI); +static constexpr uint32_t kX86CalleeSaveArgSpills = + (1 << art::x86::ECX) | (1 << art::x86::EDX) | (1 << art::x86::EBX); +static constexpr uint32_t kX86CalleeSaveEverythingSpills = + (1 << art::x86::EAX) | (1 << art::x86::ECX) | (1 << art::x86::EDX) | (1 << art::x86::EBX); + +static constexpr uint32_t kX86CalleeSaveFpArgSpills = + (1 << art::x86::XMM0) | (1 << art::x86::XMM1) | + (1 << art::x86::XMM2) | (1 << art::x86::XMM3); +static constexpr uint32_t kX86CalleeSaveFpEverythingSpills = + (1 << art::x86::XMM0) | (1 << art::x86::XMM1) | + (1 << art::x86::XMM2) | (1 << art::x86::XMM3) | + (1 << art::x86::XMM4) | (1 << art::x86::XMM5) | + (1 << art::x86::XMM6) | (1 << art::x86::XMM7); + +class X86CalleeSaveFrame { + public: + static constexpr uint32_t GetCoreSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kX86CalleeSaveAlwaysSpills | kX86CalleeSaveRefSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kX86CalleeSaveArgSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kX86CalleeSaveEverythingSpills : 0); + } + + static constexpr uint32_t GetFpSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return (type == CalleeSaveType::kSaveRefsAndArgs ? kX86CalleeSaveFpArgSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kX86CalleeSaveFpEverythingSpills : 0); + } + + static constexpr uint32_t GetFrameSize(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return RoundUp((POPCOUNT(GetCoreSpills(type)) /* gprs */ + + 2 * POPCOUNT(GetFpSpills(type)) /* fprs */ + + 1 /* Method* */) * static_cast(kX86PointerSize), kStackAlignment); + } + + static constexpr QuickMethodFrameInfo GetMethodFrameInfo(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return QuickMethodFrameInfo(GetFrameSize(type), GetCoreSpills(type), GetFpSpills(type)); + } + + static constexpr size_t GetFpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + (POPCOUNT(GetCoreSpills(type)) + + 2 * POPCOUNT(GetFpSpills(type))) * static_cast(kX86PointerSize); + } + + static constexpr size_t GetGpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + POPCOUNT(GetCoreSpills(type)) * static_cast(kX86PointerSize); + } + + static constexpr size_t GetReturnPcOffset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - static_cast(kX86PointerSize); + } +}; + +} // namespace x86 +} // namespace art + +#endif // ART_RUNTIME_ARCH_X86_CALLEE_SAVE_FRAME_X86_H_ diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc index e6a91247cbef13a0c16681d770193e5e0d685011..8b243342f900f2e8f42b4db3230188fef8463502 100644 --- a/runtime/arch/x86/fault_handler_x86.cc +++ b/runtime/arch/x86/fault_handler_x86.cc @@ -20,11 +20,11 @@ #include "art_method.h" #include "base/enums.h" +#include "base/globals.h" #include "base/hex_dump.h" #include "base/logging.h" // For VLOG. #include "base/macros.h" #include "base/safe_copy.h" -#include "globals.h" #include "thread-current-inl.h" #if defined(__APPLE__) diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 5c4ae4ea12cd39ba4f711adbb69acb2dc399b7b3..9fe41ca83ba406bb5404f0711cdda98de5994364 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -923,7 +923,10 @@ MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) END_FUNCTION VAR(c_name) END_MACRO -// Macro for string and type resolution and initialization. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. + */ MACRO3(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET) DEFINE_FUNCTION VAR(c_name) SETUP_SAVE_EVERYTHING_FRAME ebx, ebx, \runtime_method_offset // save ref containing registers for GC @@ -932,7 +935,7 @@ MACRO3(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name, runtime_method_offset CFI_ADJUST_CFA_OFFSET(8) pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) - PUSH eax // pass arg1 + PUSH eax // pass the index of the constant as arg1 call CALLVAR(cxx_name) // cxx_name(arg1, Thread*) addl MACRO_LITERAL(16), %esp // pop arguments CFI_ADJUST_CFA_OFFSET(-16) @@ -984,6 +987,7 @@ GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_region_tlab, RegionTLAB) @@ -996,6 +1000,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_tlab, TLAB) @@ -1278,6 +1283,8 @@ GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFr ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO @@ -1287,7 +1294,7 @@ DEFINE_FUNCTION art_quick_lock_object jz .Lslow_lock .Lretry_lock: movl MIRROR_OBJECT_LOCK_WORD_OFFSET(%eax), %ecx // ecx := lock word - test LITERAL(LOCK_WORD_STATE_MASK), %ecx // test the 2 high bits. + test LITERAL(LOCK_WORD_STATE_MASK_SHIFTED), %ecx // test the 2 high bits. jne .Lslow_lock // slow path if either of the two high bits are set. movl %ecx, %edx // save lock word (edx) to keep read barrier bits. andl LITERAL(LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED), %ecx // zero the gc bits. @@ -1357,7 +1364,7 @@ DEFINE_FUNCTION art_quick_unlock_object .Lretry_unlock: movl MIRROR_OBJECT_LOCK_WORD_OFFSET(%eax), %ecx // ecx := lock word movl %fs:THREAD_ID_OFFSET, %edx // edx := thread id - test LITERAL(LOCK_WORD_STATE_MASK), %ecx + test LITERAL(LOCK_WORD_STATE_MASK_SHIFTED), %ecx jnz .Lslow_unlock // lock word contains a monitor cmpw %cx, %dx // does the thread id match? jne .Lslow_unlock @@ -1432,6 +1439,10 @@ DEFINE_FUNCTION art_quick_instance_of END_FUNCTION art_quick_instance_of DEFINE_FUNCTION art_quick_check_instance_of + // Type check using the bit string passes null as the target class. In that case just throw. + testl %ecx, %ecx + jz .Lthrow_class_cast_exception_for_bitstring_check + PUSH eax // alignment padding PUSH ecx // pass arg2 - checked class PUSH eax // pass arg1 - obj @@ -1449,6 +1460,7 @@ DEFINE_FUNCTION art_quick_check_instance_of addl LITERAL(4), %esp CFI_ADJUST_CFA_OFFSET(-4) +.Lthrow_class_cast_exception_for_bitstring_check: SETUP_SAVE_ALL_CALLEE_SAVES_FRAME ebx, ebx // save all registers as basis for long jump context // Outgoing argument set up PUSH eax // alignment padding @@ -2424,99 +2436,49 @@ DEFINE_FUNCTION art_quick_osr_stub 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/quick_method_frame_info_x86.h b/runtime/arch/x86/quick_method_frame_info_x86.h deleted file mode 100644 index 9a6633365c18c0aca61ffb7874be0a82153fd0fc..0000000000000000000000000000000000000000 --- a/runtime/arch/x86/quick_method_frame_info_x86.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_ARCH_X86_QUICK_METHOD_FRAME_INFO_X86_H_ -#define ART_RUNTIME_ARCH_X86_QUICK_METHOD_FRAME_INFO_X86_H_ - -#include "arch/instruction_set.h" -#include "base/bit_utils.h" -#include "base/callee_save_type.h" -#include "base/enums.h" -#include "quick/quick_method_frame_info.h" -#include "registers_x86.h" - -namespace art { -namespace x86 { - -enum XMM { - XMM0 = 0, - XMM1 = 1, - XMM2 = 2, - XMM3 = 3, - XMM4 = 4, - XMM5 = 5, - XMM6 = 6, - XMM7 = 7, -}; - -static constexpr uint32_t kX86CalleeSaveAlwaysSpills = - (1 << art::x86::kNumberOfCpuRegisters); // Fake return address callee save. -static constexpr uint32_t kX86CalleeSaveRefSpills = - (1 << art::x86::EBP) | (1 << art::x86::ESI) | (1 << art::x86::EDI); -static constexpr uint32_t kX86CalleeSaveArgSpills = - (1 << art::x86::ECX) | (1 << art::x86::EDX) | (1 << art::x86::EBX); -static constexpr uint32_t kX86CalleeSaveEverythingSpills = - (1 << art::x86::EAX) | (1 << art::x86::ECX) | (1 << art::x86::EDX) | (1 << art::x86::EBX); - -static constexpr uint32_t kX86CalleeSaveFpArgSpills = - (1 << art::x86::XMM0) | (1 << art::x86::XMM1) | - (1 << art::x86::XMM2) | (1 << art::x86::XMM3); -static constexpr uint32_t kX86CalleeSaveFpEverythingSpills = - (1 << art::x86::XMM0) | (1 << art::x86::XMM1) | - (1 << art::x86::XMM2) | (1 << art::x86::XMM3) | - (1 << art::x86::XMM4) | (1 << art::x86::XMM5) | - (1 << art::x86::XMM6) | (1 << art::x86::XMM7); - -constexpr uint32_t X86CalleeSaveCoreSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kX86CalleeSaveAlwaysSpills | kX86CalleeSaveRefSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kX86CalleeSaveArgSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kX86CalleeSaveEverythingSpills : 0); -} - -constexpr uint32_t X86CalleeSaveFpSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return (type == CalleeSaveType::kSaveRefsAndArgs ? kX86CalleeSaveFpArgSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kX86CalleeSaveFpEverythingSpills : 0); -} - -constexpr uint32_t X86CalleeSaveFrameSize(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return RoundUp((POPCOUNT(X86CalleeSaveCoreSpills(type)) /* gprs */ + - 2 * POPCOUNT(X86CalleeSaveFpSpills(type)) /* fprs */ + - 1 /* Method* */) * static_cast(kX86PointerSize), kStackAlignment); -} - -constexpr QuickMethodFrameInfo X86CalleeSaveMethodFrameInfo(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return QuickMethodFrameInfo(X86CalleeSaveFrameSize(type), - X86CalleeSaveCoreSpills(type), - X86CalleeSaveFpSpills(type)); -} - -} // namespace x86 -} // namespace art - -#endif // ART_RUNTIME_ARCH_X86_QUICK_METHOD_FRAME_INFO_X86_H_ diff --git a/runtime/arch/x86/registers_x86.h b/runtime/arch/x86/registers_x86.h index ded3520c76eea9f9c5cac8c7b08690f1a36483a2..d3b959fc534a5b963325534e0bcdbf89123d3bfc 100644 --- a/runtime/arch/x86/registers_x86.h +++ b/runtime/arch/x86/registers_x86.h @@ -21,8 +21,8 @@ #include +#include "base/globals.h" #include "base/macros.h" -#include "globals.h" namespace art { namespace x86 { @@ -42,6 +42,20 @@ enum Register { }; std::ostream& operator<<(std::ostream& os, const Register& rhs); +enum XmmRegister { + XMM0 = 0, + XMM1 = 1, + XMM2 = 2, + XMM3 = 3, + XMM4 = 4, + XMM5 = 5, + XMM6 = 6, + XMM7 = 7, + kNumberOfXmmRegisters = 8, + kNoXmmRegister = -1 // Signals an illegal register. +}; +std::ostream& operator<<(std::ostream& os, const XmmRegister& reg); + } // namespace x86 } // namespace art diff --git a/runtime/arch/x86_64/quick_method_frame_info_x86_64.h b/runtime/arch/x86_64/callee_save_frame_x86_64.h similarity index 54% rename from runtime/arch/x86_64/quick_method_frame_info_x86_64.h rename to runtime/arch/x86_64/callee_save_frame_x86_64.h index ebf976ef71fa01902ca38ba0ad64ff718a565be1..228a902d38a637ef12038fb747c4844d1efb3fcd 100644 --- a/runtime/arch/x86_64/quick_method_frame_info_x86_64.h +++ b/runtime/arch/x86_64/callee_save_frame_x86_64.h @@ -14,13 +14,14 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_ARCH_X86_64_QUICK_METHOD_FRAME_INFO_X86_64_H_ -#define ART_RUNTIME_ARCH_X86_64_QUICK_METHOD_FRAME_INFO_X86_64_H_ +#ifndef ART_RUNTIME_ARCH_X86_64_CALLEE_SAVE_FRAME_X86_64_H_ +#define ART_RUNTIME_ARCH_X86_64_CALLEE_SAVE_FRAME_X86_64_H_ #include "arch/instruction_set.h" #include "base/bit_utils.h" #include "base/callee_save_type.h" #include "base/enums.h" +#include "base/globals.h" #include "quick/quick_method_frame_info.h" #include "registers_x86_64.h" @@ -55,35 +56,54 @@ static constexpr uint32_t kX86_64CalleeSaveFpEverythingSpills = (1 << art::x86_64::XMM8) | (1 << art::x86_64::XMM9) | (1 << art::x86_64::XMM10) | (1 << art::x86_64::XMM11); -constexpr uint32_t X86_64CalleeSaveCoreSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kX86_64CalleeSaveAlwaysSpills | kX86_64CalleeSaveRefSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kX86_64CalleeSaveArgSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kX86_64CalleeSaveEverythingSpills : 0); -} +class X86_64CalleeSaveFrame { + public: + static constexpr uint32_t GetCoreSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kX86_64CalleeSaveAlwaysSpills | kX86_64CalleeSaveRefSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kX86_64CalleeSaveArgSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kX86_64CalleeSaveEverythingSpills : 0); + } -constexpr uint32_t X86_64CalleeSaveFpSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kX86_64CalleeSaveFpSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kX86_64CalleeSaveFpArgSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kX86_64CalleeSaveFpEverythingSpills : 0); -} + static constexpr uint32_t GetFpSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kX86_64CalleeSaveFpSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kX86_64CalleeSaveFpArgSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kX86_64CalleeSaveFpEverythingSpills : 0); + } -constexpr uint32_t X86_64CalleeSaveFrameSize(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return RoundUp((POPCOUNT(X86_64CalleeSaveCoreSpills(type)) /* gprs */ + - POPCOUNT(X86_64CalleeSaveFpSpills(type)) /* fprs */ + - 1 /* Method* */) * static_cast(kX86_64PointerSize), kStackAlignment); -} + static constexpr uint32_t GetFrameSize(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return RoundUp((POPCOUNT(GetCoreSpills(type)) /* gprs */ + + POPCOUNT(GetFpSpills(type)) /* fprs */ + + 1 /* Method* */) * static_cast(kX86_64PointerSize), kStackAlignment); + } -constexpr QuickMethodFrameInfo X86_64CalleeSaveMethodFrameInfo(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return QuickMethodFrameInfo(X86_64CalleeSaveFrameSize(type), - X86_64CalleeSaveCoreSpills(type), - X86_64CalleeSaveFpSpills(type)); -} + static constexpr QuickMethodFrameInfo GetMethodFrameInfo(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return QuickMethodFrameInfo(GetFrameSize(type), GetCoreSpills(type), GetFpSpills(type)); + } + + static constexpr size_t GetFpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + (POPCOUNT(GetCoreSpills(type)) + + POPCOUNT(GetFpSpills(type))) * static_cast(kX86_64PointerSize); + } + + static constexpr size_t GetGpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + POPCOUNT(GetCoreSpills(type)) * static_cast(kX86_64PointerSize); + } + + static constexpr size_t GetReturnPcOffset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - static_cast(kX86_64PointerSize); + } +}; } // namespace x86_64 } // namespace art -#endif // ART_RUNTIME_ARCH_X86_64_QUICK_METHOD_FRAME_INFO_X86_64_H_ +#endif // ART_RUNTIME_ARCH_X86_64_CALLEE_SAVE_FRAME_X86_64_H_ diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index a8132006060fbba874c9cbc2b2cb0267c9b1f1f3..c41d3e4685fa69dfee96f165d9acdde723a69e7b 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -951,12 +951,15 @@ MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) END_FUNCTION VAR(c_name) END_MACRO -// Macro for string and type resolution and initialization. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. + */ MACRO3(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET) DEFINE_FUNCTION VAR(c_name) SETUP_SAVE_EVERYTHING_FRAME \runtime_method_offset // save everything for GC // Outgoing argument set up - movl %eax, %edi // pass string index + movl %eax, %edi // pass the index of the constant as arg0 movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current() call CALLVAR(cxx_name) // cxx_name(arg0, Thread*) testl %eax, %eax // If result is null, deliver the OOME. @@ -1008,6 +1011,7 @@ GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_region_tlab, RegionTLAB) @@ -1020,6 +1024,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_tlab, TLAB) @@ -1298,6 +1303,8 @@ END_FUNCTION art_quick_alloc_object_initialized_region_tlab ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO @@ -1307,7 +1314,7 @@ DEFINE_FUNCTION art_quick_lock_object jz .Lslow_lock .Lretry_lock: movl MIRROR_OBJECT_LOCK_WORD_OFFSET(%edi), %ecx // ecx := lock word. - test LITERAL(LOCK_WORD_STATE_MASK), %ecx // Test the 2 high bits. + test LITERAL(LOCK_WORD_STATE_MASK_SHIFTED), %ecx // Test the 2 high bits. jne .Lslow_lock // Slow path if either of the two high bits are set. movl %ecx, %edx // save lock word (edx) to keep read barrier bits. andl LITERAL(LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED), %ecx // zero the gc bits. @@ -1357,7 +1364,7 @@ DEFINE_FUNCTION art_quick_unlock_object .Lretry_unlock: movl MIRROR_OBJECT_LOCK_WORD_OFFSET(%edi), %ecx // ecx := lock word movl %gs:THREAD_ID_OFFSET, %edx // edx := thread id - test LITERAL(LOCK_WORD_STATE_MASK), %ecx + test LITERAL(LOCK_WORD_STATE_MASK_SHIFTED), %ecx jnz .Lslow_unlock // lock word contains a monitor cmpw %cx, %dx // does the thread id match? jne .Lslow_unlock @@ -1403,6 +1410,10 @@ DEFINE_FUNCTION art_quick_unlock_object_no_inline END_FUNCTION art_quick_unlock_object_no_inline DEFINE_FUNCTION art_quick_check_instance_of + // Type check using the bit string passes null as the target class. In that case just throw. + testl %esi, %esi + jz .Lthrow_class_cast_exception_for_bitstring_check + // We could check the super classes here but that is usually already checked in the caller. PUSH rdi // Save args for exc PUSH rsi @@ -1426,6 +1437,7 @@ DEFINE_FUNCTION art_quick_check_instance_of POP rsi // Pop arguments POP rdi +.Lthrow_class_cast_exception_for_bitstring_check: SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // save all registers as basis for long jump context mov %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current() call SYMBOL(artThrowClassCastExceptionForObject) // (Object* src, Class* dest, Thread*) @@ -2408,81 +2420,29 @@ DEFINE_FUNCTION art_quick_osr_stub 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/arch/x86_64/registers_x86_64.h b/runtime/arch/x86_64/registers_x86_64.h index 4f2243170e75dd70e2716c6e9d860e626e38add4..66aea705d2825c93bdf7eb7ea495426ee086a520 100644 --- a/runtime/arch/x86_64/registers_x86_64.h +++ b/runtime/arch/x86_64/registers_x86_64.h @@ -21,8 +21,8 @@ #include +#include "base/globals.h" #include "base/macros.h" -#include "globals.h" namespace art { namespace x86_64 { diff --git a/runtime/arch/x86_64/thread_x86_64.cc b/runtime/arch/x86_64/thread_x86_64.cc index 19d25f69905671f6036cf27bc57314ebd54de0fd..5c0446fb4c7a44686947ae82c1489029613536c6 100644 --- a/runtime/arch/x86_64/thread_x86_64.cc +++ b/runtime/arch/x86_64/thread_x86_64.cc @@ -25,6 +25,10 @@ #include #include #include +#elif defined(__Fuchsia__) +#include +#include +#include #endif namespace art { @@ -40,6 +44,13 @@ void Thread::InitCpu() { #if defined(__linux__) arch_prctl(ARCH_SET_GS, this); +#elif defined(__Fuchsia__) + Thread* thread_ptr = this; + zx_status_t status = zx_object_set_property(zx_thread_self(), + ZX_PROP_REGISTER_GS, + &thread_ptr, + sizeof(thread_ptr)); + CHECK_EQ(status, ZX_OK) << "failed to set GS register"; #else UNIMPLEMENTED(FATAL) << "Need to set GS"; #endif diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h index 384581fc4fafee73d3094c14876a6e8927627411..c5fb7d5f402899a5f773a5c35c9b0ada9ec50004 100644 --- a/runtime/art_field-inl.h +++ b/runtime/art_field-inl.h @@ -21,7 +21,7 @@ #include -#include "class_linker.h" +#include "class_linker-inl.h" #include "dex/dex_file-inl.h" #include "dex/primitive.h" #include "gc/accounting/card_table-inl.h" @@ -34,12 +34,16 @@ namespace art { +inline bool ArtField::IsProxyField() { + return GetDeclaringClass()->IsProxyClass(); +} + template inline ObjPtr ArtField::GetDeclaringClass() { GcRootSource gc_root_source(this); ObjPtr result = declaring_class_.Read(&gc_root_source); DCHECK(result != nullptr); - DCHECK(result->IsLoaded() || result->IsErroneous()) << result->GetStatus(); + DCHECK(result->IsIdxLoaded() || result->IsErroneous()) << result->GetStatus(); return result; } @@ -302,25 +306,21 @@ inline bool ArtField::IsPrimitiveType() REQUIRES_SHARED(Locks::mutator_lock_) { inline ObjPtr ArtField::LookupResolvedType() { ScopedAssertNoThreadSuspension ants(__FUNCTION__); - const uint32_t field_index = GetDexFieldIndex(); - ObjPtr declaring_class = GetDeclaringClass(); - if (UNLIKELY(declaring_class->IsProxyClass())) { + if (UNLIKELY(IsProxyField())) { return ProxyFindSystemClass(GetTypeDescriptor()); } ObjPtr type = Runtime::Current()->GetClassLinker()->LookupResolvedType( - declaring_class->GetDexFile().GetFieldId(field_index).type_idx_, declaring_class); + GetDexFile()->GetFieldId(GetDexFieldIndex()).type_idx_, this); DCHECK(!Thread::Current()->IsExceptionPending()); return type; } inline ObjPtr ArtField::ResolveType() { - const uint32_t field_index = GetDexFieldIndex(); - ObjPtr declaring_class = GetDeclaringClass(); - if (UNLIKELY(declaring_class->IsProxyClass())) { + if (UNLIKELY(IsProxyField())) { return ProxyFindSystemClass(GetTypeDescriptor()); } ObjPtr type = Runtime::Current()->GetClassLinker()->ResolveType( - declaring_class->GetDexFile().GetFieldId(field_index).type_idx_, declaring_class); + GetDexFile()->GetFieldId(GetDexFieldIndex()).type_idx_, this); DCHECK_EQ(type == nullptr, Thread::Current()->IsExceptionPending()); return type; } @@ -329,24 +329,21 @@ inline size_t ArtField::FieldSize() REQUIRES_SHARED(Locks::mutator_lock_) { return Primitive::ComponentSize(GetTypeAsPrimitiveType()); } +template inline ObjPtr ArtField::GetDexCache() REQUIRES_SHARED(Locks::mutator_lock_) { - return GetDeclaringClass()->GetDexCache(); + ObjPtr klass = GetDeclaringClass(); + return klass->GetDexCache(); } inline const DexFile* ArtField::GetDexFile() REQUIRES_SHARED(Locks::mutator_lock_) { - return GetDexCache()->GetDexFile(); + return GetDexCache()->GetDexFile(); } -inline ObjPtr ArtField::GetStringName(Thread* self, bool resolve) { - auto dex_field_index = GetDexFieldIndex(); +inline ObjPtr ArtField::ResolveNameString() { + uint32_t dex_field_index = GetDexFieldIndex(); CHECK_NE(dex_field_index, dex::kDexNoIndex); - ObjPtr dex_cache = GetDexCache(); - const DexFile::FieldId& field_id = dex_cache->GetDexFile()->GetFieldId(dex_field_index); - ObjPtr name = dex_cache->GetResolvedString(field_id.name_idx_); - if (resolve && name == nullptr) { - name = ResolveGetStringName(self, field_id.name_idx_, dex_cache); - } - return name; + const DexFile::FieldId& field_id = GetDexFile()->GetFieldId(dex_field_index); + return Runtime::Current()->GetClassLinker()->ResolveString(field_id.name_idx_, this); } template diff --git a/runtime/art_field.cc b/runtime/art_field.cc index b867621f02358e37e4aad480f0df63a1a48dede3..6cbd9e4cfc6df46f663a8e6ef9d9c59fe0614f75 100644 --- a/runtime/art_field.cc +++ b/runtime/art_field.cc @@ -52,13 +52,6 @@ ObjPtr ArtField::ProxyFindSystemClass(const char* descriptor) { return klass; } -ObjPtr ArtField::ResolveGetStringName(Thread* self, - dex::StringIndex string_idx, - ObjPtr dex_cache) { - StackHandleScope<1> hs(self); - return Runtime::Current()->GetClassLinker()->ResolveString(string_idx, hs.NewHandle(dex_cache)); -} - std::string ArtField::PrettyField(ArtField* f, bool with_type) { if (f == nullptr) { return "null"; diff --git a/runtime/art_field.h b/runtime/art_field.h index f39af3900c806a3118087a1b454707ae36a58655..123595c6fe2e8551abe39e63252e47ac85c2d43c 100644 --- a/runtime/art_field.h +++ b/runtime/art_field.h @@ -201,8 +201,7 @@ class ArtField FINAL { const char* GetName() REQUIRES_SHARED(Locks::mutator_lock_); // Resolves / returns the name from the dex cache. - ObjPtr GetStringName(Thread* self, bool resolve) - REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr ResolveNameString() REQUIRES_SHARED(Locks::mutator_lock_); const char* GetTypeDescriptor() REQUIRES_SHARED(Locks::mutator_lock_); @@ -215,6 +214,7 @@ class ArtField FINAL { size_t FieldSize() REQUIRES_SHARED(Locks::mutator_lock_); + template ObjPtr GetDexCache() REQUIRES_SHARED(Locks::mutator_lock_); const DexFile* GetDexFile() REQUIRES_SHARED(Locks::mutator_lock_); @@ -236,12 +236,10 @@ class ArtField FINAL { REQUIRES_SHARED(Locks::mutator_lock_); private: + bool IsProxyField() REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr ProxyFindSystemClass(const char* descriptor) REQUIRES_SHARED(Locks::mutator_lock_); - ObjPtr ResolveGetStringName(Thread* self, - dex::StringIndex string_idx, - ObjPtr dex_cache) - REQUIRES_SHARED(Locks::mutator_lock_); void GetAccessFlagsDCheck() REQUIRES_SHARED(Locks::mutator_lock_); void GetOffsetDCheck() REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index c1fac364bbd4a632821a6063530706cd5baa2c42..18595cf17adac18d8fc06969248cf67c75e9829f 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -245,6 +245,12 @@ inline const char* ArtMethod::GetName() { } } +inline ObjPtr ArtMethod::ResolveNameString() { + DCHECK(!IsProxyMethod()); + const DexFile::MethodId& method_id = GetDexFile()->GetMethodId(GetDexMethodIndex()); + return Runtime::Current()->GetClassLinker()->ResolveString(method_id.name_idx_, this); +} + inline const DexFile::CodeItem* ArtMethod::GetCodeItem() { return GetDexFile()->GetCodeItem(GetCodeItemOffset()); } @@ -324,7 +330,7 @@ inline mirror::ClassLoader* ArtMethod::GetClassLoader() { template inline mirror::DexCache* ArtMethod::GetDexCache() { if (LIKELY(!IsObsolete())) { - mirror::Class* klass = GetDeclaringClass(); + ObjPtr klass = GetDeclaringClass(); return klass->GetDexCache(); } else { DCHECK(!IsProxyMethod()); diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 7030e0657b14091cb4fb3d36259ed46b4dabf164..80b6921c8afefe4ff157a89022f9040a1e71e2b2 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -24,7 +24,9 @@ #include "art_method-inl.h" #include "base/stringpiece.h" #include "class_linker-inl.h" +#include "class_root.h" #include "debugger.h" +#include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_exception_helpers.h" @@ -35,7 +37,7 @@ #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "jit/profiling_info.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_ext.h" #include "mirror/executable.h" @@ -47,7 +49,6 @@ #include "runtime_callbacks.h" #include "scoped_thread_state_change-inl.h" #include "vdex_file.h" -#include "well_known_classes.h" namespace art { @@ -68,7 +69,7 @@ ArtMethod* ArtMethod::GetCanonicalMethod(PointerSize pointer_size) { if (LIKELY(!IsDefault())) { return this; } else { - mirror::Class* declaring_class = GetDeclaringClass(); + ObjPtr declaring_class = GetDeclaringClass(); DCHECK(declaring_class->IsInterface()); ArtMethod* ret = declaring_class->FindInterfaceMethod(declaring_class->GetDexCache(), GetDexMethodIndex(), @@ -142,16 +143,6 @@ uint16_t ArtMethod::FindObsoleteDexClassDefIndex() { return dex_file->GetIndexForClassDef(*class_def); } -ObjPtr ArtMethod::GetNameAsString(Thread* self) { - CHECK(!IsProxyMethod()); - StackHandleScope<1> hs(self); - Handle dex_cache(hs.NewHandle(GetDexCache())); - auto* dex_file = dex_cache->GetDexFile(); - uint32_t dex_method_idx = GetDexMethodIndex(); - const DexFile::MethodId& method_id = dex_file->GetMethodId(dex_method_idx); - return Runtime::Current()->GetClassLinker()->ResolveString(method_id.name_idx_, dex_cache); -} - void ArtMethod::ThrowInvocationTimeError() { DCHECK(!IsInvokable()); // NOTE: IsDefaultConflicting must be first since the actual method might or might not be abstract @@ -213,8 +204,8 @@ ArtMethod* ArtMethod::FindOverriddenMethod(PointerSize pointer_size) { if (IsStatic()) { return nullptr; } - mirror::Class* declaring_class = GetDeclaringClass(); - mirror::Class* super_class = declaring_class->GetSuperClass(); + ObjPtr declaring_class = GetDeclaringClass(); + ObjPtr super_class = declaring_class->GetSuperClass(); uint16_t method_index = GetMethodIndex(); ArtMethod* result = nullptr; // Did this method override a super class method? If so load the result from the super class' @@ -229,7 +220,7 @@ ArtMethod* ArtMethod::FindOverriddenMethod(PointerSize pointer_size) { } else { mirror::IfTable* iftable = GetDeclaringClass()->GetIfTable(); for (size_t i = 0; i < iftable->Count() && result == nullptr; i++) { - mirror::Class* interface = iftable->GetInterface(i); + ObjPtr interface = iftable->GetInterface(i); for (ArtMethod& interface_method : interface->GetVirtualMethods(pointer_size)) { if (HasSameNameAndSignature(interface_method.GetInterfaceMethodIfProxy(pointer_size))) { result = &interface_method; @@ -424,36 +415,24 @@ bool ArtMethod::IsPolymorphicSignature() { if (!IsNative() || !IsVarargs()) { return false; } - mirror::Class* cls = GetDeclaringClass(); - return (cls == WellKnownClasses::ToClass(WellKnownClasses::java_lang_invoke_MethodHandle) || - cls == WellKnownClasses::ToClass(WellKnownClasses::java_lang_invoke_VarHandle)); + ObjPtr> class_roots = + Runtime::Current()->GetClassLinker()->GetClassRoots(); + ObjPtr cls = GetDeclaringClass(); + return (cls == GetClassRoot(class_roots) || + cls == GetClassRoot(class_roots)); } static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx) { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx); - const uint8_t* class_data = dex_file.GetClassData(class_def); - CHECK(class_data != nullptr); - ClassDataItemIterator it(dex_file, class_data); - it.SkipAllFields(); - // Process methods - size_t class_def_method_index = 0; - while (it.HasNextDirectMethod()) { - if (it.GetMemberIndex() == method_idx) { - return class_def_method_index; - } - class_def_method_index++; - it.Next(); - } - while (it.HasNextVirtualMethod()) { - if (it.GetMemberIndex() == method_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) { return class_def_method_index; } class_def_method_index++; - it.Next(); } - DCHECK(!it.HasNext()); LOG(FATAL) << "Failed to find method index " << method_idx << " in " << dex_file.GetLocation(); UNREACHABLE(); } @@ -510,7 +489,7 @@ static const OatFile::OatMethod FindOatMethodFor(ArtMethod* method, } // Although we overwrite the trampoline of non-static methods, we may get here via the resolution // method for direct methods (or virtual methods made direct). - mirror::Class* declaring_class = method->GetDeclaringClass(); + ObjPtr declaring_class = method->GetDeclaringClass(); size_t oat_method_index; if (method->IsStatic() || method->IsDirect()) { // Simple case where the oat method index was stashed at load time. @@ -571,7 +550,7 @@ bool ArtMethod::EqualParameters(Handle> param ArrayRef ArtMethod::GetQuickenedInfo() { const DexFile& dex_file = GetDeclaringClass()->GetDexFile(); - const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); + const OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); if (oat_dex_file == nullptr || (oat_dex_file->GetOatFile() == nullptr)) { return ArrayRef(); } diff --git a/runtime/art_method.h b/runtime/art_method.h index 012d7067565d70bf68195e625d84652d540c8bc4..09debb0c50c2dc1d3682d15a91dc7f1936b0a238 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -589,7 +589,7 @@ class ArtMethod FINAL { ALWAYS_INLINE const char* GetName() REQUIRES_SHARED(Locks::mutator_lock_); - ObjPtr GetNameAsString(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr ResolveNameString() REQUIRES_SHARED(Locks::mutator_lock_); const DexFile::CodeItem* GetCodeItem() REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/asm_support.h b/runtime/asm_support.h index 2f7d6ab98fbdf9d177e3decf181fb7851bb02df2..e65c19495ed75beb6ff04d4d9d7bd6448a6b8198 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -73,7 +73,7 @@ ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET, // Offset of field Thread::tlsPtr_.mterp_current_ibase. #define THREAD_CURRENT_IBASE_OFFSET \ - (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 162) * __SIZEOF_POINTER__) + (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 166) * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET, art::Thread::MterpCurrentIBaseOffset().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_default_ibase. diff --git a/runtime/backtrace_helper.cc b/runtime/backtrace_helper.cc new file mode 100644 index 0000000000000000000000000000000000000000..21a0568033a637e9d3ede655ded645796c81d979 --- /dev/null +++ b/runtime/backtrace_helper.cc @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "backtrace_helper.h" + +#if defined(__linux__) + +#include +#include + +#include +#include + +#include "thread-inl.h" + +#else + +// For UNUSED +#include "base/macros.h" + +#endif + +namespace art { + +// We only really support libbacktrace on linux which is unfortunate but since this is only for +// gcstress this isn't a huge deal. +#if defined(__linux__) + +static const char* kBacktraceCollectorTlsKey = "BacktraceCollectorTlsKey"; + +struct BacktraceMapHolder : public TLSData { + BacktraceMapHolder() : map_(BacktraceMap::Create(getpid())) {} + + std::unique_ptr map_; +}; + +static BacktraceMap* GetMap(Thread* self) { + BacktraceMapHolder* map_holder = + reinterpret_cast(self->GetCustomTLS(kBacktraceCollectorTlsKey)); + if (map_holder == nullptr) { + map_holder = new BacktraceMapHolder; + // We don't care about the function names. Turning this off makes everything significantly + // faster. + map_holder->map_->SetResolveNames(false); + // Only created and queried on Thread::Current so no sync needed. + self->SetCustomTLS(kBacktraceCollectorTlsKey, map_holder); + } + + return map_holder->map_.get(); +} + +void BacktraceCollector::Collect() { + std::unique_ptr backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, + BACKTRACE_CURRENT_THREAD, + GetMap(Thread::Current()))); + backtrace->SetSkipFrames(true); + if (!backtrace->Unwind(skip_count_, nullptr)) { + return; + } + for (Backtrace::const_iterator it = backtrace->begin(); + max_depth_ > num_frames_ && it != backtrace->end(); + ++it) { + out_frames_[num_frames_++] = static_cast(it->pc); + } +} + +#else + +#pragma clang diagnostic push +#pragma clang diagnostic warning "-W#warnings" +#warning "Backtrace collector is not implemented. GCStress cannot be used." +#pragma clang diagnostic pop + +// We only have an implementation for linux. On other plaforms just return nothing. This is not +// really correct but we only use this for hashing and gcstress so it's not too big a deal. +void BacktraceCollector::Collect() { + UNUSED(skip_count_); + UNUSED(out_frames_); + UNUSED(max_depth_); + num_frames_ = 0; +} + +#endif + +} // namespace art diff --git a/runtime/backtrace_helper.h b/runtime/backtrace_helper.h index ace118c50b73a0e5fcc8cdfd8c550b4293c23b53..8eda3fa0a10b885192cb6a7b63ff996df357cef9 100644 --- a/runtime/backtrace_helper.h +++ b/runtime/backtrace_helper.h @@ -17,11 +17,12 @@ #ifndef ART_RUNTIME_BACKTRACE_HELPER_H_ #define ART_RUNTIME_BACKTRACE_HELPER_H_ -#include +#include +#include namespace art { -// Based on debug malloc logic from libc/bionic/debug_stacktrace.cpp. +// Using libbacktrace class BacktraceCollector { public: BacktraceCollector(uintptr_t* out_frames, size_t max_depth, size_t skip_count) @@ -32,25 +33,9 @@ class BacktraceCollector { } // Collect the backtrace, do not call more than once. - void Collect() { - _Unwind_Backtrace(&Callback, this); - } + void Collect(); private: - static _Unwind_Reason_Code Callback(_Unwind_Context* context, void* arg) { - auto* const state = reinterpret_cast(arg); - const uintptr_t ip = _Unwind_GetIP(context); - // The first stack frame is get_backtrace itself. Skip it. - if (ip != 0 && state->skip_count_ > 0) { - --state->skip_count_; - return _URC_NO_REASON; - } - // ip may be off for ARM but it shouldn't matter since we only use it for hashing. - state->out_frames_[state->num_frames_] = ip; - state->num_frames_++; - return state->num_frames_ >= state->max_depth_ ? _URC_END_OF_STACK : _URC_NO_REASON; - } - uintptr_t* const out_frames_ = nullptr; size_t num_frames_ = 0u; const size_t max_depth_ = 0u; diff --git a/runtime/barrier_test.cc b/runtime/barrier_test.cc index 04bb6bab1ed8557555192ae55c7bbf657b7f205f..88075ba36888d5fc8c4aeedc3f12d9437339bcdf 100644 --- a/runtime/barrier_test.cc +++ b/runtime/barrier_test.cc @@ -69,18 +69,18 @@ TEST_F(BarrierTest, CheckWait) { thread_pool.AddTask(self, new CheckWaitTask(&barrier, &count1, &count2)); } thread_pool.StartWorkers(self); - while (count1.LoadRelaxed() != num_threads) { + while (count1.load(std::memory_order_relaxed) != num_threads) { timeout_barrier.Increment(self, 1, 100); // sleep 100 msecs } // Count 2 should still be zero since no thread should have gone past the barrier. - EXPECT_EQ(0, count2.LoadRelaxed()); + EXPECT_EQ(0, count2.load(std::memory_order_relaxed)); // Perform one additional Wait(), allowing pool threads to proceed. barrier.Wait(self); // Wait for all the threads to finish. thread_pool.Wait(self, true, false); // Both counts should be equal to num_threads now. - EXPECT_EQ(count1.LoadRelaxed(), num_threads); - EXPECT_EQ(count2.LoadRelaxed(), num_threads); + EXPECT_EQ(count1.load(std::memory_order_relaxed), num_threads); + EXPECT_EQ(count2.load(std::memory_order_relaxed), num_threads); timeout_barrier.Init(self, 0); // Reset to zero for destruction. } @@ -124,7 +124,7 @@ TEST_F(BarrierTest, CheckPass) { // Wait for all the tasks to complete using the barrier. barrier.Increment(self, expected_total_tasks); // The total number of completed tasks should be equal to expected_total_tasks. - EXPECT_EQ(count.LoadRelaxed(), expected_total_tasks); + EXPECT_EQ(count.load(std::memory_order_relaxed), expected_total_tasks); } } // namespace art diff --git a/runtime/base/mem_map_arena_pool.cc b/runtime/base/mem_map_arena_pool.cc new file mode 100644 index 0000000000000000000000000000000000000000..702f0e453ba4f3da2ad8bdd2ad6140f8358d2add --- /dev/null +++ b/runtime/base/mem_map_arena_pool.cc @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mem_map_arena_pool.h" + +#include + +#include +#include +#include +#include + +#include + +#include "base/arena_allocator-inl.h" +#include "base/mem_map.h" +#include "base/systrace.h" + +namespace art { + +class MemMapArena FINAL : public Arena { + public: + MemMapArena(size_t size, bool low_4gb, const char* name); + virtual ~MemMapArena(); + void Release() OVERRIDE; + + private: + std::unique_ptr map_; +}; + +MemMapArena::MemMapArena(size_t size, bool low_4gb, const char* name) { + // Round up to a full page as that's the smallest unit of allocation for mmap() + // and we want to be able to use all memory that we actually allocate. + size = RoundUp(size, kPageSize); + std::string error_msg; + map_.reset(MemMap::MapAnonymous( + name, nullptr, size, PROT_READ | PROT_WRITE, low_4gb, false, &error_msg)); + CHECK(map_.get() != nullptr) << error_msg; + memory_ = map_->Begin(); + static_assert(ArenaAllocator::kArenaAlignment <= kPageSize, + "Arena should not need stronger alignment than kPageSize."); + DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment); + size_ = map_->Size(); +} + +MemMapArena::~MemMapArena() { + // Destroys MemMap via std::unique_ptr<>. +} + +void MemMapArena::Release() { + if (bytes_allocated_ > 0) { + map_->MadviseDontNeedAndZero(); + bytes_allocated_ = 0; + } +} + +MemMapArenaPool::MemMapArenaPool(bool low_4gb, const char* name) + : low_4gb_(low_4gb), + name_(name), + free_arenas_(nullptr) { + MemMap::Init(); +} + +MemMapArenaPool::~MemMapArenaPool() { + ReclaimMemory(); +} + +void MemMapArenaPool::ReclaimMemory() { + while (free_arenas_ != nullptr) { + Arena* arena = free_arenas_; + free_arenas_ = free_arenas_->next_; + delete arena; + } +} + +void MemMapArenaPool::LockReclaimMemory() { + std::lock_guard lock(lock_); + ReclaimMemory(); +} + +Arena* MemMapArenaPool::AllocArena(size_t size) { + Arena* ret = nullptr; + { + std::lock_guard lock(lock_); + if (free_arenas_ != nullptr && LIKELY(free_arenas_->Size() >= size)) { + ret = free_arenas_; + free_arenas_ = free_arenas_->next_; + } + } + if (ret == nullptr) { + ret = new MemMapArena(size, low_4gb_, name_); + } + ret->Reset(); + return ret; +} + +void MemMapArenaPool::TrimMaps() { + ScopedTrace trace(__PRETTY_FUNCTION__); + std::lock_guard lock(lock_); + for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) { + arena->Release(); + } +} + +size_t MemMapArenaPool::GetBytesAllocated() const { + size_t total = 0; + std::lock_guard lock(lock_); + for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) { + total += arena->GetBytesAllocated(); + } + return total; +} + +void MemMapArenaPool::FreeArenaChain(Arena* first) { + if (kRunningOnMemoryTool) { + for (Arena* arena = first; arena != nullptr; arena = arena->next_) { + MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_); + } + } + + if (arena_allocator::kArenaAllocatorPreciseTracking) { + // Do not reuse arenas when tracking. + while (first != nullptr) { + Arena* next = first->next_; + delete first; + first = next; + } + return; + } + + if (first != nullptr) { + Arena* last = first; + while (last->next_ != nullptr) { + last = last->next_; + } + std::lock_guard lock(lock_); + last->next_ = free_arenas_; + free_arenas_ = first; + } +} + +} // namespace art diff --git a/runtime/base/mem_map_arena_pool.h b/runtime/base/mem_map_arena_pool.h new file mode 100644 index 0000000000000000000000000000000000000000..24e150e1e70d767c346933c8c8025599a1a5a191 --- /dev/null +++ b/runtime/base/mem_map_arena_pool.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_BASE_MEM_MAP_ARENA_POOL_H_ +#define ART_RUNTIME_BASE_MEM_MAP_ARENA_POOL_H_ + +#include "base/arena_allocator.h" + +namespace art { + +class MemMapArenaPool FINAL : public ArenaPool { + public: + explicit MemMapArenaPool(bool low_4gb = false, const char* name = "LinearAlloc"); + virtual ~MemMapArenaPool(); + Arena* AllocArena(size_t size) OVERRIDE; + void FreeArenaChain(Arena* first) OVERRIDE; + size_t GetBytesAllocated() const OVERRIDE; + void ReclaimMemory() OVERRIDE; + void LockReclaimMemory() OVERRIDE; + // Trim the maps in arenas by madvising, used by JIT to reduce memory usage. + void TrimMaps() OVERRIDE; + + private: + const bool low_4gb_; + const char* name_; + Arena* free_arenas_; + // Use a std::mutex here as Arenas are second-from-the-bottom when using MemMaps, and MemMap + // itself uses std::mutex scoped to within an allocate/free only. + mutable std::mutex lock_; + + DISALLOW_COPY_AND_ASSIGN(MemMapArenaPool); +}; + +} // namespace art + +#endif // ART_RUNTIME_BASE_MEM_MAP_ARENA_POOL_H_ diff --git a/runtime/base/mutator_locked_dumpable.h b/runtime/base/mutator_locked_dumpable.h new file mode 100644 index 0000000000000000000000000000000000000000..cf2199c1008dfbcf1b9ebf75668b5cfd887bb143 --- /dev/null +++ b/runtime/base/mutator_locked_dumpable.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_BASE_MUTATOR_LOCKED_DUMPABLE_H_ +#define ART_RUNTIME_BASE_MUTATOR_LOCKED_DUMPABLE_H_ + +#include "base/mutex.h" +#include "thread-current-inl.h" + +namespace art { + +template +class MutatorLockedDumpable { + public: + explicit MutatorLockedDumpable(T& value) REQUIRES_SHARED(Locks::mutator_lock_) : value_(value) {} + + void Dump(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_) { + value_.Dump(os); + } + + private: + const T& value_; + + DISALLOW_COPY_AND_ASSIGN(MutatorLockedDumpable); +}; + +// template +// std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable& rhs) +// // TODO: should be REQUIRES_SHARED(Locks::mutator_lock_) however annotalysis +// // currently fails for this. +// NO_THREAD_SAFETY_ANALYSIS; + +template +inline std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable& rhs) + NO_THREAD_SAFETY_ANALYSIS { + Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); + rhs.Dump(os); + return os; +} + +} // namespace art + +#endif // ART_RUNTIME_BASE_MUTATOR_LOCKED_DUMPABLE_H_ diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h index d6dbab4606c68fecf4805ff53c2a896fbd681616..51ca274cbb5cc7575a3fc366852f61b18db29925 100644 --- a/runtime/base/mutex-inl.h +++ b/runtime/base/mutex-inl.h @@ -161,7 +161,7 @@ inline void ReaderWriterMutex::SharedLock(Thread* self) { #if ART_USE_FUTEXES bool done = false; do { - int32_t cur_state = state_.LoadRelaxed(); + int32_t cur_state = state_.load(std::memory_order_relaxed); if (LIKELY(cur_state >= 0)) { // Add as an extra reader. done = state_.CompareAndSetWeakAcquire(cur_state, cur_state + 1); @@ -185,7 +185,7 @@ inline void ReaderWriterMutex::SharedUnlock(Thread* self) { #if ART_USE_FUTEXES bool done = false; do { - int32_t cur_state = state_.LoadRelaxed(); + int32_t cur_state = state_.load(std::memory_order_relaxed); if (LIKELY(cur_state > 0)) { // Reduce state by 1 and impose lock release load/store ordering. // Note, the relaxed loads below musn't reorder before the CompareAndSet. @@ -193,8 +193,8 @@ inline void ReaderWriterMutex::SharedUnlock(Thread* self) { // a status bit into the state on contention. done = state_.CompareAndSetWeakSequentiallyConsistent(cur_state, cur_state - 1); if (done && (cur_state - 1) == 0) { // Weak CAS may fail spuriously. - if (num_pending_writers_.LoadRelaxed() > 0 || - num_pending_readers_.LoadRelaxed() > 0) { + if (num_pending_writers_.load(std::memory_order_relaxed) > 0 || + num_pending_readers_.load(std::memory_order_relaxed) > 0) { // Wake any exclusive waiters as there are now no readers. futex(state_.Address(), FUTEX_WAKE, -1, nullptr, nullptr, 0); } @@ -221,7 +221,7 @@ inline bool Mutex::IsExclusiveHeld(const Thread* self) const { } inline pid_t Mutex::GetExclusiveOwnerTid() const { - return exclusive_owner_.LoadRelaxed(); + return exclusive_owner_.load(std::memory_order_relaxed); } inline void Mutex::AssertExclusiveHeld(const Thread* self) const { @@ -248,16 +248,16 @@ inline bool ReaderWriterMutex::IsExclusiveHeld(const Thread* self) const { inline pid_t ReaderWriterMutex::GetExclusiveOwnerTid() const { #if ART_USE_FUTEXES - int32_t state = state_.LoadRelaxed(); + int32_t state = state_.load(std::memory_order_relaxed); if (state == 0) { return 0; // No owner. } else if (state > 0) { return -1; // Shared. } else { - return exclusive_owner_.LoadRelaxed(); + return exclusive_owner_.load(std::memory_order_relaxed); } #else - return exclusive_owner_.LoadRelaxed(); + return exclusive_owner_.load(std::memory_order_relaxed); #endif } @@ -290,10 +290,6 @@ inline ReaderMutexLock::~ReaderMutexLock() { mu_.SharedUnlock(self_); } -// Catch bug where variable name is omitted. "ReaderMutexLock (lock);" instead of -// "ReaderMutexLock mu(lock)". -#define ReaderMutexLock(x) static_assert(0, "ReaderMutexLock declaration missing variable name") - } // namespace art #endif // ART_RUNTIME_BASE_MUTEX_INL_H_ diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index a1f30b679455a0a880fe1e36299c12703c745f23..dd58d75a32d3c46b57e43762c3ff4345c35b936a 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -42,6 +42,7 @@ Mutex* Locks::allocated_monitor_ids_lock_ = nullptr; Mutex* Locks::allocated_thread_ids_lock_ = nullptr; ReaderWriterMutex* Locks::breakpoint_lock_ = nullptr; ReaderWriterMutex* Locks::classlinker_classes_lock_ = nullptr; +Mutex* Locks::custom_tls_lock_ = nullptr; Mutex* Locks::deoptimization_lock_ = nullptr; ReaderWriterMutex* Locks::heap_bitmap_lock_ = nullptr; Mutex* Locks::instrument_entrypoints_lock_ = nullptr; @@ -128,15 +129,15 @@ class ScopedAllMutexesLock FINAL { public: explicit ScopedAllMutexesLock(const BaseMutex* mutex) : mutex_(mutex) { for (uint32_t i = 0; - !gAllMutexData->all_mutexes_guard.CompareAndSetWeakAcquire(0, mutex); + !gAllMutexData->all_mutexes_guard.CompareAndSetWeakAcquire(nullptr, mutex); ++i) { BackOff(i); } } ~ScopedAllMutexesLock() { - DCHECK_EQ(gAllMutexData->all_mutexes_guard.LoadRelaxed(), mutex_); - gAllMutexData->all_mutexes_guard.StoreRelease(0); + DCHECK_EQ(gAllMutexData->all_mutexes_guard.load(std::memory_order_relaxed), mutex_); + gAllMutexData->all_mutexes_guard.store(nullptr, std::memory_order_release); } private: @@ -147,15 +148,17 @@ class Locks::ScopedExpectedMutexesOnWeakRefAccessLock FINAL { public: explicit ScopedExpectedMutexesOnWeakRefAccessLock(const BaseMutex* mutex) : mutex_(mutex) { for (uint32_t i = 0; - !Locks::expected_mutexes_on_weak_ref_access_guard_.CompareAndSetWeakAcquire(0, mutex); + !Locks::expected_mutexes_on_weak_ref_access_guard_.CompareAndSetWeakAcquire(nullptr, + mutex); ++i) { BackOff(i); } } ~ScopedExpectedMutexesOnWeakRefAccessLock() { - DCHECK_EQ(Locks::expected_mutexes_on_weak_ref_access_guard_.LoadRelaxed(), mutex_); - Locks::expected_mutexes_on_weak_ref_access_guard_.StoreRelease(0); + DCHECK_EQ(Locks::expected_mutexes_on_weak_ref_access_guard_.load(std::memory_order_relaxed), + mutex_); + Locks::expected_mutexes_on_weak_ref_access_guard_.store(nullptr, std::memory_order_release); } private: @@ -193,8 +196,8 @@ class ScopedContentionRecorder FINAL : public ValueObject { }; BaseMutex::BaseMutex(const char* name, LockLevel level) - : level_(level), - name_(name), + : name_(name), + level_(level), should_respond_to_empty_checkpoint_request_(false) { if (kLogLockContentions) { ScopedAllMutexesLock mu(this); @@ -293,7 +296,7 @@ void BaseMutex::CheckSafeToWait(Thread* self) { void BaseMutex::ContentionLogData::AddToWaitTime(uint64_t value) { if (kLogLockContentions) { // Atomically add value to wait_time. - wait_time.FetchAndAddSequentiallyConsistent(value); + wait_time.fetch_add(value, std::memory_order_seq_cst); } } @@ -306,19 +309,19 @@ void BaseMutex::RecordContention(uint64_t blocked_tid, data->AddToWaitTime(nano_time_blocked); ContentionLogEntry* log = data->contention_log; // This code is intentionally racy as it is only used for diagnostics. - uint32_t slot = data->cur_content_log_entry.LoadRelaxed(); + int32_t slot = data->cur_content_log_entry.load(std::memory_order_relaxed); if (log[slot].blocked_tid == blocked_tid && log[slot].owner_tid == blocked_tid) { ++log[slot].count; } else { uint32_t new_slot; do { - slot = data->cur_content_log_entry.LoadRelaxed(); + slot = data->cur_content_log_entry.load(std::memory_order_relaxed); new_slot = (slot + 1) % kContentionLogSize; } while (!data->cur_content_log_entry.CompareAndSetWeakRelaxed(slot, new_slot)); log[new_slot].blocked_tid = blocked_tid; log[new_slot].owner_tid = owner_tid; - log[new_slot].count.StoreRelaxed(1); + log[new_slot].count.store(1, std::memory_order_relaxed); } } } @@ -327,8 +330,8 @@ void BaseMutex::DumpContention(std::ostream& os) const { if (kLogLockContentions) { const ContentionLogData* data = contention_log_data_; const ContentionLogEntry* log = data->contention_log; - uint64_t wait_time = data->wait_time.LoadRelaxed(); - uint32_t contention_count = data->contention_count.LoadRelaxed(); + uint64_t wait_time = data->wait_time.load(std::memory_order_relaxed); + uint32_t contention_count = data->contention_count.load(std::memory_order_relaxed); if (contention_count == 0) { os << "never contended"; } else { @@ -340,7 +343,7 @@ void BaseMutex::DumpContention(std::ostream& os) const { for (size_t i = 0; i < kContentionLogSize; ++i) { uint64_t blocked_tid = log[i].blocked_tid; uint64_t owner_tid = log[i].owner_tid; - uint32_t count = log[i].count.LoadRelaxed(); + uint32_t count = log[i].count.load(std::memory_order_relaxed); if (count > 0) { auto it = most_common_blocked.find(blocked_tid); if (it != most_common_blocked.end()) { @@ -384,10 +387,10 @@ void BaseMutex::DumpContention(std::ostream& os) const { Mutex::Mutex(const char* name, LockLevel level, bool recursive) - : BaseMutex(name, level), exclusive_owner_(0), recursive_(recursive), recursion_count_(0) { + : BaseMutex(name, level), exclusive_owner_(0), recursion_count_(0), recursive_(recursive) { #if ART_USE_FUTEXES - DCHECK_EQ(0, state_.LoadRelaxed()); - DCHECK_EQ(0, num_contenders_.LoadRelaxed()); + DCHECK_EQ(0, state_.load(std::memory_order_relaxed)); + DCHECK_EQ(0, num_contenders_.load(std::memory_order_relaxed)); #else CHECK_MUTEX_CALL(pthread_mutex_init, (&mutex_, nullptr)); #endif @@ -402,7 +405,7 @@ static bool IsSafeToCallAbortSafe() { Mutex::~Mutex() { bool safe_to_call_abort = Locks::IsSafeToCallAbortRacy(); #if ART_USE_FUTEXES - if (state_.LoadRelaxed() != 0) { + if (state_.load(std::memory_order_relaxed) != 0) { LOG(safe_to_call_abort ? FATAL : WARNING) << "destroying mutex with owner: " << GetExclusiveOwnerTid(); } else { @@ -410,7 +413,7 @@ Mutex::~Mutex() { LOG(safe_to_call_abort ? FATAL : WARNING) << "unexpectedly found an owner on unlocked mutex " << name_; } - if (num_contenders_.LoadSequentiallyConsistent() != 0) { + if (num_contenders_.load(std::memory_order_seq_cst) != 0) { LOG(safe_to_call_abort ? FATAL : WARNING) << "unexpectedly found a contender on mutex " << name_; } @@ -436,7 +439,7 @@ void Mutex::ExclusiveLock(Thread* self) { #if ART_USE_FUTEXES bool done = false; do { - int32_t cur_state = state_.LoadRelaxed(); + int32_t cur_state = state_.load(std::memory_order_relaxed); if (LIKELY(cur_state == 0)) { // Change state from 0 to 1 and impose load/store ordering appropriate for lock acquisition. done = state_.CompareAndSetWeakAcquire(0 /* cur_state */, 1 /* new state */); @@ -457,12 +460,12 @@ void Mutex::ExclusiveLock(Thread* self) { num_contenders_--; } } while (!done); - DCHECK_EQ(state_.LoadRelaxed(), 1); + DCHECK_EQ(state_.load(std::memory_order_relaxed), 1); #else CHECK_MUTEX_CALL(pthread_mutex_lock, (&mutex_)); #endif DCHECK_EQ(GetExclusiveOwnerTid(), 0); - exclusive_owner_.StoreRelaxed(SafeGetTid(self)); + exclusive_owner_.store(SafeGetTid(self), std::memory_order_relaxed); RegisterAsLocked(self); } recursion_count_++; @@ -482,7 +485,7 @@ bool Mutex::ExclusiveTryLock(Thread* self) { #if ART_USE_FUTEXES bool done = false; do { - int32_t cur_state = state_.LoadRelaxed(); + int32_t cur_state = state_.load(std::memory_order_relaxed); if (cur_state == 0) { // Change state from 0 to 1 and impose load/store ordering appropriate for lock acquisition. done = state_.CompareAndSetWeakAcquire(0 /* cur_state */, 1 /* new state */); @@ -490,7 +493,7 @@ bool Mutex::ExclusiveTryLock(Thread* self) { return false; } } while (!done); - DCHECK_EQ(state_.LoadRelaxed(), 1); + DCHECK_EQ(state_.load(std::memory_order_relaxed), 1); #else int result = pthread_mutex_trylock(&mutex_); if (result == EBUSY) { @@ -502,7 +505,7 @@ bool Mutex::ExclusiveTryLock(Thread* self) { } #endif DCHECK_EQ(GetExclusiveOwnerTid(), 0); - exclusive_owner_.StoreRelaxed(SafeGetTid(self)); + exclusive_owner_.store(SafeGetTid(self), std::memory_order_relaxed); RegisterAsLocked(self); } recursion_count_++; @@ -539,10 +542,10 @@ void Mutex::ExclusiveUnlock(Thread* self) { #if ART_USE_FUTEXES bool done = false; do { - int32_t cur_state = state_.LoadRelaxed(); + int32_t cur_state = state_.load(std::memory_order_relaxed); if (LIKELY(cur_state == 1)) { // We're no longer the owner. - exclusive_owner_.StoreRelaxed(0); + exclusive_owner_.store(0 /* pid */, std::memory_order_relaxed); // Change state to 0 and impose load/store ordering appropriate for lock release. // Note, the relaxed loads below mustn't reorder before the CompareAndSet. // TODO: the ordering here is non-trivial as state is split across 3 fields, fix by placing @@ -550,7 +553,7 @@ void Mutex::ExclusiveUnlock(Thread* self) { done = state_.CompareAndSetWeakSequentiallyConsistent(cur_state, 0 /* new state */); if (LIKELY(done)) { // Spurious fail? // Wake a contender. - if (UNLIKELY(num_contenders_.LoadRelaxed() > 0)) { + if (UNLIKELY(num_contenders_.load(std::memory_order_relaxed) > 0)) { futex(state_.Address(), FUTEX_WAKE, 1, nullptr, nullptr, 0); } } @@ -569,7 +572,7 @@ void Mutex::ExclusiveUnlock(Thread* self) { } } while (!done); #else - exclusive_owner_.StoreRelaxed(0); + exclusive_owner_.store(0 /* pid */, std::memory_order_relaxed); CHECK_MUTEX_CALL(pthread_mutex_unlock, (&mutex_)); #endif } @@ -593,7 +596,7 @@ void Mutex::WakeupToRespondToEmptyCheckpoint() { #if ART_USE_FUTEXES // Wake up all the waiters so they will respond to the emtpy checkpoint. DCHECK(should_respond_to_empty_checkpoint_request_); - if (UNLIKELY(num_contenders_.LoadRelaxed() > 0)) { + if (UNLIKELY(num_contenders_.load(std::memory_order_relaxed) > 0)) { futex(state_.Address(), FUTEX_WAKE, -1, nullptr, nullptr, 0); } #else @@ -610,15 +613,15 @@ ReaderWriterMutex::ReaderWriterMutex(const char* name, LockLevel level) #if !ART_USE_FUTEXES CHECK_MUTEX_CALL(pthread_rwlock_init, (&rwlock_, nullptr)); #endif - exclusive_owner_.StoreRelaxed(0); + exclusive_owner_.store(0 /* pid */, std::memory_order_relaxed); } ReaderWriterMutex::~ReaderWriterMutex() { #if ART_USE_FUTEXES - CHECK_EQ(state_.LoadRelaxed(), 0); + CHECK_EQ(state_.load(std::memory_order_relaxed), 0); CHECK_EQ(GetExclusiveOwnerTid(), 0); - CHECK_EQ(num_pending_readers_.LoadRelaxed(), 0); - CHECK_EQ(num_pending_writers_.LoadRelaxed(), 0); + CHECK_EQ(num_pending_readers_.load(std::memory_order_relaxed), 0); + CHECK_EQ(num_pending_writers_.load(std::memory_order_relaxed), 0); #else // We can't use CHECK_MUTEX_CALL here because on shutdown a suspended daemon thread // may still be using locks. @@ -637,7 +640,7 @@ void ReaderWriterMutex::ExclusiveLock(Thread* self) { #if ART_USE_FUTEXES bool done = false; do { - int32_t cur_state = state_.LoadRelaxed(); + int32_t cur_state = state_.load(std::memory_order_relaxed); if (LIKELY(cur_state == 0)) { // Change state from 0 to -1 and impose load/store ordering appropriate for lock acquisition. done = state_.CompareAndSetWeakAcquire(0 /* cur_state*/, -1 /* new state */); @@ -658,12 +661,12 @@ void ReaderWriterMutex::ExclusiveLock(Thread* self) { --num_pending_writers_; } } while (!done); - DCHECK_EQ(state_.LoadRelaxed(), -1); + DCHECK_EQ(state_.load(std::memory_order_relaxed), -1); #else CHECK_MUTEX_CALL(pthread_rwlock_wrlock, (&rwlock_)); #endif DCHECK_EQ(GetExclusiveOwnerTid(), 0); - exclusive_owner_.StoreRelaxed(SafeGetTid(self)); + exclusive_owner_.store(SafeGetTid(self), std::memory_order_relaxed); RegisterAsLocked(self); AssertExclusiveHeld(self); } @@ -676,10 +679,10 @@ void ReaderWriterMutex::ExclusiveUnlock(Thread* self) { #if ART_USE_FUTEXES bool done = false; do { - int32_t cur_state = state_.LoadRelaxed(); + int32_t cur_state = state_.load(std::memory_order_relaxed); if (LIKELY(cur_state == -1)) { // We're no longer the owner. - exclusive_owner_.StoreRelaxed(0); + exclusive_owner_.store(0 /* pid */, std::memory_order_relaxed); // Change state from -1 to 0 and impose load/store ordering appropriate for lock release. // Note, the relaxed loads below musn't reorder before the CompareAndSet. // TODO: the ordering here is non-trivial as state is split across 3 fields, fix by placing @@ -687,8 +690,8 @@ void ReaderWriterMutex::ExclusiveUnlock(Thread* self) { done = state_.CompareAndSetWeakSequentiallyConsistent(-1 /* cur_state*/, 0 /* new state */); if (LIKELY(done)) { // Weak CAS may fail spuriously. // Wake any waiters. - if (UNLIKELY(num_pending_readers_.LoadRelaxed() > 0 || - num_pending_writers_.LoadRelaxed() > 0)) { + if (UNLIKELY(num_pending_readers_.load(std::memory_order_relaxed) > 0 || + num_pending_writers_.load(std::memory_order_relaxed) > 0)) { futex(state_.Address(), FUTEX_WAKE, -1, nullptr, nullptr, 0); } } @@ -697,7 +700,7 @@ void ReaderWriterMutex::ExclusiveUnlock(Thread* self) { } } while (!done); #else - exclusive_owner_.StoreRelaxed(0); + exclusive_owner_.store(0 /* pid */, std::memory_order_relaxed); CHECK_MUTEX_CALL(pthread_rwlock_unlock, (&rwlock_)); #endif } @@ -710,7 +713,7 @@ bool ReaderWriterMutex::ExclusiveLockWithTimeout(Thread* self, int64_t ms, int32 timespec end_abs_ts; InitTimeSpec(true, CLOCK_MONOTONIC, ms, ns, &end_abs_ts); do { - int32_t cur_state = state_.LoadRelaxed(); + int32_t cur_state = state_.load(std::memory_order_relaxed); if (cur_state == 0) { // Change state from 0 to -1 and impose load/store ordering appropriate for lock acquisition. done = state_.CompareAndSetWeakAcquire(0 /* cur_state */, -1 /* new state */); @@ -753,7 +756,7 @@ bool ReaderWriterMutex::ExclusiveLockWithTimeout(Thread* self, int64_t ms, int32 PLOG(FATAL) << "pthread_rwlock_timedwrlock failed for " << name_; } #endif - exclusive_owner_.StoreRelaxed(SafeGetTid(self)); + exclusive_owner_.store(SafeGetTid(self), std::memory_order_relaxed); RegisterAsLocked(self); AssertSharedHeld(self); return true; @@ -782,7 +785,7 @@ bool ReaderWriterMutex::SharedTryLock(Thread* self) { #if ART_USE_FUTEXES bool done = false; do { - int32_t cur_state = state_.LoadRelaxed(); + int32_t cur_state = state_.load(std::memory_order_relaxed); if (cur_state >= 0) { // Add as an extra reader and impose load/store ordering appropriate for lock acquisition. done = state_.CompareAndSetWeakAcquire(cur_state, cur_state + 1); @@ -822,9 +825,9 @@ void ReaderWriterMutex::Dump(std::ostream& os) const { << " level=" << static_cast(level_) << " owner=" << GetExclusiveOwnerTid() #if ART_USE_FUTEXES - << " state=" << state_.LoadSequentiallyConsistent() - << " num_pending_writers=" << num_pending_writers_.LoadSequentiallyConsistent() - << " num_pending_readers=" << num_pending_readers_.LoadSequentiallyConsistent() + << " state=" << state_.load(std::memory_order_seq_cst) + << " num_pending_writers=" << num_pending_writers_.load(std::memory_order_seq_cst) + << " num_pending_readers=" << num_pending_readers_.load(std::memory_order_seq_cst) #endif << " "; DumpContention(os); @@ -844,8 +847,8 @@ void ReaderWriterMutex::WakeupToRespondToEmptyCheckpoint() { #if ART_USE_FUTEXES // Wake up all the waiters so they will respond to the emtpy checkpoint. DCHECK(should_respond_to_empty_checkpoint_request_); - if (UNLIKELY(num_pending_readers_.LoadRelaxed() > 0 || - num_pending_writers_.LoadRelaxed() > 0)) { + if (UNLIKELY(num_pending_readers_.load(std::memory_order_relaxed) > 0 || + num_pending_writers_.load(std::memory_order_relaxed) > 0)) { futex(state_.Address(), FUTEX_WAKE, -1, nullptr, nullptr, 0); } #else @@ -856,7 +859,7 @@ void ReaderWriterMutex::WakeupToRespondToEmptyCheckpoint() { ConditionVariable::ConditionVariable(const char* name, Mutex& guard) : name_(name), guard_(guard) { #if ART_USE_FUTEXES - DCHECK_EQ(0, sequence_.LoadRelaxed()); + DCHECK_EQ(0, sequence_.load(std::memory_order_relaxed)); num_waiters_ = 0; #else pthread_condattr_t cond_attrs; @@ -899,7 +902,7 @@ void ConditionVariable::Broadcast(Thread* self) { sequence_++; // Indicate the broadcast occurred. bool done = false; do { - int32_t cur_sequence = sequence_.LoadRelaxed(); + int32_t cur_sequence = sequence_.load(std::memory_order_relaxed); // Requeue waiters onto mutex. The waiter holds the contender count on the mutex high ensuring // mutex unlocks will awaken the requeued waiter thread. done = futex(sequence_.Address(), FUTEX_CMP_REQUEUE, 0, @@ -948,7 +951,7 @@ void ConditionVariable::WaitHoldingLocks(Thread* self) { // Ensure the Mutex is contended so that requeued threads are awoken. guard_.num_contenders_++; guard_.recursion_count_ = 1; - int32_t cur_sequence = sequence_.LoadRelaxed(); + int32_t cur_sequence = sequence_.load(std::memory_order_relaxed); guard_.ExclusiveUnlock(self); if (futex(sequence_.Address(), FUTEX_WAIT, cur_sequence, nullptr, nullptr, 0) != 0) { // Futex failed, check it is an expected error. @@ -974,14 +977,14 @@ void ConditionVariable::WaitHoldingLocks(Thread* self) { CHECK_GE(num_waiters_, 0); num_waiters_--; // We awoke and so no longer require awakes from the guard_'s unlock. - CHECK_GE(guard_.num_contenders_.LoadRelaxed(), 0); + CHECK_GE(guard_.num_contenders_.load(std::memory_order_relaxed), 0); guard_.num_contenders_--; #else pid_t old_owner = guard_.GetExclusiveOwnerTid(); - guard_.exclusive_owner_.StoreRelaxed(0); + guard_.exclusive_owner_.store(0 /* pid */, std::memory_order_relaxed); guard_.recursion_count_ = 0; CHECK_MUTEX_CALL(pthread_cond_wait, (&cond_, &guard_.mutex_)); - guard_.exclusive_owner_.StoreRelaxed(old_owner); + guard_.exclusive_owner_.store(old_owner, std::memory_order_relaxed); #endif guard_.recursion_count_ = old_recursion_count; } @@ -999,7 +1002,7 @@ bool ConditionVariable::TimedWait(Thread* self, int64_t ms, int32_t ns) { // Ensure the Mutex is contended so that requeued threads are awoken. guard_.num_contenders_++; guard_.recursion_count_ = 1; - int32_t cur_sequence = sequence_.LoadRelaxed(); + int32_t cur_sequence = sequence_.load(std::memory_order_relaxed); guard_.ExclusiveUnlock(self); if (futex(sequence_.Address(), FUTEX_WAIT, cur_sequence, &rel_ts, nullptr, 0) != 0) { if (errno == ETIMEDOUT) { @@ -1015,7 +1018,7 @@ bool ConditionVariable::TimedWait(Thread* self, int64_t ms, int32_t ns) { CHECK_GE(num_waiters_, 0); num_waiters_--; // We awoke and so no longer require awakes from the guard_'s unlock. - CHECK_GE(guard_.num_contenders_.LoadRelaxed(), 0); + CHECK_GE(guard_.num_contenders_.load(std::memory_order_relaxed), 0); guard_.num_contenders_--; #else #if !defined(__APPLE__) @@ -1024,7 +1027,7 @@ bool ConditionVariable::TimedWait(Thread* self, int64_t ms, int32_t ns) { int clock = CLOCK_REALTIME; #endif pid_t old_owner = guard_.GetExclusiveOwnerTid(); - guard_.exclusive_owner_.StoreRelaxed(0); + guard_.exclusive_owner_.store(0 /* pid */, std::memory_order_relaxed); guard_.recursion_count_ = 0; timespec ts; InitTimeSpec(true, clock, ms, ns, &ts); @@ -1035,7 +1038,7 @@ bool ConditionVariable::TimedWait(Thread* self, int64_t ms, int32_t ns) { errno = rc; PLOG(FATAL) << "TimedWait failed for " << name_; } - guard_.exclusive_owner_.StoreRelaxed(old_owner); + guard_.exclusive_owner_.store(old_owner, std::memory_order_relaxed); #endif guard_.recursion_count_ = old_recursion_count; return timed_out; @@ -1055,6 +1058,7 @@ void Locks::Init() { DCHECK(allocated_thread_ids_lock_ != nullptr); DCHECK(breakpoint_lock_ != nullptr); DCHECK(classlinker_classes_lock_ != nullptr); + DCHECK(custom_tls_lock_ != nullptr); DCHECK(deoptimization_lock_ != nullptr); DCHECK(heap_bitmap_lock_ != nullptr); DCHECK(oat_file_manager_lock_ != nullptr); @@ -1218,6 +1222,10 @@ void Locks::Init() { DCHECK(jni_function_table_lock_ == nullptr); jni_function_table_lock_ = new Mutex("JNI function table lock", current_lock_level); + UPDATE_CURRENT_LOCK_LEVEL(kCustomTlsLock); + DCHECK(custom_tls_lock_ == nullptr); + custom_tls_lock_ = new Mutex("Thread::custom_tls_ lock", current_lock_level); + UPDATE_CURRENT_LOCK_LEVEL(kNativeDebugInterfaceLock); DCHECK(native_debug_interface_lock_ == nullptr); native_debug_interface_lock_ = new Mutex("Native debug interface lock", current_lock_level); @@ -1254,12 +1262,13 @@ void Locks::InitConditions() { } void Locks::SetClientCallback(ClientCallback* safe_to_call_abort_cb) { - safe_to_call_abort_callback.StoreRelease(safe_to_call_abort_cb); + safe_to_call_abort_callback.store(safe_to_call_abort_cb, std::memory_order_release); } // Helper to allow checking shutdown while ignoring locking requirements. bool Locks::IsSafeToCallAbortRacy() { - Locks::ClientCallback* safe_to_call_abort_cb = safe_to_call_abort_callback.LoadAcquire(); + Locks::ClientCallback* safe_to_call_abort_cb = + safe_to_call_abort_callback.load(std::memory_order_acquire); return safe_to_call_abort_cb != nullptr && safe_to_call_abort_cb(); } diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 437661798f4530b5a22facec3aa29eca3500ed65..af2e7b2763f322dd5d70763b434008758d08724e 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -31,10 +31,10 @@ #include "base/globals.h" #include "base/macros.h" -#if defined(__APPLE__) -#define ART_USE_FUTEXES 0 -#else +#if defined(__linux__) #define ART_USE_FUTEXES 1 +#else +#define ART_USE_FUTEXES 0 #endif // Currently Darwin doesn't support locks with timeouts. @@ -57,7 +57,7 @@ class Mutex; // partial ordering and thereby cause deadlock situations to fail checks. // // [1] http://www.drdobbs.com/parallel/use-lock-hierarchies-to-avoid-deadlock/204801163 -enum LockLevel { +enum LockLevel : uint8_t { kLoggingLock = 0, kSwapMutexesLock, kUnexpectedSignalLock, @@ -65,15 +65,20 @@ enum LockLevel { kAbortLock, kNativeDebugInterfaceLock, kSignalHandlingLock, + // A generic lock level for mutexs that should not allow any additional mutexes to be gained after + // acquiring it. + kGenericBottomLock, kJdwpAdbStateLock, kJdwpSocketLock, kRegionSpaceRegionLock, kMarkSweepMarkStackLock, + kJitCodeCacheLock, kRosAllocGlobalLock, kRosAllocBracketLock, kRosAllocBulkFreeLock, kTaggingLockLevel, kTransactionLogLock, + kCustomTlsLock, kJniFunctionTableLock, kJniWeakGlobalsLock, kJniGlobalsLock, @@ -94,7 +99,6 @@ enum LockLevel { kOatFileManagerLock, kTracingUniqueMethodsLock, kTracingStreamingLock, - kDeoptimizedMethodsLock, kClassLoaderClassesLock, kDefaultMutexLevel, kDexLock, @@ -105,7 +109,6 @@ enum LockLevel { kMonitorPoolLock, kClassLinkerClassesLock, // TODO rename. kDexToDexCompilerLock, - kJitCodeCacheLock, kCHALock, kSubtypeCheckLock, kBreakpointLock, @@ -142,20 +145,20 @@ enum LockLevel { }; std::ostream& operator<<(std::ostream& os, const LockLevel& rhs); -const bool kDebugLocking = kIsDebugBuild; +constexpr bool kDebugLocking = kIsDebugBuild; // Record Log contention information, dumpable via SIGQUIT. #ifdef ART_USE_FUTEXES // To enable lock contention logging, set this to true. -const bool kLogLockContentions = false; +constexpr bool kLogLockContentions = false; #else // Keep this false as lock contention logging is supported only with // futex. -const bool kLogLockContentions = false; +constexpr bool kLogLockContentions = false; #endif -const size_t kContentionLogSize = 4; -const size_t kContentionLogDataSize = kLogLockContentions ? 1 : 0; -const size_t kAllMutexDataSize = kLogLockContentions ? 1 : 0; +constexpr size_t kContentionLogSize = 4; +constexpr size_t kContentionLogDataSize = kLogLockContentions ? 1 : 0; +constexpr size_t kAllMutexDataSize = kLogLockContentions ? 1 : 0; // Base class for all Mutex implementations class BaseMutex { @@ -196,9 +199,7 @@ class BaseMutex { void RecordContention(uint64_t blocked_tid, uint64_t owner_tid, uint64_t nano_time_blocked); void DumpContention(std::ostream& os) const; - const LockLevel level_; // Support for lock hierarchy. const char* const name_; - bool should_respond_to_empty_checkpoint_request_; // A log entry that records contention but makes no guarantee that either tid will be held live. struct ContentionLogEntry { @@ -221,10 +222,13 @@ class BaseMutex { }; ContentionLogData contention_log_data_[kContentionLogDataSize]; + const LockLevel level_; // Support for lock hierarchy. + bool should_respond_to_empty_checkpoint_request_; + public: bool HasEverContended() const { if (kLogLockContentions) { - return contention_log_data_->contention_count.LoadSequentiallyConsistent() > 0; + return contention_log_data_->contention_count.load(std::memory_order_seq_cst) > 0; } return false; } @@ -307,8 +311,10 @@ class LOCKABLE Mutex : public BaseMutex { pthread_mutex_t mutex_; Atomic exclusive_owner_; // Guarded by mutex_. Asynchronous reads are OK. #endif - const bool recursive_; // Can the lock be recursively held? + unsigned int recursion_count_; + const bool recursive_; // Can the lock be recursively held? + friend class ConditionVariable; DISALLOW_COPY_AND_ASSIGN(Mutex); }; @@ -522,8 +528,6 @@ class SCOPED_CAPABILITY MutexLock { Mutex& mu_; DISALLOW_COPY_AND_ASSIGN(MutexLock); }; -// Catch bug where variable name is omitted. "MutexLock (lock);" instead of "MutexLock mu(lock)". -#define MutexLock(x) static_assert(0, "MutexLock declaration missing variable name") // Scoped locker/unlocker for a ReaderWriterMutex that acquires read access to mu upon // construction and releases it upon destruction. @@ -557,9 +561,6 @@ class SCOPED_CAPABILITY WriterMutexLock { ReaderWriterMutex& mu_; DISALLOW_COPY_AND_ASSIGN(WriterMutexLock); }; -// Catch bug where variable name is omitted. "WriterMutexLock (lock);" instead of -// "WriterMutexLock mu(lock)". -#define WriterMutexLock(x) static_assert(0, "WriterMutexLock declaration missing variable name") // For StartNoThreadSuspension and EndNoThreadSuspension. class CAPABILITY("role") Role { @@ -738,8 +739,20 @@ class Locks { // Guard accesses to the JNI function table override. static Mutex* jni_function_table_lock_ ACQUIRED_AFTER(jni_weak_globals_lock_); + // Guard accesses to the Thread::custom_tls_. We use this to allow the TLS of other threads to be + // read (the reader must hold the ThreadListLock or have some other way of ensuring the thread + // will not die in that case though). This is useful for (eg) the implementation of + // GetThreadLocalStorage. + static Mutex* custom_tls_lock_ ACQUIRED_AFTER(jni_function_table_lock_); + + // When declaring any Mutex add BOTTOM_MUTEX_ACQUIRED_AFTER to use annotalysis to check the code + // doesn't try to acquire a higher level Mutex. NB Due to the way the annotalysis works this + // actually only encodes the mutex being below jni_function_table_lock_ although having + // kGenericBottomLock level is lower than this. + #define BOTTOM_MUTEX_ACQUIRED_AFTER ACQUIRED_AFTER(art::Locks::custom_tls_lock_) + // Have an exclusive aborting thread. - static Mutex* abort_lock_ ACQUIRED_AFTER(jni_function_table_lock_); + static Mutex* abort_lock_ ACQUIRED_AFTER(custom_tls_lock_); // Allow mutual exclusion when manipulating Thread::suspend_count_. // TODO: Does the trade-off of a per-thread lock make sense? diff --git a/runtime/base/quasi_atomic.h b/runtime/base/quasi_atomic.h index 067d01db01c2eff6361838c2cd4d95cc86180489..0012f6482b887e7f9f4d472e17ff5488b1c13ead 100644 --- a/runtime/base/quasi_atomic.h +++ b/runtime/base/quasi_atomic.h @@ -152,14 +152,6 @@ class QuasiAtomic { return NeedSwapMutexes(isa); } - static void ThreadFenceAcquire() { - std::atomic_thread_fence(std::memory_order_acquire); - } - - static void ThreadFenceRelease() { - std::atomic_thread_fence(std::memory_order_release); - } - static void ThreadFenceForConstructor() { #if defined(__aarch64__) __asm__ __volatile__("dmb ishst" : : : "memory"); @@ -168,10 +160,6 @@ class QuasiAtomic { #endif } - static void ThreadFenceSequentiallyConsistent() { - std::atomic_thread_fence(std::memory_order_seq_cst); - } - private: static Mutex* GetSwapMutex(const volatile int64_t* addr); static int64_t SwapMutexRead64(volatile const int64_t* addr); diff --git a/runtime/bit_memory_region.h b/runtime/bit_memory_region.h deleted file mode 100644 index 3a696f196941c013044e9aeaca8543133ba25ba2..0000000000000000000000000000000000000000 --- a/runtime/bit_memory_region.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_BIT_MEMORY_REGION_H_ -#define ART_RUNTIME_BIT_MEMORY_REGION_H_ - -#include "memory_region.h" - -namespace art { - -// Bit memory region is a bit offset subregion of a normal memoryregion. This is useful for -// abstracting away the bit start offset to avoid needing passing as an argument everywhere. -class BitMemoryRegion FINAL : public ValueObject { - public: - BitMemoryRegion() = default; - ALWAYS_INLINE BitMemoryRegion(MemoryRegion region, size_t bit_offset, size_t bit_size) { - bit_start_ = bit_offset % kBitsPerByte; - const size_t start = bit_offset / kBitsPerByte; - const size_t end = (bit_offset + bit_size + kBitsPerByte - 1) / kBitsPerByte; - region_ = region.Subregion(start, end - start); - } - - void* pointer() const { return region_.pointer(); } - size_t size() const { return region_.size(); } - size_t BitOffset() const { return bit_start_; } - size_t size_in_bits() const { - return region_.size_in_bits(); - } - - ALWAYS_INLINE BitMemoryRegion Subregion(size_t bit_offset, size_t bit_size) const { - return BitMemoryRegion(region_, bit_start_ + bit_offset, bit_size); - } - - // Load a single bit in the region. The bit at offset 0 is the least - // significant bit in the first byte. - ALWAYS_INLINE bool LoadBit(uintptr_t bit_offset) const { - return region_.LoadBit(bit_offset + bit_start_); - } - - ALWAYS_INLINE void StoreBit(uintptr_t bit_offset, bool value) const { - region_.StoreBit(bit_offset + bit_start_, value); - } - - ALWAYS_INLINE uint32_t LoadBits(uintptr_t bit_offset, size_t length) const { - return region_.LoadBits(bit_offset + bit_start_, length); - } - - // Store at a bit offset from inside the bit memory region. - ALWAYS_INLINE void StoreBits(uintptr_t bit_offset, uint32_t value, size_t length) { - region_.StoreBits(bit_offset + bit_start_, value, length); - } - - private: - MemoryRegion region_; - size_t bit_start_ = 0; -}; - -} // namespace art - -#endif // ART_RUNTIME_BIT_MEMORY_REGION_H_ diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h index e2ad7fd83f0c91422643ccaead5f4978f070fa8f..8f9f45c30b2b6c6773aeafdaac3e163b12929c37 100644 --- a/runtime/check_reference_map_visitor.h +++ b/runtime/check_reference_map_visitor.h @@ -64,20 +64,18 @@ class CheckReferenceMapVisitor : public StackVisitor { void CheckOptimizedMethod(int* registers, int number_of_references, uint32_t native_pc_offset) REQUIRES_SHARED(Locks::mutator_lock_) { ArtMethod* m = GetMethod(); - CodeInfo code_info = GetCurrentOatQuickMethodHeader()->GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); + CodeInfo code_info(GetCurrentOatQuickMethodHeader()); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); CodeItemDataAccessor accessor(m->DexInstructionData()); uint16_t number_of_dex_registers = accessor.RegistersSize(); - DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); - uint32_t register_mask = code_info.GetRegisterMaskOf(encoding, stack_map); - BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, stack_map); + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map); + DCHECK_EQ(dex_register_map.size(), number_of_dex_registers); + uint32_t register_mask = code_info.GetRegisterMaskOf(stack_map); + BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map); for (int i = 0; i < number_of_references; ++i) { int reg = registers[i]; CHECK_LT(reg, accessor.RegistersSize()); - DexRegisterLocation location = dex_register_map.GetDexRegisterLocation( - reg, number_of_dex_registers, code_info, encoding); + DexRegisterLocation location = dex_register_map[reg]; switch (location.GetKind()) { case DexRegisterLocation::Kind::kNone: // Not set, should not be a reference. @@ -100,7 +98,7 @@ class CheckReferenceMapVisitor : public StackVisitor { CHECK_EQ(location.GetValue(), 0); break; default: - LOG(FATAL) << "Unexpected location kind " << location.GetInternalKind(); + LOG(FATAL) << "Unexpected location kind " << location.GetKind(); } } } diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index ae06f8f9bc4b8363b813e17e48d32de86d114300..2536b2341694dcd46acb22c7325c96bee1a9cc9c 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -17,7 +17,10 @@ #ifndef ART_RUNTIME_CLASS_LINKER_INL_H_ #define ART_RUNTIME_CLASS_LINKER_INL_H_ -#include "art_field.h" +#include + +#include "art_field-inl.h" +#include "art_method-inl.h" #include "class_linker.h" #include "gc/heap-inl.h" #include "gc_root-inl.h" @@ -29,25 +32,22 @@ #include "obj_ptr-inl.h" #include "scoped_thread_state_change-inl.h" -#include - namespace art { -inline mirror::Class* ClassLinker::FindArrayClass(Thread* self, - ObjPtr* element_class) { +inline ObjPtr ClassLinker::FindArrayClass(Thread* self, + ObjPtr element_class) { for (size_t i = 0; i < kFindArrayCacheSize; ++i) { // Read the cached array class once to avoid races with other threads setting it. ObjPtr array_class = find_array_class_cache_[i].Read(); - if (array_class != nullptr && array_class->GetComponentType() == *element_class) { - return array_class.Ptr(); + if (array_class != nullptr && array_class->GetComponentType() == element_class) { + return array_class; } } std::string descriptor = "["; std::string temp; - descriptor += (*element_class)->GetDescriptor(&temp); - StackHandleScope<2> hs(Thread::Current()); - Handle class_loader(hs.NewHandle((*element_class)->GetClassLoader())); - HandleWrapperObjPtr h_element_class(hs.NewHandleWrapper(element_class)); + descriptor += element_class->GetDescriptor(&temp); + StackHandleScope<1> hs(Thread::Current()); + Handle class_loader(hs.NewHandle(element_class->GetClassLoader())); ObjPtr array_class = FindClass(self, descriptor.c_str(), class_loader); if (array_class != nullptr) { // Benign races in storing array class and incrementing index. @@ -58,7 +58,55 @@ inline mirror::Class* ClassLinker::FindArrayClass(Thread* self, // We should have a NoClassDefFoundError. self->AssertPendingException(); } - return array_class.Ptr(); + return array_class; +} + +inline ObjPtr ClassLinker::ResolveString(dex::StringIndex string_idx, + ArtField* referrer) { + Thread::PoisonObjectPointersIfDebug(); + DCHECK(!Thread::Current()->IsExceptionPending()); + // We do not need the read barrier for getting the DexCache for the initial resolved type + // lookup as both from-space and to-space copies point to the same native resolved types array. + ObjPtr resolved = + referrer->GetDexCache()->GetResolvedString(string_idx); + if (resolved == nullptr) { + resolved = DoResolveString(string_idx, referrer->GetDexCache()); + } + return resolved; +} + +inline ObjPtr ClassLinker::ResolveString(dex::StringIndex string_idx, + ArtMethod* referrer) { + Thread::PoisonObjectPointersIfDebug(); + DCHECK(!Thread::Current()->IsExceptionPending()); + // We do not need the read barrier for getting the DexCache for the initial resolved type + // lookup as both from-space and to-space copies point to the same native resolved types array. + ObjPtr resolved = + referrer->GetDexCache()->GetResolvedString(string_idx); + if (resolved == nullptr) { + resolved = DoResolveString(string_idx, referrer->GetDexCache()); + } + return resolved; +} + +inline ObjPtr ClassLinker::ResolveString(dex::StringIndex string_idx, + Handle dex_cache) { + Thread::PoisonObjectPointersIfDebug(); + DCHECK(!Thread::Current()->IsExceptionPending()); + ObjPtr resolved = dex_cache->GetResolvedString(string_idx); + if (resolved == nullptr) { + resolved = DoResolveString(string_idx, dex_cache); + } + return resolved; +} + +inline ObjPtr ClassLinker::LookupString(dex::StringIndex string_idx, + ObjPtr dex_cache) { + ObjPtr resolved = dex_cache->GetResolvedString(string_idx); + if (resolved == nullptr) { + resolved = DoLookupString(string_idx, dex_cache); + } + return resolved; } inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx, @@ -68,38 +116,41 @@ inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx, HandleWrapperObjPtr referrer_wrapper = hs.NewHandleWrapper(&referrer); Thread::Current()->PoisonObjectPointers(); } - if (kIsDebugBuild) { - Thread::Current()->AssertNoPendingException(); - } + DCHECK(!Thread::Current()->IsExceptionPending()); // We do not need the read barrier for getting the DexCache for the initial resolved type // lookup as both from-space and to-space copies point to the same native resolved types array. ObjPtr resolved_type = referrer->GetDexCache()->GetResolvedType(type_idx); if (resolved_type == nullptr) { - StackHandleScope<2> hs(Thread::Current()); - Handle h_dex_cache(hs.NewHandle(referrer->GetDexCache())); - Handle class_loader(hs.NewHandle(referrer->GetClassLoader())); - resolved_type = DoResolveType(type_idx, h_dex_cache, class_loader); + resolved_type = DoResolveType(type_idx, referrer); } return resolved_type; } inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx, - ArtMethod* referrer) { + ArtField* referrer) { Thread::PoisonObjectPointersIfDebug(); - if (kIsDebugBuild) { - Thread::Current()->AssertNoPendingException(); + DCHECK(!Thread::Current()->IsExceptionPending()); + // We do not need the read barrier for getting the DexCache for the initial resolved type + // lookup as both from-space and to-space copies point to the same native resolved types array. + ObjPtr resolved_type = + referrer->GetDexCache()->GetResolvedType(type_idx); + if (UNLIKELY(resolved_type == nullptr)) { + resolved_type = DoResolveType(type_idx, referrer->GetDeclaringClass()); } + return resolved_type; +} + +inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx, + ArtMethod* referrer) { + Thread::PoisonObjectPointersIfDebug(); + DCHECK(!Thread::Current()->IsExceptionPending()); // We do not need the read barrier for getting the DexCache for the initial resolved type // lookup as both from-space and to-space copies point to the same native resolved types array. ObjPtr resolved_type = referrer->GetDexCache()->GetResolvedType(type_idx); if (UNLIKELY(resolved_type == nullptr)) { - StackHandleScope<2> hs(Thread::Current()); - ObjPtr referring_class = referrer->GetDeclaringClass(); - Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); - Handle class_loader(hs.NewHandle(referring_class->GetClassLoader())); - resolved_type = DoResolveType(type_idx, dex_cache, class_loader); + resolved_type = DoResolveType(type_idx, referrer->GetDeclaringClass()); } return resolved_type; } @@ -123,7 +174,19 @@ inline ObjPtr ClassLinker::LookupResolvedType(dex::TypeIndex type ObjPtr type = referrer->GetDexCache()->GetResolvedType(type_idx); if (type == nullptr) { - type = DoLookupResolvedType(type_idx, referrer->GetDexCache(), referrer->GetClassLoader()); + type = DoLookupResolvedType(type_idx, referrer); + } + return type; +} + +inline ObjPtr ClassLinker::LookupResolvedType(dex::TypeIndex type_idx, + ArtField* referrer) { + // We do not need the read barrier for getting the DexCache for the initial resolved type + // lookup as both from-space and to-space copies point to the same native resolved types array. + ObjPtr type = + referrer->GetDexCache()->GetResolvedType(type_idx); + if (type == nullptr) { + type = DoLookupResolvedType(type_idx, referrer->GetDeclaringClass()); } return type; } @@ -135,7 +198,7 @@ inline ObjPtr ClassLinker::LookupResolvedType(dex::TypeIndex type ObjPtr type = referrer->GetDexCache()->GetResolvedType(type_idx); if (type == nullptr) { - type = DoLookupResolvedType(type_idx, referrer->GetDexCache(), referrer->GetClassLoader()); + type = DoLookupResolvedType(type_idx, referrer->GetDeclaringClass()); } return type; } @@ -365,14 +428,6 @@ inline ArtField* ClassLinker::ResolveField(uint32_t field_idx, return resolved_field; } -inline mirror::Class* ClassLinker::GetClassRoot(ClassRoot class_root) { - DCHECK(!class_roots_.IsNull()); - mirror::ObjectArray* class_roots = class_roots_.Read(); - ObjPtr klass = class_roots->Get(class_root); - DCHECK(klass != nullptr); - return klass.Ptr(); -} - template inline void ClassLinker::VisitClassTables(const Visitor& visitor) { Thread* const self = Thread::Current(); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 6daad88a4887306a33839492abe2cc913a7b76ef..966d636f62354555b29fc44175645a3a07c320c7 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -51,10 +51,12 @@ #include "cha.h" #include "class_linker-inl.h" #include "class_loader_utils.h" +#include "class_root.h" #include "class_table-inl.h" #include "compiler_callbacks.h" #include "debug_print.h" #include "debugger.h" +#include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_exception_helpers.h" @@ -79,12 +81,11 @@ #include "imtable-inl.h" #include "intern_table.h" #include "interpreter/interpreter.h" -#include "java_vm_ext.h" #include "jit/debugger_interface.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" -#include "jit/profile_compilation_info.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "linear_alloc.h" #include "mirror/call_site.h" #include "mirror/class-inl.h" @@ -116,6 +117,7 @@ #include "oat_file_assistant.h" #include "oat_file_manager.h" #include "object_lock.h" +#include "profile/profile_compilation_info.h" #include "runtime.h" #include "runtime_callbacks.h" #include "scoped_thread_state_change-inl.h" @@ -196,8 +198,7 @@ static void HandleEarlierVerifyError(Thread* self, } } else { // Previous error has been stored as an instance. Just rethrow. - ObjPtr throwable_class = - self->DecodeJObject(WellKnownClasses::java_lang_Throwable)->AsClass(); + ObjPtr throwable_class = GetClassRoot(class_linker); ObjPtr error_class = obj->GetClass(); CHECK(throwable_class->IsAssignableFrom(error_class)); self->SetException(obj->AsThrowable()); @@ -270,9 +271,13 @@ static void WrapExceptionInInitializer(Handle klass) // cannot in general be guaranteed, but in all likelihood leads to breakage down the line. if (klass->GetClassLoader() == nullptr && !Runtime::Current()->IsAotCompiler()) { std::string tmp; - LOG(kIsDebugBuild ? FATAL : WARNING) << klass->GetDescriptor(&tmp) - << " failed initialization: " - << self->GetException()->Dump(); + // We want to LOG(FATAL) on debug builds since this really shouldn't be happening but we need to + // make sure to only do it if we don't have AsyncExceptions being thrown around since those + // could have caused the error. + bool known_impossible = kIsDebugBuild && !Runtime::Current()->AreAsyncExceptionsThrown(); + LOG(known_impossible ? FATAL : WARNING) << klass->GetDescriptor(&tmp) + << " failed initialization: " + << self->GetException()->Dump(); } env->ExceptionClear(); @@ -371,7 +376,6 @@ ClassLinker::ClassLinker(InternTable* intern_table) : boot_class_table_(new ClassTable()), failed_dex_cache_class_lookups_(0), class_roots_(nullptr), - array_iftable_(nullptr), find_array_class_cache_next_victim_(0), init_done_(false), log_new_roots_(false), @@ -427,10 +431,10 @@ bool ClassLinker::InitWithoutImage(std::vector> b heap->IncrementDisableMovingGC(self); StackHandleScope<64> hs(self); // 64 is picked arbitrarily. auto class_class_size = mirror::Class::ClassClassSize(image_pointer_size_); - Handle java_lang_Class(hs.NewHandle(down_cast( - heap->AllocNonMovableObject(self, nullptr, class_class_size, VoidFunctor())))); + Handle java_lang_Class(hs.NewHandle(ObjPtr::DownCast(MakeObjPtr( + heap->AllocNonMovableObject(self, nullptr, class_class_size, VoidFunctor()))))); CHECK(java_lang_Class != nullptr); - mirror::Class::SetClassClass(java_lang_Class.Get()); + java_lang_Class->SetClassFlags(mirror::kClassFlagClass); java_lang_Class->SetClass(java_lang_Class.Get()); if (kUseBakerReadBarrier) { java_lang_Class->AssertReadBarrierState(); @@ -479,86 +483,67 @@ bool ClassLinker::InitWithoutImage(std::vector> b mirror::ObjectArray::ClassSize(image_pointer_size_)))); object_array_class->SetComponentType(java_lang_Object.Get()); - // Setup the char (primitive) class to be used for char[]. - Handle char_class(hs.NewHandle( - AllocClass(self, java_lang_Class.Get(), - mirror::Class::PrimitiveClassSize(image_pointer_size_)))); - // The primitive char class won't be initialized by - // InitializePrimitiveClass until line 459, but strings (and - // internal char arrays) will be allocated before that and the - // component size, which is computed from the primitive type, needs - // to be set here. - char_class->SetPrimitiveType(Primitive::kPrimChar); - - // Setup the char[] class to be used for String. - Handle char_array_class(hs.NewHandle( - AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_)))); - char_array_class->SetComponentType(char_class.Get()); - mirror::CharArray::SetArrayClass(char_array_class.Get()); - // Setup String. Handle java_lang_String(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::String::ClassSize(image_pointer_size_)))); java_lang_String->SetStringClass(); - mirror::String::SetClass(java_lang_String.Get()); mirror::Class::SetStatus(java_lang_String, ClassStatus::kResolved, self); // Setup java.lang.ref.Reference. Handle java_lang_ref_Reference(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::Reference::ClassSize(image_pointer_size_)))); - mirror::Reference::SetClass(java_lang_ref_Reference.Get()); java_lang_ref_Reference->SetObjectSize(mirror::Reference::InstanceSize()); mirror::Class::SetStatus(java_lang_ref_Reference, ClassStatus::kResolved, self); // Create storage for root classes, save away our work so far (requires descriptors). class_roots_ = GcRoot>( - mirror::ObjectArray::Alloc(self, object_array_class.Get(), - kClassRootsMax)); + mirror::ObjectArray::Alloc(self, + object_array_class.Get(), + static_cast(ClassRoot::kMax))); CHECK(!class_roots_.IsNull()); - SetClassRoot(kJavaLangClass, java_lang_Class.Get()); - SetClassRoot(kJavaLangObject, java_lang_Object.Get()); - SetClassRoot(kClassArrayClass, class_array_class.Get()); - SetClassRoot(kObjectArrayClass, object_array_class.Get()); - SetClassRoot(kCharArrayClass, char_array_class.Get()); - SetClassRoot(kJavaLangString, java_lang_String.Get()); - SetClassRoot(kJavaLangRefReference, java_lang_ref_Reference.Get()); + SetClassRoot(ClassRoot::kJavaLangClass, java_lang_Class.Get()); + SetClassRoot(ClassRoot::kJavaLangObject, java_lang_Object.Get()); + SetClassRoot(ClassRoot::kClassArrayClass, class_array_class.Get()); + SetClassRoot(ClassRoot::kObjectArrayClass, object_array_class.Get()); + SetClassRoot(ClassRoot::kJavaLangString, java_lang_String.Get()); + SetClassRoot(ClassRoot::kJavaLangRefReference, java_lang_ref_Reference.Get()); // Fill in the empty iftable. Needs to be done after the kObjectArrayClass root is set. java_lang_Object->SetIfTable(AllocIfTable(self, 0)); - // Setup the primitive type classes. - SetClassRoot(kPrimitiveBoolean, CreatePrimitiveClass(self, Primitive::kPrimBoolean)); - SetClassRoot(kPrimitiveByte, CreatePrimitiveClass(self, Primitive::kPrimByte)); - SetClassRoot(kPrimitiveShort, CreatePrimitiveClass(self, Primitive::kPrimShort)); - SetClassRoot(kPrimitiveInt, CreatePrimitiveClass(self, Primitive::kPrimInt)); - SetClassRoot(kPrimitiveLong, CreatePrimitiveClass(self, Primitive::kPrimLong)); - SetClassRoot(kPrimitiveFloat, CreatePrimitiveClass(self, Primitive::kPrimFloat)); - SetClassRoot(kPrimitiveDouble, CreatePrimitiveClass(self, Primitive::kPrimDouble)); - SetClassRoot(kPrimitiveVoid, CreatePrimitiveClass(self, Primitive::kPrimVoid)); - // Create array interface entries to populate once we can load system classes. - array_iftable_ = GcRoot(AllocIfTable(self, 2)); + object_array_class->SetIfTable(AllocIfTable(self, 2)); + DCHECK_EQ(GetArrayIfTable(), object_array_class->GetIfTable()); - // Create int array type for AllocDexCache (done in AppendToBootClassPath). + // Setup the primitive type classes. + SetClassRoot(ClassRoot::kPrimitiveBoolean, CreatePrimitiveClass(self, Primitive::kPrimBoolean)); + SetClassRoot(ClassRoot::kPrimitiveByte, CreatePrimitiveClass(self, Primitive::kPrimByte)); + SetClassRoot(ClassRoot::kPrimitiveChar, CreatePrimitiveClass(self, Primitive::kPrimChar)); + SetClassRoot(ClassRoot::kPrimitiveShort, CreatePrimitiveClass(self, Primitive::kPrimShort)); + SetClassRoot(ClassRoot::kPrimitiveInt, CreatePrimitiveClass(self, Primitive::kPrimInt)); + SetClassRoot(ClassRoot::kPrimitiveLong, CreatePrimitiveClass(self, Primitive::kPrimLong)); + SetClassRoot(ClassRoot::kPrimitiveFloat, CreatePrimitiveClass(self, Primitive::kPrimFloat)); + SetClassRoot(ClassRoot::kPrimitiveDouble, CreatePrimitiveClass(self, Primitive::kPrimDouble)); + SetClassRoot(ClassRoot::kPrimitiveVoid, CreatePrimitiveClass(self, Primitive::kPrimVoid)); + + // Create int array type for native pointer arrays (for example vtables) on 32-bit archs. Handle int_array_class(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_)))); - int_array_class->SetComponentType(GetClassRoot(kPrimitiveInt)); - mirror::IntArray::SetArrayClass(int_array_class.Get()); - SetClassRoot(kIntArrayClass, int_array_class.Get()); + int_array_class->SetComponentType(GetClassRoot(ClassRoot::kPrimitiveInt, this)); + SetClassRoot(ClassRoot::kIntArrayClass, int_array_class.Get()); - // Create long array type for AllocDexCache (done in AppendToBootClassPath). + // Create long array type for native pointer arrays (for example vtables) on 64-bit archs. Handle long_array_class(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_)))); - long_array_class->SetComponentType(GetClassRoot(kPrimitiveLong)); - mirror::LongArray::SetArrayClass(long_array_class.Get()); - SetClassRoot(kLongArrayClass, long_array_class.Get()); + long_array_class->SetComponentType(GetClassRoot(ClassRoot::kPrimitiveLong, this)); + SetClassRoot(ClassRoot::kLongArrayClass, long_array_class.Get()); // now that these are registered, we can use AllocClass() and AllocObjectArray // Set up DexCache. This cannot be done later since AppendToBootClassPath calls AllocDexCache. Handle java_lang_DexCache(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::DexCache::ClassSize(image_pointer_size_)))); - SetClassRoot(kJavaLangDexCache, java_lang_DexCache.Get()); + SetClassRoot(ClassRoot::kJavaLangDexCache, java_lang_DexCache.Get()); java_lang_DexCache->SetDexCacheClass(); java_lang_DexCache->SetObjectSize(mirror::DexCache::InstanceSize()); mirror::Class::SetStatus(java_lang_DexCache, ClassStatus::kResolved, self); @@ -567,8 +552,7 @@ bool ClassLinker::InitWithoutImage(std::vector> b // Setup dalvik.system.ClassExt Handle dalvik_system_ClassExt(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::ClassExt::ClassSize(image_pointer_size_)))); - SetClassRoot(kDalvikSystemClassExt, dalvik_system_ClassExt.Get()); - mirror::ClassExt::SetClass(dalvik_system_ClassExt.Get()); + SetClassRoot(ClassRoot::kDalvikSystemClassExt, dalvik_system_ClassExt.Get()); mirror::Class::SetStatus(dalvik_system_ClassExt, ClassStatus::kResolved, self); // Set up array classes for string, field, method @@ -576,7 +560,7 @@ bool ClassLinker::InitWithoutImage(std::vector> b AllocClass(self, java_lang_Class.Get(), mirror::ObjectArray::ClassSize(image_pointer_size_)))); object_array_string->SetComponentType(java_lang_String.Get()); - SetClassRoot(kJavaLangStringArrayClass, object_array_string.Get()); + SetClassRoot(ClassRoot::kJavaLangStringArrayClass, object_array_string.Get()); LinearAlloc* linear_alloc = runtime->GetLinearAlloc(); // Create runtime resolution and imt conflict methods. @@ -602,10 +586,6 @@ bool ClassLinker::InitWithoutImage(std::vector> b // now we can use FindSystemClass - // run char class through InitializePrimitiveClass to finish init - InitializePrimitiveClass(char_class.Get(), Primitive::kPrimChar); - SetClassRoot(kPrimitiveChar, char_class.Get()); // needs descriptor - // Set up GenericJNI entrypoint. That is mainly a hack for common_compiler_test.h so that // we do not need friend classes or a publicly exposed setter. quick_generic_jni_trampoline_ = GetQuickGenericJniStub(); @@ -630,25 +610,20 @@ bool ClassLinker::InitWithoutImage(std::vector> b CHECK_EQ(dalvik_system_ClassExt->GetObjectSize(), mirror::ClassExt::InstanceSize()); // Setup the primitive array type classes - can't be done until Object has a vtable. - SetClassRoot(kBooleanArrayClass, FindSystemClass(self, "[Z")); - mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass)); + SetClassRoot(ClassRoot::kBooleanArrayClass, FindSystemClass(self, "[Z")); - SetClassRoot(kByteArrayClass, FindSystemClass(self, "[B")); - mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass)); + SetClassRoot(ClassRoot::kByteArrayClass, FindSystemClass(self, "[B")); - CheckSystemClass(self, char_array_class, "[C"); + SetClassRoot(ClassRoot::kCharArrayClass, FindSystemClass(self, "[C")); - SetClassRoot(kShortArrayClass, FindSystemClass(self, "[S")); - mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass)); + SetClassRoot(ClassRoot::kShortArrayClass, FindSystemClass(self, "[S")); CheckSystemClass(self, int_array_class, "[I"); CheckSystemClass(self, long_array_class, "[J"); - SetClassRoot(kFloatArrayClass, FindSystemClass(self, "[F")); - mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass)); + SetClassRoot(ClassRoot::kFloatArrayClass, FindSystemClass(self, "[F")); - SetClassRoot(kDoubleArrayClass, FindSystemClass(self, "[D")); - mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass)); + SetClassRoot(ClassRoot::kDoubleArrayClass, FindSystemClass(self, "[D")); // Run Class through FindSystemClass. This initializes the dex_cache_ fields and register it // in class_table_. @@ -664,8 +639,8 @@ bool ClassLinker::InitWithoutImage(std::vector> b CHECK(java_io_Serializable != nullptr); // We assume that Cloneable/Serializable don't have superinterfaces -- normally we'd have to // crawl up and explicitly list all of the supers as well. - array_iftable_.Read()->SetInterface(0, java_lang_Cloneable.Get()); - array_iftable_.Read()->SetInterface(1, java_io_Serializable.Get()); + object_array_class->GetIfTable()->SetInterface(0, java_lang_Cloneable.Get()); + object_array_class->GetIfTable()->SetInterface(1, java_io_Serializable.Get()); // Sanity check Class[] and Object[]'s interfaces. GetDirectInterface may cause thread // suspension. @@ -679,103 +654,89 @@ bool ClassLinker::InitWithoutImage(std::vector> b mirror::Class::GetDirectInterface(self, object_array_class.Get(), 1)); CHECK_EQ(object_array_string.Get(), - FindSystemClass(self, GetClassRootDescriptor(kJavaLangStringArrayClass))); + FindSystemClass(self, GetClassRootDescriptor(ClassRoot::kJavaLangStringArrayClass))); // End of special init trickery, all subsequent classes may be loaded via FindSystemClass. // Create java.lang.reflect.Proxy root. - SetClassRoot(kJavaLangReflectProxy, FindSystemClass(self, "Ljava/lang/reflect/Proxy;")); + SetClassRoot(ClassRoot::kJavaLangReflectProxy, + FindSystemClass(self, "Ljava/lang/reflect/Proxy;")); // Create java.lang.reflect.Field.class root. - auto* class_root = FindSystemClass(self, "Ljava/lang/reflect/Field;"); + ObjPtr class_root = FindSystemClass(self, "Ljava/lang/reflect/Field;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangReflectField, class_root); - mirror::Field::SetClass(class_root); + SetClassRoot(ClassRoot::kJavaLangReflectField, class_root); // Create java.lang.reflect.Field array root. class_root = FindSystemClass(self, "[Ljava/lang/reflect/Field;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangReflectFieldArrayClass, class_root); - mirror::Field::SetArrayClass(class_root); + SetClassRoot(ClassRoot::kJavaLangReflectFieldArrayClass, class_root); // Create java.lang.reflect.Constructor.class root and array root. class_root = FindSystemClass(self, "Ljava/lang/reflect/Constructor;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangReflectConstructor, class_root); - mirror::Constructor::SetClass(class_root); + SetClassRoot(ClassRoot::kJavaLangReflectConstructor, class_root); class_root = FindSystemClass(self, "[Ljava/lang/reflect/Constructor;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangReflectConstructorArrayClass, class_root); - mirror::Constructor::SetArrayClass(class_root); + SetClassRoot(ClassRoot::kJavaLangReflectConstructorArrayClass, class_root); // Create java.lang.reflect.Method.class root and array root. class_root = FindSystemClass(self, "Ljava/lang/reflect/Method;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangReflectMethod, class_root); - mirror::Method::SetClass(class_root); + SetClassRoot(ClassRoot::kJavaLangReflectMethod, class_root); class_root = FindSystemClass(self, "[Ljava/lang/reflect/Method;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangReflectMethodArrayClass, class_root); - mirror::Method::SetArrayClass(class_root); + SetClassRoot(ClassRoot::kJavaLangReflectMethodArrayClass, class_root); // Create java.lang.invoke.CallSite.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/CallSite;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangInvokeCallSite, class_root); - mirror::CallSite::SetClass(class_root); + SetClassRoot(ClassRoot::kJavaLangInvokeCallSite, class_root); // Create java.lang.invoke.MethodType.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/MethodType;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangInvokeMethodType, class_root); - mirror::MethodType::SetClass(class_root); + SetClassRoot(ClassRoot::kJavaLangInvokeMethodType, class_root); // Create java.lang.invoke.MethodHandleImpl.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/MethodHandleImpl;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangInvokeMethodHandleImpl, class_root); - mirror::MethodHandleImpl::SetClass(class_root); + SetClassRoot(ClassRoot::kJavaLangInvokeMethodHandleImpl, class_root); + SetClassRoot(ClassRoot::kJavaLangInvokeMethodHandle, class_root->GetSuperClass()); // Create java.lang.invoke.MethodHandles.Lookup.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/MethodHandles$Lookup;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangInvokeMethodHandlesLookup, class_root); - mirror::MethodHandlesLookup::SetClass(class_root); + SetClassRoot(ClassRoot::kJavaLangInvokeMethodHandlesLookup, class_root); // Create java.lang.invoke.VarHandle.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/VarHandle;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangInvokeVarHandle, class_root); - mirror::VarHandle::SetClass(class_root); + SetClassRoot(ClassRoot::kJavaLangInvokeVarHandle, class_root); // Create java.lang.invoke.FieldVarHandle.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/FieldVarHandle;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangInvokeFieldVarHandle, class_root); - mirror::FieldVarHandle::SetClass(class_root); + SetClassRoot(ClassRoot::kJavaLangInvokeFieldVarHandle, class_root); // Create java.lang.invoke.ArrayElementVarHandle.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/ArrayElementVarHandle;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangInvokeArrayElementVarHandle, class_root); - mirror::ArrayElementVarHandle::SetClass(class_root); + SetClassRoot(ClassRoot::kJavaLangInvokeArrayElementVarHandle, class_root); // Create java.lang.invoke.ByteArrayViewVarHandle.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/ByteArrayViewVarHandle;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangInvokeByteArrayViewVarHandle, class_root); - mirror::ByteArrayViewVarHandle::SetClass(class_root); + SetClassRoot(ClassRoot::kJavaLangInvokeByteArrayViewVarHandle, class_root); // Create java.lang.invoke.ByteBufferViewVarHandle.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/ByteBufferViewVarHandle;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangInvokeByteBufferViewVarHandle, class_root); - mirror::ByteBufferViewVarHandle::SetClass(class_root); + SetClassRoot(ClassRoot::kJavaLangInvokeByteBufferViewVarHandle, class_root); class_root = FindSystemClass(self, "Ldalvik/system/EmulatedStackFrame;"); CHECK(class_root != nullptr); - SetClassRoot(kDalvikSystemEmulatedStackFrame, class_root); - mirror::EmulatedStackFrame::SetClass(class_root); + SetClassRoot(ClassRoot::kDalvikSystemEmulatedStackFrame, class_root); // java.lang.ref classes need to be specially flagged, but otherwise are normal classes // finish initializing Reference class @@ -801,18 +762,17 @@ bool ClassLinker::InitWithoutImage(std::vector> b class_root = FindSystemClass(self, "Ljava/lang/ClassLoader;"); class_root->SetClassLoaderClass(); CHECK_EQ(class_root->GetObjectSize(), mirror::ClassLoader::InstanceSize()); - SetClassRoot(kJavaLangClassLoader, class_root); + SetClassRoot(ClassRoot::kJavaLangClassLoader, class_root); // Set up java.lang.Throwable, java.lang.ClassNotFoundException, and // java.lang.StackTraceElement as a convenience. - SetClassRoot(kJavaLangThrowable, FindSystemClass(self, "Ljava/lang/Throwable;")); - mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable)); - SetClassRoot(kJavaLangClassNotFoundException, + SetClassRoot(ClassRoot::kJavaLangThrowable, FindSystemClass(self, "Ljava/lang/Throwable;")); + SetClassRoot(ClassRoot::kJavaLangClassNotFoundException, FindSystemClass(self, "Ljava/lang/ClassNotFoundException;")); - SetClassRoot(kJavaLangStackTraceElement, FindSystemClass(self, "Ljava/lang/StackTraceElement;")); - SetClassRoot(kJavaLangStackTraceElementArrayClass, + SetClassRoot(ClassRoot::kJavaLangStackTraceElement, + FindSystemClass(self, "Ljava/lang/StackTraceElement;")); + SetClassRoot(ClassRoot::kJavaLangStackTraceElementArrayClass, FindSystemClass(self, "[Ljava/lang/StackTraceElement;")); - mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement)); // Create conflict tables that depend on the class linker. runtime->FixupConflictTables(); @@ -824,15 +784,30 @@ bool ClassLinker::InitWithoutImage(std::vector> b return true; } +static void CreateStringInitBindings(Thread* self, ClassLinker* class_linker) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Find String. -> StringFactory bindings. + ObjPtr string_factory_class = + class_linker->FindSystemClass(self, "Ljava/lang/StringFactory;"); + CHECK(string_factory_class != nullptr); + ObjPtr string_class = GetClassRoot(class_linker); + WellKnownClasses::InitStringInit(string_class, string_factory_class); + // Update the primordial thread. + self->InitStringEntryPoints(); +} + void ClassLinker::FinishInit(Thread* self) { VLOG(startup) << "ClassLinker::FinishInit entering"; + CreateStringInitBindings(self, this); + // Let the heap know some key offsets into java.lang.ref instances // Note: we hard code the field indexes here rather than using FindInstanceField // as the types of the field can't be resolved prior to the runtime being // fully initialized - StackHandleScope<2> hs(self); - Handle java_lang_ref_Reference = hs.NewHandle(GetClassRoot(kJavaLangRefReference)); + StackHandleScope<3> hs(self); + Handle java_lang_ref_Reference = + hs.NewHandle(GetClassRoot(this)); Handle java_lang_ref_FinalizerReference = hs.NewHandle(FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;")); @@ -857,7 +832,7 @@ void ClassLinker::FinishInit(Thread* self) { CHECK_STREQ(zombie->GetTypeDescriptor(), "Ljava/lang/Object;"); // ensure all class_roots_ are initialized - for (size_t i = 0; i < kClassRootsMax; i++) { + for (size_t i = 0; i < static_cast(ClassRoot::kMax); i++) { ClassRoot class_root = static_cast(i); ObjPtr klass = GetClassRoot(class_root); CHECK(klass != nullptr); @@ -866,24 +841,39 @@ void ClassLinker::FinishInit(Thread* self) { // if possible add new checks there to catch errors early } - CHECK(!array_iftable_.IsNull()); + CHECK(GetArrayIfTable() != nullptr); // disable the slow paths in FindClass and CreatePrimitiveClass now // that Object, Class, and Object[] are setup init_done_ = true; + // Under sanitization, the small carve-out to handle stack overflow might not be enough to + // initialize the StackOverflowError class (as it might require running the verifier). Instead, + // ensure that the class will be initialized. + if (kMemoryToolIsAvailable && !Runtime::Current()->IsAotCompiler()) { + verifier::MethodVerifier::Init(); // Need to prepare the verifier. + + ObjPtr soe_klass = FindSystemClass(self, "Ljava/lang/StackOverflowError;"); + if (soe_klass == nullptr || !EnsureInitialized(self, hs.NewHandle(soe_klass), true, true)) { + // Strange, but don't crash. + LOG(WARNING) << "Could not prepare StackOverflowError."; + self->ClearException(); + } + } + VLOG(startup) << "ClassLinker::FinishInit exiting"; } -void ClassLinker::RunRootClinits() { - Thread* self = Thread::Current(); - for (size_t i = 0; i < ClassLinker::kClassRootsMax; ++i) { - ObjPtr c = GetClassRoot(ClassRoot(i)); +void ClassLinker::RunRootClinits(Thread* self) { + for (size_t i = 0; i < static_cast(ClassRoot::kMax); ++i) { + ObjPtr c = GetClassRoot(ClassRoot(i), this); if (!c->IsArrayClass() && !c->IsPrimitive()) { StackHandleScope<1> hs(self); - Handle h_class(hs.NewHandle(GetClassRoot(ClassRoot(i)))); + Handle h_class(hs.NewHandle(c)); EnsureInitialized(self, h_class, true, true); self->AssertNoPendingException(); + } else { + DCHECK(c->IsInitialized()); } } } @@ -1013,54 +1003,17 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { } class_roots_ = GcRoot>( - down_cast*>( - spaces[0]->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots))); - mirror::Class::SetClassClass(class_roots_.Read()->Get(kJavaLangClass)); + ObjPtr>::DownCast(MakeObjPtr( + spaces[0]->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)))); + DCHECK_EQ(GetClassRoot(this)->GetClassFlags(), mirror::kClassFlagClass); - // Special case of setting up the String class early so that we can test arbitrary objects - // as being Strings or not - mirror::String::SetClass(GetClassRoot(kJavaLangString)); - - ObjPtr java_lang_Object = GetClassRoot(kJavaLangObject); + ObjPtr java_lang_Object = GetClassRoot(this); java_lang_Object->SetObjectSize(sizeof(mirror::Object)); // Allocate in non-movable so that it's possible to check if a JNI weak global ref has been // cleared without triggering the read barrier and unintentionally mark the sentinel alive. runtime->SetSentinel(heap->AllocNonMovableObject( self, java_lang_Object, java_lang_Object->GetObjectSize(), VoidFunctor())); - // reinit array_iftable_ from any array class instance, they should be == - array_iftable_ = GcRoot(GetClassRoot(kObjectArrayClass)->GetIfTable()); - DCHECK_EQ(array_iftable_.Read(), GetClassRoot(kBooleanArrayClass)->GetIfTable()); - // String class root was set above - mirror::Field::SetClass(GetClassRoot(kJavaLangReflectField)); - mirror::Field::SetArrayClass(GetClassRoot(kJavaLangReflectFieldArrayClass)); - mirror::Constructor::SetClass(GetClassRoot(kJavaLangReflectConstructor)); - mirror::Constructor::SetArrayClass(GetClassRoot(kJavaLangReflectConstructorArrayClass)); - mirror::Method::SetClass(GetClassRoot(kJavaLangReflectMethod)); - mirror::Method::SetArrayClass(GetClassRoot(kJavaLangReflectMethodArrayClass)); - mirror::CallSite::SetClass(GetClassRoot(kJavaLangInvokeCallSite)); - mirror::MethodHandleImpl::SetClass(GetClassRoot(kJavaLangInvokeMethodHandleImpl)); - mirror::MethodHandlesLookup::SetClass(GetClassRoot(kJavaLangInvokeMethodHandlesLookup)); - mirror::MethodType::SetClass(GetClassRoot(kJavaLangInvokeMethodType)); - mirror::VarHandle::SetClass(GetClassRoot(kJavaLangInvokeVarHandle)); - mirror::FieldVarHandle::SetClass(GetClassRoot(kJavaLangInvokeFieldVarHandle)); - mirror::ArrayElementVarHandle::SetClass(GetClassRoot(kJavaLangInvokeArrayElementVarHandle)); - mirror::ByteArrayViewVarHandle::SetClass(GetClassRoot(kJavaLangInvokeByteArrayViewVarHandle)); - mirror::ByteBufferViewVarHandle::SetClass(GetClassRoot(kJavaLangInvokeByteBufferViewVarHandle)); - mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference)); - mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass)); - mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass)); - mirror::CharArray::SetArrayClass(GetClassRoot(kCharArrayClass)); - mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass)); - mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass)); - mirror::IntArray::SetArrayClass(GetClassRoot(kIntArrayClass)); - mirror::LongArray::SetArrayClass(GetClassRoot(kLongArrayClass)); - mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass)); - mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable)); - mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement)); - mirror::EmulatedStackFrame::SetClass(GetClassRoot(kDalvikSystemEmulatedStackFrame)); - mirror::ClassExt::SetClass(GetClassRoot(kDalvikSystemClassExt)); - for (gc::space::ImageSpace* image_space : spaces) { // Boot class loader, use a null handle. std::vector> dex_files; @@ -1150,7 +1103,7 @@ static bool FlattenPathClassLoader(ObjPtr class_loader, return false; // Stop the visit. } if (name != nullptr) { - out_dex_file_names->push_front(name.Ptr()); + out_dex_file_names->push_front(name); } return true; // Continue with the next Element. }; @@ -1304,12 +1257,12 @@ void AppImageClassLoadersAndDexCachesHelper::Update( ObjPtr klass = types[j].load(std::memory_order_relaxed).object.Read(); if (space->HasAddress(klass.Ptr())) { DCHECK(!klass->IsErroneous()) << klass->GetStatus(); - auto it = new_class_set->Find(ClassTable::TableSlot(klass)); + auto it = new_class_set->find(ClassTable::TableSlot(klass)); DCHECK(it != new_class_set->end()); DCHECK_EQ(it->Read(), klass); ObjPtr super_class = klass->GetSuperClass(); if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) { - auto it2 = new_class_set->Find(ClassTable::TableSlot(super_class)); + auto it2 = new_class_set->find(ClassTable::TableSlot(super_class)); DCHECK(it2 != new_class_set->end()); DCHECK_EQ(it2->Read(), super_class); } @@ -1386,7 +1339,7 @@ static std::unique_ptr OpenOatDexFile(const OatFile* oat_file, REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(error_msg != nullptr); std::unique_ptr dex_file; - const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(location, nullptr, error_msg); + const OatDexFile* oat_dex_file = oat_file->GetOatDexFile(location, nullptr, error_msg); if (oat_dex_file == nullptr) { return std::unique_ptr(); } @@ -1671,20 +1624,20 @@ bool ClassLinker::AddImageSpace( hs.NewHandle(dex_caches_object->AsObjectArray())); Handle> class_roots(hs.NewHandle( header.GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray())); - static_assert(ImageHeader::kClassLoader + 1u == ImageHeader::kImageRootsMax, - "Class loader should be the last image root."); MutableHandle 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(kClassRootsMax)) { + if (class_roots->GetLength() != static_cast(ClassRoot::kMax)) { *error_msg = StringPrintf("Expected %d class roots but got %d", class_roots->GetLength(), - static_cast(kClassRootsMax)); + static_cast(ClassRoot::kMax)); return false; } // Check against existing class roots to make sure they match the ones in the boot image. - for (size_t i = 0; i < kClassRootsMax; i++) { - if (class_roots->Get(i) != GetClassRoot(static_cast(i))) { + ObjPtr> existing_class_roots = GetClassRoots(); + for (size_t i = 0; i < static_cast(ClassRoot::kMax); i++) { + if (class_roots->Get(i) != GetClassRoot(static_cast(i), existing_class_roots)) { *error_msg = "App image class roots must have pointer equality with runtime ones."; return false; } @@ -1828,21 +1781,6 @@ bool ClassLinker::AddImageSpace( header.VisitPackedArtMethods(&visitor, space->Begin(), image_pointer_size_); } - if (!app_image) { - // Make the string intern table and class table immutable for boot image. - // PIC app oat files may mmap a read-only copy into their own .bss section, - // so enforce that the data in the boot image tables remains unchanged. - // - // We cannot do that for app image even after the fixup as some interned - // String references may actually end up pointing to moveable Strings. - uint8_t* const_section_begin = space->Begin() + header.GetBootImageConstantTablesOffset(); - CheckedCall(mprotect, - "protect constant tables", - const_section_begin, - header.GetBootImageConstantTablesSize(), - PROT_READ); - } - ClassTable* class_table = nullptr; { WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); @@ -2001,7 +1939,6 @@ void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) { void ClassLinker::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) { class_roots_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); VisitClassRoots(visitor, flags); - array_iftable_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); // Instead of visiting the find_array_class_cache_ drop it so that it doesn't prevent class // unloading if we are marking roots. DropFindArrayClassCache(); @@ -2127,8 +2064,7 @@ void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor) { // Add 100 in case new classes get loaded when we are filling in the object array. class_table_size = NumZygoteClasses() + NumNonZygoteClasses() + 100; } - ObjPtr class_type = mirror::Class::GetJavaLangClass(); - ObjPtr array_of_class = FindArrayClass(self, &class_type); + ObjPtr array_of_class = GetClassRoot>(this); classes.Assign( mirror::ObjectArray::Alloc(self, array_of_class, class_table_size)); CHECK(classes != nullptr); // OOME. @@ -2151,35 +2087,6 @@ void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor) { } ClassLinker::~ClassLinker() { - mirror::Class::ResetClass(); - mirror::Constructor::ResetClass(); - mirror::Field::ResetClass(); - mirror::Method::ResetClass(); - mirror::Reference::ResetClass(); - mirror::StackTraceElement::ResetClass(); - mirror::String::ResetClass(); - mirror::Throwable::ResetClass(); - mirror::BooleanArray::ResetArrayClass(); - mirror::ByteArray::ResetArrayClass(); - mirror::CharArray::ResetArrayClass(); - mirror::Constructor::ResetArrayClass(); - mirror::DoubleArray::ResetArrayClass(); - mirror::Field::ResetArrayClass(); - mirror::FloatArray::ResetArrayClass(); - mirror::Method::ResetArrayClass(); - mirror::IntArray::ResetArrayClass(); - mirror::LongArray::ResetArrayClass(); - mirror::ShortArray::ResetArrayClass(); - mirror::CallSite::ResetClass(); - mirror::MethodType::ResetClass(); - mirror::MethodHandleImpl::ResetClass(); - mirror::MethodHandlesLookup::ResetClass(); - mirror::VarHandle::ResetClass(); - mirror::FieldVarHandle::ResetClass(); - mirror::ArrayElementVarHandle::ResetClass(); - mirror::ByteArrayViewVarHandle::ResetClass(); - mirror::ByteBufferViewVarHandle::ResetClass(); - mirror::EmulatedStackFrame::ResetClass(); Thread* const self = Thread::Current(); for (const ClassLoaderData& data : class_loaders_) { // CHA unloading analysis is not needed. No negative consequences are expected because @@ -2214,20 +2121,20 @@ void ClassLinker::DeleteClassLoader(Thread* self, const ClassLoaderData& data, b delete data.class_table; } -mirror::PointerArray* ClassLinker::AllocPointerArray(Thread* self, size_t length) { - return down_cast( +ObjPtr ClassLinker::AllocPointerArray(Thread* self, size_t length) { + return ObjPtr::DownCast( image_pointer_size_ == PointerSize::k64 - ? static_cast(mirror::LongArray::Alloc(self, length)) - : static_cast(mirror::IntArray::Alloc(self, length))); + ? ObjPtr(mirror::LongArray::Alloc(self, length)) + : ObjPtr(mirror::IntArray::Alloc(self, length))); } -mirror::DexCache* ClassLinker::AllocDexCache(ObjPtr* out_location, - Thread* self, - const DexFile& dex_file) { +ObjPtr ClassLinker::AllocDexCache(/*out*/ ObjPtr* out_location, + Thread* self, + const DexFile& dex_file) { StackHandleScope<1> hs(self); DCHECK(out_location != nullptr); auto dex_cache(hs.NewHandle(ObjPtr::DownCast( - GetClassRoot(kJavaLangDexCache)->AllocObject(self)))); + GetClassRoot(this)->AllocObject(self)))); if (dex_cache == nullptr) { self->AssertPendingOOMException(); return nullptr; @@ -2241,9 +2148,9 @@ mirror::DexCache* ClassLinker::AllocDexCache(ObjPtr* out_locatio return dex_cache.Get(); } -mirror::DexCache* ClassLinker::AllocAndInitializeDexCache(Thread* self, - const DexFile& dex_file, - LinearAlloc* linear_alloc) { +ObjPtr ClassLinker::AllocAndInitializeDexCache(Thread* self, + const DexFile& dex_file, + LinearAlloc* linear_alloc) { ObjPtr location = nullptr; ObjPtr dex_cache = AllocDexCache(&location, self, dex_file); if (dex_cache != nullptr) { @@ -2256,12 +2163,12 @@ mirror::DexCache* ClassLinker::AllocAndInitializeDexCache(Thread* self, linear_alloc, image_pointer_size_); } - return dex_cache.Ptr(); + return dex_cache; } -mirror::Class* ClassLinker::AllocClass(Thread* self, - ObjPtr java_lang_Class, - uint32_t class_size) { +ObjPtr ClassLinker::AllocClass(Thread* self, + ObjPtr java_lang_Class, + uint32_t class_size) { DCHECK_GE(class_size, sizeof(mirror::Class)); gc::Heap* heap = Runtime::Current()->GetHeap(); mirror::Class::InitializeClassVisitor visitor(class_size); @@ -2275,20 +2182,20 @@ mirror::Class* ClassLinker::AllocClass(Thread* self, return k->AsClass(); } -mirror::Class* ClassLinker::AllocClass(Thread* self, uint32_t class_size) { - return AllocClass(self, GetClassRoot(kJavaLangClass), class_size); +ObjPtr ClassLinker::AllocClass(Thread* self, uint32_t class_size) { + return AllocClass(self, GetClassRoot(this), class_size); } -mirror::ObjectArray* ClassLinker::AllocStackTraceElementArray( +ObjPtr> ClassLinker::AllocStackTraceElementArray( Thread* self, size_t length) { return mirror::ObjectArray::Alloc( - self, GetClassRoot(kJavaLangStackTraceElementArrayClass), length); + self, GetClassRoot>(this), length); } -mirror::Class* ClassLinker::EnsureResolved(Thread* self, - const char* descriptor, - ObjPtr klass) { +ObjPtr ClassLinker::EnsureResolved(Thread* self, + const char* descriptor, + ObjPtr klass) { DCHECK(klass != nullptr); if (kIsDebugBuild) { StackHandleScope<1> hs(self); @@ -2360,7 +2267,7 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self, // Return the loaded class. No exceptions should be pending. CHECK(klass->IsResolved()) << klass->PrettyClass(); self->AssertNoPendingException(); - return klass.Ptr(); + return klass; } typedef std::pair ClassPathEntry; @@ -2382,7 +2289,7 @@ bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnabl const char* descriptor, size_t hash, Handle class_loader, - ObjPtr* result) { + /*out*/ ObjPtr* result) { // Termination case: boot class loader. if (IsBootClassLoader(soa, class_loader.Get())) { *result = FindClassInBootClassLoaderClassPath(self, descriptor, hash); @@ -2496,9 +2403,9 @@ ObjPtr ClassLinker::FindClassInBaseDexClassLoaderClassPath( return ret; } -mirror::Class* ClassLinker::FindClass(Thread* self, - const char* descriptor, - Handle class_loader) { +ObjPtr ClassLinker::FindClass(Thread* self, + const char* descriptor, + Handle class_loader) { DCHECK_NE(*descriptor, '\0') << "descriptor is empty string"; DCHECK(self != nullptr); self->AssertNoPendingException(); @@ -2635,8 +2542,8 @@ mirror::Class* ClassLinker::FindClass(Thread* self, if (old == nullptr) { old = result_ptr; // For the comparison below, after releasing the lock. if (descriptor_equals) { - class_table->InsertWithHash(result_ptr.Ptr(), hash); - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get()); + class_table->InsertWithHash(result_ptr, hash); + WriteBarrier::ForEveryFieldWrite(class_loader.Get()); } // else throw below, after releasing the lock. } } @@ -2663,16 +2570,16 @@ mirror::Class* ClassLinker::FindClass(Thread* self, DescriptorToDot(descriptor).c_str()); return nullptr; } - // success, return mirror::Class* - return result_ptr.Ptr(); + // Success. + return result_ptr; } -mirror::Class* ClassLinker::DefineClass(Thread* self, - const char* descriptor, - size_t hash, - Handle class_loader, - const DexFile& dex_file, - const DexFile::ClassDef& dex_class_def) { +ObjPtr ClassLinker::DefineClass(Thread* self, + const char* descriptor, + size_t hash, + Handle class_loader, + const DexFile& dex_file, + const DexFile::ClassDef& dex_class_def) { StackHandleScope<3> hs(self); auto klass = hs.NewHandle(nullptr); @@ -2680,17 +2587,17 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, if (UNLIKELY(!init_done_)) { // finish up init of hand crafted class_roots_ if (strcmp(descriptor, "Ljava/lang/Object;") == 0) { - klass.Assign(GetClassRoot(kJavaLangObject)); + klass.Assign(GetClassRoot(this)); } else if (strcmp(descriptor, "Ljava/lang/Class;") == 0) { - klass.Assign(GetClassRoot(kJavaLangClass)); + klass.Assign(GetClassRoot(this)); } else if (strcmp(descriptor, "Ljava/lang/String;") == 0) { - klass.Assign(GetClassRoot(kJavaLangString)); + klass.Assign(GetClassRoot(this)); } else if (strcmp(descriptor, "Ljava/lang/ref/Reference;") == 0) { - klass.Assign(GetClassRoot(kJavaLangRefReference)); + klass.Assign(GetClassRoot(this)); } else if (strcmp(descriptor, "Ljava/lang/DexCache;") == 0) { - klass.Assign(GetClassRoot(kJavaLangDexCache)); + klass.Assign(GetClassRoot(this)); } else if (strcmp(descriptor, "Ldalvik/system/ClassExt;") == 0) { - klass.Assign(GetClassRoot(kDalvikSystemClassExt)); + klass.Assign(GetClassRoot(this)); } } @@ -2740,7 +2647,7 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, ObjectLock lock(self, klass); klass->SetClinitThreadId(self->GetTid()); // Make sure we have a valid empty iftable even if there are errors. - klass->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable()); + klass->SetIfTable(GetClassRoot(this)->GetIfTable()); // Add the newly loaded class to the loaded classes table. ObjPtr existing = InsertClass(descriptor, klass.Get(), hash); @@ -2830,52 +2737,50 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, uint32_t ClassLinker::SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def) { - const uint8_t* class_data = dex_file.GetClassData(dex_class_def); size_t num_ref = 0; size_t num_8 = 0; size_t num_16 = 0; size_t num_32 = 0; size_t num_64 = 0; - if (class_data != nullptr) { - // We allow duplicate definitions of the same field in a class_data_item - // but ignore the repeated indexes here, b/21868015. - uint32_t last_field_idx = dex::kDexNoIndex; - for (ClassDataItemIterator it(dex_file, class_data); it.HasNextStaticField(); it.Next()) { - uint32_t field_idx = it.GetMemberIndex(); - // Ordering enforced by DexFileVerifier. - DCHECK(last_field_idx == dex::kDexNoIndex || last_field_idx <= field_idx); - if (UNLIKELY(field_idx == last_field_idx)) { - continue; - } - last_field_idx = field_idx; - const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); - const char* descriptor = dex_file.GetFieldTypeDescriptor(field_id); - char c = descriptor[0]; - switch (c) { - case 'L': - case '[': - num_ref++; - break; - case 'J': - case 'D': - num_64++; - break; - case 'I': - case 'F': - num_32++; - break; - case 'S': - case 'C': - num_16++; - break; - case 'B': - case 'Z': - num_8++; - break; - default: - LOG(FATAL) << "Unknown descriptor: " << c; - UNREACHABLE(); - } + ClassAccessor accessor(dex_file, dex_class_def); + // We allow duplicate definitions of the same field in a class_data_item + // but ignore the repeated indexes here, b/21868015. + uint32_t last_field_idx = dex::kDexNoIndex; + for (const ClassAccessor::Field& field : accessor.GetStaticFields()) { + uint32_t field_idx = field.GetIndex(); + // Ordering enforced by DexFileVerifier. + DCHECK(last_field_idx == dex::kDexNoIndex || last_field_idx <= field_idx); + if (UNLIKELY(field_idx == last_field_idx)) { + continue; + } + last_field_idx = field_idx; + const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); + const char* descriptor = dex_file.GetFieldTypeDescriptor(field_id); + char c = descriptor[0]; + switch (c) { + case 'L': + case '[': + num_ref++; + break; + case 'J': + case 'D': + num_64++; + break; + case 'I': + case 'F': + num_32++; + break; + case 'S': + case 'C': + num_16++; + break; + case 'B': + case 'Z': + num_8++; + break; + default: + LOG(FATAL) << "Unknown descriptor: " << c; + UNREACHABLE(); } } return mirror::Class::ComputeClassSize(false, @@ -2906,6 +2811,7 @@ const void* ClassLinker::GetQuickOatCodeFor(ArtMethod* method) { } bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* quick_code) { + ScopedAssertNoThreadSuspension sants(__FUNCTION__); if (UNLIKELY(method->IsNative() || method->IsProxyMethod())) { return false; } @@ -2935,6 +2841,12 @@ bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* return true; } + if (quick_code == GetQuickInstrumentationEntryPoint()) { + const void* instr_target = instr->GetCodeForInvoke(method); + DCHECK_NE(instr_target, GetQuickInstrumentationEntryPoint()) << method->PrettyMethod(); + return ShouldUseInterpreterEntrypoint(method, instr_target); + } + if (runtime->IsJavaDebuggable()) { // For simplicity, we ignore precompiled code and go to the interpreter // assuming we don't already have jitted code. @@ -2959,6 +2871,7 @@ bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* } void ClassLinker::FixupStaticTrampolines(ObjPtr klass) { + ScopedAssertNoThreadSuspension sants(__FUNCTION__); DCHECK(klass->IsInitialized()) << klass->PrettyDescriptor(); if (klass->NumDirectMethods() == 0) { return; // No direct methods => no static methods. @@ -2971,19 +2884,17 @@ void ClassLinker::FixupStaticTrampolines(ObjPtr klass) { } const DexFile& dex_file = klass->GetDexFile(); - const DexFile::ClassDef* dex_class_def = klass->GetClassDef(); - CHECK(dex_class_def != nullptr); - const uint8_t* class_data = dex_file.GetClassData(*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(class_data != nullptr) << klass->PrettyDescriptor(); - ClassDataItemIterator it(dex_file, class_data); - it.SkipAllFields(); + CHECK(accessor.HasClassData()) << klass->PrettyDescriptor(); bool has_oat_class; OatFile::OatClass oat_class = OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class); // Link the code of methods skipped by LinkCode. - for (size_t method_index = 0; it.HasNextDirectMethod(); ++method_index, it.Next()) { + for (size_t method_index = 0; method_index < accessor.NumDirectMethods(); ++method_index) { ArtMethod* method = klass->GetDirectMethod(method_index, image_pointer_size_); if (!method->IsStatic()) { // Only update static methods. @@ -3020,6 +2931,7 @@ static void LinkCode(ClassLinker* class_linker, ArtMethod* method, const OatFile::OatClass* oat_class, uint32_t class_def_method_index) REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedAssertNoThreadSuspension sants(__FUNCTION__); Runtime* const runtime = Runtime::Current(); if (runtime->IsAotCompiler()) { // The following code only applies to a non-compiler runtime. @@ -3080,7 +2992,7 @@ void ClassLinker::SetupClass(const DexFile& dex_file, const char* descriptor = dex_file.GetClassDescriptor(dex_class_def); CHECK(descriptor != nullptr); - klass->SetClass(GetClassRoot(kJavaLangClass)); + klass->SetClass(GetClassRoot(this)); uint32_t access_flags = dex_class_def.GetJavaAccessFlags(); CHECK_EQ(access_flags & ~kAccJavaFlagsMask, 0U); klass->SetAccessFlags(access_flags); @@ -3092,17 +3004,6 @@ void ClassLinker::SetupClass(const DexFile& dex_file, klass->SetDexTypeIndex(dex_class_def.class_idx_); } -void ClassLinker::LoadClass(Thread* self, - const DexFile& dex_file, - const DexFile::ClassDef& dex_class_def, - Handle klass) { - const uint8_t* class_data = dex_file.GetClassData(dex_class_def); - if (class_data == nullptr) { - return; // no fields or methods - for example a marker interface - } - LoadClassMembers(self, dex_file, class_data, klass); -} - LengthPrefixedArray* ClassLinker::AllocArtFieldArray(Thread* self, LinearAlloc* allocator, size_t length) { @@ -3161,10 +3062,15 @@ LinearAlloc* ClassLinker::GetOrCreateAllocatorForClassLoader(ObjPtr klass) { +void ClassLinker::LoadClass(Thread* self, + const DexFile& dex_file, + const DexFile::ClassDef& dex_class_def, + Handle klass) { + ClassAccessor accessor(dex_file, dex_class_def); + if (!accessor.HasClassData()) { + return; + } + Runtime* const runtime = Runtime::Current(); { // Note: We cannot have thread suspension until the field and method arrays are setup or else // Class::VisitFieldRoots may miss some fields or methods. @@ -3173,45 +3079,79 @@ void ClassLinker::LoadClassMembers(Thread* self, // We allow duplicate definitions of the same field in a class_data_item // but ignore the repeated indexes here, b/21868015. LinearAlloc* const allocator = GetAllocatorForClassLoader(klass->GetClassLoader()); - ClassDataItemIterator it(dex_file, class_data); LengthPrefixedArray* sfields = AllocArtFieldArray(self, allocator, - it.NumStaticFields()); - size_t num_sfields = 0; - uint32_t last_field_idx = 0u; - for (; it.HasNextStaticField(); it.Next()) { - uint32_t field_idx = it.GetMemberIndex(); - DCHECK_GE(field_idx, last_field_idx); // Ordering enforced by DexFileVerifier. - if (num_sfields == 0 || LIKELY(field_idx > last_field_idx)) { - DCHECK_LT(num_sfields, it.NumStaticFields()); - LoadField(it, klass, &sfields->At(num_sfields)); - ++num_sfields; - last_field_idx = field_idx; - } - } - - // Load instance fields. + accessor.NumStaticFields()); LengthPrefixedArray* ifields = AllocArtFieldArray(self, allocator, - it.NumInstanceFields()); + accessor.NumInstanceFields()); + size_t num_sfields = 0u; size_t num_ifields = 0u; - last_field_idx = 0u; - for (; it.HasNextInstanceField(); it.Next()) { - uint32_t field_idx = it.GetMemberIndex(); - DCHECK_GE(field_idx, last_field_idx); // Ordering enforced by DexFileVerifier. - if (num_ifields == 0 || LIKELY(field_idx > last_field_idx)) { - DCHECK_LT(num_ifields, it.NumInstanceFields()); - LoadField(it, klass, &ifields->At(num_ifields)); - ++num_ifields; - last_field_idx = field_idx; - } - } + uint32_t last_static_field_idx = 0u; + uint32_t last_instance_field_idx = 0u; + + // Methods + bool has_oat_class = false; + const OatFile::OatClass oat_class = (runtime->IsStarted() && !runtime->IsAotCompiler()) + ? OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class) + : OatFile::OatClass::Invalid(); + const OatFile::OatClass* oat_class_ptr = has_oat_class ? &oat_class : nullptr; + klass->SetMethodsPtr( + AllocArtMethodArray(self, allocator, accessor.NumMethods()), + accessor.NumDirectMethods(), + accessor.NumVirtualMethods()); + size_t class_def_method_index = 0; + uint32_t last_dex_method_index = dex::kDexNoIndex; + size_t last_class_def_method_index = 0; - if (UNLIKELY(num_sfields != it.NumStaticFields()) || - UNLIKELY(num_ifields != it.NumInstanceFields())) { + // Use the visitor since the ranged based loops are bit slower from seeking. Seeking to the + // methods needs to decode all of the fields. + accessor.VisitFieldsAndMethods([&]( + const ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t field_idx = field.GetIndex(); + DCHECK_GE(field_idx, last_static_field_idx); // Ordering enforced by DexFileVerifier. + if (num_sfields == 0 || LIKELY(field_idx > last_static_field_idx)) { + LoadField(field, klass, &sfields->At(num_sfields)); + ++num_sfields; + last_static_field_idx = field_idx; + } + }, [&](const ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t field_idx = field.GetIndex(); + DCHECK_GE(field_idx, last_instance_field_idx); // Ordering enforced by DexFileVerifier. + if (num_ifields == 0 || LIKELY(field_idx > last_instance_field_idx)) { + LoadField(field, klass, &ifields->At(num_ifields)); + ++num_ifields; + last_instance_field_idx = field_idx; + } + }, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* art_method = klass->GetDirectMethodUnchecked(class_def_method_index, + image_pointer_size_); + LoadMethod(dex_file, method, klass, art_method); + LinkCode(this, art_method, oat_class_ptr, class_def_method_index); + uint32_t it_method_index = method.GetIndex(); + if (last_dex_method_index == it_method_index) { + // duplicate case + art_method->SetMethodIndex(last_class_def_method_index); + } else { + art_method->SetMethodIndex(class_def_method_index); + last_dex_method_index = it_method_index; + last_class_def_method_index = class_def_method_index; + } + ++class_def_method_index; + }, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* art_method = klass->GetVirtualMethodUnchecked( + class_def_method_index - accessor.NumDirectMethods(), + image_pointer_size_); + LoadMethod(dex_file, method, klass, art_method); + LinkCode(this, art_method, oat_class_ptr, class_def_method_index); + ++class_def_method_index; + }); + + if (UNLIKELY(num_ifields + num_sfields != accessor.NumFields())) { LOG(WARNING) << "Duplicate fields in class " << klass->PrettyDescriptor() - << " (unique static fields: " << num_sfields << "/" << it.NumStaticFields() - << ", unique instance fields: " << num_ifields << "/" << it.NumInstanceFields() << ")"; + << " (unique static fields: " << num_sfields << "/" << accessor.NumStaticFields() + << ", unique instance fields: " << num_ifields << "/" << accessor.NumInstanceFields() + << ")"; // NOTE: Not shrinking the over-allocated sfields/ifields, just setting size. if (sfields != nullptr) { sfields->SetSize(num_sfields); @@ -3225,87 +3165,49 @@ void ClassLinker::LoadClassMembers(Thread* self, DCHECK_EQ(klass->NumStaticFields(), num_sfields); klass->SetIFieldsPtr(ifields); DCHECK_EQ(klass->NumInstanceFields(), num_ifields); - // Load methods. - bool has_oat_class = false; - const OatFile::OatClass oat_class = - (Runtime::Current()->IsStarted() && !Runtime::Current()->IsAotCompiler()) - ? OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class) - : OatFile::OatClass::Invalid(); - const OatFile::OatClass* oat_class_ptr = has_oat_class ? &oat_class : nullptr; - klass->SetMethodsPtr( - AllocArtMethodArray(self, allocator, it.NumDirectMethods() + it.NumVirtualMethods()), - it.NumDirectMethods(), - it.NumVirtualMethods()); - size_t class_def_method_index = 0; - uint32_t last_dex_method_index = dex::kDexNoIndex; - size_t last_class_def_method_index = 0; - // TODO These should really use the iterators. - for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) { - ArtMethod* method = klass->GetDirectMethodUnchecked(i, image_pointer_size_); - LoadMethod(dex_file, it, klass, method); - LinkCode(this, method, oat_class_ptr, class_def_method_index); - uint32_t it_method_index = it.GetMemberIndex(); - if (last_dex_method_index == it_method_index) { - // duplicate case - method->SetMethodIndex(last_class_def_method_index); - } else { - method->SetMethodIndex(class_def_method_index); - last_dex_method_index = it_method_index; - last_class_def_method_index = class_def_method_index; - } - class_def_method_index++; - } - for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) { - ArtMethod* method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_); - LoadMethod(dex_file, it, klass, method); - DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i); - LinkCode(this, method, oat_class_ptr, class_def_method_index); - class_def_method_index++; - } - DCHECK(!it.HasNext()); } // Ensure that the card is marked so that remembered sets pick up native roots. - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(klass.Get()); + WriteBarrier::ForEveryFieldWrite(klass.Get()); self->AllowThreadSuspension(); } -void ClassLinker::LoadField(const ClassDataItemIterator& it, +void ClassLinker::LoadField(const ClassAccessor::Field& field, Handle klass, ArtField* dst) { - const uint32_t field_idx = it.GetMemberIndex(); + const uint32_t field_idx = field.GetIndex(); dst->SetDexFieldIndex(field_idx); dst->SetDeclaringClass(klass.Get()); // Get access flags from the DexFile. If this is a boot class path class, // also set its runtime hidden API access flags. - uint32_t access_flags = it.GetFieldAccessFlags(); + uint32_t access_flags = field.GetAccessFlags(); if (klass->IsBootStrapClassLoaded()) { access_flags = - HiddenApiAccessFlags::EncodeForRuntime(access_flags, it.DecodeHiddenAccessFlags()); + HiddenApiAccessFlags::EncodeForRuntime(access_flags, field.DecodeHiddenAccessFlags()); } dst->SetAccessFlags(access_flags); } void ClassLinker::LoadMethod(const DexFile& dex_file, - const ClassDataItemIterator& it, + const ClassAccessor::Method& method, Handle klass, ArtMethod* dst) { - uint32_t dex_method_idx = it.GetMemberIndex(); + const uint32_t dex_method_idx = method.GetIndex(); const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx); const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_); ScopedAssertNoThreadSuspension ants("LoadMethod"); dst->SetDexMethodIndex(dex_method_idx); dst->SetDeclaringClass(klass.Get()); - dst->SetCodeItemOffset(it.GetMethodCodeItemOffset()); + dst->SetCodeItemOffset(method.GetCodeItemOffset()); // Get access flags from the DexFile. If this is a boot class path class, // also set its runtime hidden API access flags. - uint32_t access_flags = it.GetMethodAccessFlags(); + uint32_t access_flags = method.GetAccessFlags(); if (klass->IsBootStrapClassLoaded()) { access_flags = - HiddenApiAccessFlags::EncodeForRuntime(access_flags, it.DecodeHiddenAccessFlags()); + HiddenApiAccessFlags::EncodeForRuntime(access_flags, method.DecodeHiddenAccessFlags()); } if (UNLIKELY(strcmp("finalize", method_name) == 0)) { @@ -3390,9 +3292,10 @@ void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, CHECK_EQ(dex_cache_location, dex_file_suffix); const OatFile* oat_file = (dex_file.GetOatDexFile() != nullptr) ? dex_file.GetOatDexFile()->GetOatFile() : nullptr; - // Clean up pass to remove null dex caches. Also check if we need to initialize OatFile .bss. - // Null dex caches can occur due to class unloading and we are lazily removing null entries. - bool initialize_oat_file_bss = (oat_file != nullptr); + // Clean up pass to remove null dex caches; null dex caches can occur due to class unloading + // and we are lazily removing null entries. Also check if we need to initialize OatFile data + // (.data.bimg.rel.ro and .bss sections) needed for code execution. + bool initialize_oat_file_data = (oat_file != nullptr) && oat_file->IsExecutable(); JavaVMExt* const vm = self->GetJniEnv()->GetVm(); for (auto it = dex_caches_.begin(); it != dex_caches_.end(); ) { DexCacheData data = *it; @@ -3400,15 +3303,36 @@ void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, vm->DeleteWeakGlobalRef(self, data.weak_root); it = dex_caches_.erase(it); } else { - if (initialize_oat_file_bss && + if (initialize_oat_file_data && it->dex_file->GetOatDexFile() != nullptr && it->dex_file->GetOatDexFile()->GetOatFile() == oat_file) { - initialize_oat_file_bss = false; // Already initialized. + initialize_oat_file_data = false; // Already initialized. } ++it; } } - if (initialize_oat_file_bss) { + if (initialize_oat_file_data) { + // Initialize the .data.bimg.rel.ro section. + if (!oat_file->GetBootImageRelocations().empty()) { + uint8_t* reloc_begin = const_cast(oat_file->DataBimgRelRoBegin()); + CheckedCall(mprotect, + "un-protect boot image relocations", + reloc_begin, + oat_file->DataBimgRelRoSize(), + PROT_READ | PROT_WRITE); + uint32_t boot_image_begin = dchecked_integral_cast(reinterpret_cast( + Runtime::Current()->GetHeap()->GetBootImageSpaces().front()->Begin())); + for (const uint32_t& relocation : oat_file->GetBootImageRelocations()) { + const_cast(relocation) += boot_image_begin; + } + CheckedCall(mprotect, + "protect boot image relocations", + reloc_begin, + oat_file->DataBimgRelRoSize(), + PROT_READ); + } + + // Initialize the .bss section. // TODO: Pre-initialize from boot/app image? ArtMethod* resolution_method = Runtime::Current()->GetResolutionMethod(); for (ArtMethod*& entry : oat_file->GetBssMethods()) { @@ -3430,7 +3354,7 @@ void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, if (class_loader != nullptr) { // Since we added a strong root to the class table, do the write barrier as required for // remembered sets and generational GCs. - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader); + WriteBarrier::ForEveryFieldWrite(class_loader); } dex_caches_.push_back(data); } @@ -3458,6 +3382,7 @@ ObjPtr ClassLinker::EnsureSameClassLoader( void ClassLinker::RegisterExistingDexCache(ObjPtr dex_cache, ObjPtr class_loader) { + SCOPED_TRACE << __FUNCTION__ << " " << dex_cache->GetDexFile()->GetLocation(); Thread* self = Thread::Current(); StackHandleScope<2> hs(self); Handle h_dex_cache(hs.NewHandle(dex_cache)); @@ -3479,13 +3404,17 @@ void ClassLinker::RegisterExistingDexCache(ObjPtr dex_cache, WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); table = InsertClassTableForClassLoader(h_class_loader.Get()); } + // Avoid a deadlock between a garbage collecting thread running a checkpoint, + // a thread holding the dex lock and blocking on a condition variable regarding + // weak references access, and a thread blocking on the dex lock. + gc::ScopedGCCriticalSection gcs(self, gc::kGcCauseClassLinker, gc::kCollectorTypeClassLinker); WriterMutexLock mu(self, *Locks::dex_lock_); RegisterDexFileLocked(*dex_file, h_dex_cache.Get(), h_class_loader.Get()); table->InsertStrongRoot(h_dex_cache.Get()); if (h_class_loader.Get() != nullptr) { // Since we added a strong root to the class table, do the write barrier as required for // remembered sets and generational GCs. - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(h_class_loader.Get()); + WriteBarrier::ForEveryFieldWrite(h_class_loader.Get()); } } @@ -3501,6 +3430,7 @@ ObjPtr ClassLinker::RegisterDexFile(const DexFile& dex_file, if (old_dex_cache != nullptr) { return EnsureSameClassLoader(self, old_dex_cache, old_data, class_loader); } + SCOPED_TRACE << __FUNCTION__ << " " << dex_file.GetLocation(); LinearAlloc* const linear_alloc = GetOrCreateAllocatorForClassLoader(class_loader); DCHECK(linear_alloc != nullptr); ClassTable* table; @@ -3519,6 +3449,10 @@ ObjPtr ClassLinker::RegisterDexFile(const DexFile& dex_file, dex_file))); Handle h_location(hs.NewHandle(location)); { + // Avoid a deadlock between a garbage collecting thread running a checkpoint, + // a thread holding the dex lock and blocking on a condition variable regarding + // weak references access, and a thread blocking on the dex lock. + gc::ScopedGCCriticalSection gcs(self, gc::kGcCauseClassLinker, gc::kCollectorTypeClassLinker); WriterMutexLock mu(self, *Locks::dex_lock_); old_data = FindDexCacheDataLocked(dex_file); old_dex_cache = DecodeDexCache(self, old_data); @@ -3551,7 +3485,7 @@ ObjPtr ClassLinker::RegisterDexFile(const DexFile& dex_file, if (h_class_loader.Get() != nullptr) { // Since we added a strong root to the class table, do the write barrier as required for // remembered sets and generational GCs. - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(h_class_loader.Get()); + WriteBarrier::ForEveryFieldWrite(h_class_loader.Get()); } return h_dex_cache.Get(); } @@ -3608,27 +3542,20 @@ ClassLinker::DexCacheData ClassLinker::FindDexCacheDataLocked(const DexFile& dex return DexCacheData(); } -mirror::Class* ClassLinker::CreatePrimitiveClass(Thread* self, Primitive::Type type) { - ObjPtr klass = +ObjPtr ClassLinker::CreatePrimitiveClass(Thread* self, Primitive::Type type) { + ObjPtr primitive_class = AllocClass(self, mirror::Class::PrimitiveClassSize(image_pointer_size_)); - if (UNLIKELY(klass == nullptr)) { + if (UNLIKELY(primitive_class == nullptr)) { self->AssertPendingOOMException(); return nullptr; } - return InitializePrimitiveClass(klass, type); -} - -mirror::Class* ClassLinker::InitializePrimitiveClass(ObjPtr primitive_class, - Primitive::Type type) { - CHECK(primitive_class != nullptr); // Must hold lock on object when initializing. - Thread* self = Thread::Current(); StackHandleScope<1> hs(self); Handle h_class(hs.NewHandle(primitive_class)); ObjectLock lock(self, h_class); h_class->SetAccessFlags(kAccPublic | kAccFinal | kAccAbstract); h_class->SetPrimitiveType(type); - h_class->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable()); + h_class->SetIfTable(GetClassRoot(this)->GetIfTable()); mirror::Class::SetStatus(h_class, ClassStatus::kInitialized, self); const char* descriptor = Primitive::Descriptor(type); ObjPtr existing = InsertClass(descriptor, @@ -3638,6 +3565,10 @@ mirror::Class* ClassLinker::InitializePrimitiveClass(ObjPtr primi return h_class.Get(); } +inline ObjPtr ClassLinker::GetArrayIfTable() { + return GetClassRoot>(this)->GetIfTable(); +} + // Create an array class (i.e. the class object for the array, not the // array itself). "descriptor" looks like "[C" or "[[[[B" or // "[Ljava/lang/String;". @@ -3651,8 +3582,10 @@ mirror::Class* ClassLinker::InitializePrimitiveClass(ObjPtr primi // array class; that always comes from the base element class. // // Returns null with an exception raised on failure. -mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descriptor, size_t hash, - Handle class_loader) { +ObjPtr ClassLinker::CreateArrayClass(Thread* self, + const char* descriptor, + size_t hash, + Handle class_loader) { // Identify the underlying component type CHECK_EQ('[', descriptor[0]); StackHandleScope<2> hs(self); @@ -3695,7 +3628,7 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto ObjPtr new_class = LookupClass(self, descriptor, hash, component_type->GetClassLoader()); if (new_class != nullptr) { - return new_class.Ptr(); + return new_class; } } @@ -3711,17 +3644,15 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto if (UNLIKELY(!init_done_)) { // Classes that were hand created, ie not by FindSystemClass if (strcmp(descriptor, "[Ljava/lang/Class;") == 0) { - new_class.Assign(GetClassRoot(kClassArrayClass)); + new_class.Assign(GetClassRoot>(this)); } else if (strcmp(descriptor, "[Ljava/lang/Object;") == 0) { - new_class.Assign(GetClassRoot(kObjectArrayClass)); - } else if (strcmp(descriptor, GetClassRootDescriptor(kJavaLangStringArrayClass)) == 0) { - new_class.Assign(GetClassRoot(kJavaLangStringArrayClass)); - } else if (strcmp(descriptor, "[C") == 0) { - new_class.Assign(GetClassRoot(kCharArrayClass)); + new_class.Assign(GetClassRoot>(this)); + } else if (strcmp(descriptor, "[Ljava/lang/String;") == 0) { + new_class.Assign(GetClassRoot>(this)); } else if (strcmp(descriptor, "[I") == 0) { - new_class.Assign(GetClassRoot(kIntArrayClass)); + new_class.Assign(GetClassRoot(this)); } else if (strcmp(descriptor, "[J") == 0) { - new_class.Assign(GetClassRoot(kLongArrayClass)); + new_class.Assign(GetClassRoot(this)); } } if (new_class == nullptr) { @@ -3734,7 +3665,7 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto } ObjectLock lock(self, new_class); // Must hold lock on object when initializing. DCHECK(new_class->GetComponentType() != nullptr); - ObjPtr java_lang_Object = GetClassRoot(kJavaLangObject); + ObjPtr java_lang_Object = GetClassRoot(this); new_class->SetSuperClass(java_lang_Object); new_class->SetVTable(java_lang_Object->GetVTable()); new_class->SetPrimitiveType(Primitive::kPrimNot); @@ -3765,7 +3696,7 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto // Use the single, global copies of "interfaces" and "iftable" // (remember not to free them for arrays). { - ObjPtr array_iftable = array_iftable_.Read(); + ObjPtr array_iftable = GetArrayIfTable(); CHECK(array_iftable != nullptr); new_class->SetIfTable(array_iftable); } @@ -3798,29 +3729,30 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto // // (Yes, this happens.) - return existing.Ptr(); + return existing; } -mirror::Class* ClassLinker::FindPrimitiveClass(char type) { +ObjPtr ClassLinker::FindPrimitiveClass(char type) { + ObjPtr> class_roots = GetClassRoots(); switch (type) { case 'B': - return GetClassRoot(kPrimitiveByte); + return GetClassRoot(ClassRoot::kPrimitiveByte, class_roots); case 'C': - return GetClassRoot(kPrimitiveChar); + return GetClassRoot(ClassRoot::kPrimitiveChar, class_roots); case 'D': - return GetClassRoot(kPrimitiveDouble); + return GetClassRoot(ClassRoot::kPrimitiveDouble, class_roots); case 'F': - return GetClassRoot(kPrimitiveFloat); + return GetClassRoot(ClassRoot::kPrimitiveFloat, class_roots); case 'I': - return GetClassRoot(kPrimitiveInt); + return GetClassRoot(ClassRoot::kPrimitiveInt, class_roots); case 'J': - return GetClassRoot(kPrimitiveLong); + return GetClassRoot(ClassRoot::kPrimitiveLong, class_roots); case 'S': - return GetClassRoot(kPrimitiveShort); + return GetClassRoot(ClassRoot::kPrimitiveShort, class_roots); case 'Z': - return GetClassRoot(kPrimitiveBoolean); + return GetClassRoot(ClassRoot::kPrimitiveBoolean, class_roots); case 'V': - return GetClassRoot(kPrimitiveVoid); + return GetClassRoot(ClassRoot::kPrimitiveVoid, class_roots); default: break; } @@ -3829,7 +3761,9 @@ mirror::Class* ClassLinker::FindPrimitiveClass(char type) { return nullptr; } -mirror::Class* ClassLinker::InsertClass(const char* descriptor, ObjPtr klass, size_t hash) { +ObjPtr ClassLinker::InsertClass(const char* descriptor, + ObjPtr klass, + size_t hash) { if (VLOG_IS_ON(class_linker)) { ObjPtr dex_cache = klass->GetDexCache(); std::string source; @@ -3845,13 +3779,13 @@ mirror::Class* ClassLinker::InsertClass(const char* descriptor, ObjPtr existing = class_table->Lookup(descriptor, hash); if (existing != nullptr) { - return existing.Ptr(); + return existing; } VerifyObject(klass); class_table->InsertWithHash(klass, hash); if (class_loader != nullptr) { // This is necessary because we need to have the card dirtied for remembered sets. - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader); + WriteBarrier::ForEveryFieldWrite(class_loader); } if (log_new_roots_) { new_class_roots_.push_back(GcRoot(klass)); @@ -3881,25 +3815,25 @@ void ClassLinker::UpdateClassMethods(ObjPtr klass, klass->NumDirectMethods(), klass->NumDeclaredVirtualMethods()); // Need to mark the card so that the remembered sets and mod union tables get updated. - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(klass); + WriteBarrier::ForEveryFieldWrite(klass); } -mirror::Class* ClassLinker::LookupClass(Thread* self, - const char* descriptor, - ObjPtr class_loader) { +ObjPtr ClassLinker::LookupClass(Thread* self, + const char* descriptor, + ObjPtr class_loader) { return LookupClass(self, descriptor, ComputeModifiedUtf8Hash(descriptor), class_loader); } -mirror::Class* ClassLinker::LookupClass(Thread* self, - const char* descriptor, - size_t hash, - ObjPtr class_loader) { +ObjPtr ClassLinker::LookupClass(Thread* self, + const char* descriptor, + size_t hash, + ObjPtr class_loader) { ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); ClassTable* const class_table = ClassTableForClassLoader(class_loader); if (class_table != nullptr) { ObjPtr result = class_table->Lookup(descriptor, hash); if (result != nullptr) { - return result.Ptr(); + return result; } } return nullptr; @@ -4257,7 +4191,7 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, } } - const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); + const OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); // In case we run without an image there won't be a backing oat file. if (oat_dex_file == nullptr || oat_dex_file->GetOatFile() == nullptr) { return false; @@ -4346,16 +4280,16 @@ void ClassLinker::ResolveMethodExceptionHandlerTypes(ArtMethod* method) { } } -mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa, - jstring name, - jobjectArray interfaces, - jobject loader, - jobjectArray methods, - jobjectArray throws) { +ObjPtr ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa, + jstring name, + jobjectArray interfaces, + jobject loader, + jobjectArray methods, + jobjectArray throws) { Thread* self = soa.Self(); StackHandleScope<10> hs(self); MutableHandle temp_klass(hs.NewHandle( - AllocClass(self, GetClassRoot(kJavaLangClass), sizeof(mirror::Class)))); + AllocClass(self, GetClassRoot(this), sizeof(mirror::Class)))); if (temp_klass == nullptr) { CHECK(self->IsExceptionPending()); // OOME. return nullptr; @@ -4368,9 +4302,9 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& temp_klass->SetClassLoader(soa.Decode(loader)); DCHECK_EQ(temp_klass->GetPrimitiveType(), Primitive::kPrimNot); temp_klass->SetName(soa.Decode(name)); - temp_klass->SetDexCache(GetClassRoot(kJavaLangReflectProxy)->GetDexCache()); + temp_klass->SetDexCache(GetClassRoot(this)->GetDexCache()); // Object has an empty iftable, copy it for that reason. - temp_klass->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable()); + temp_klass->SetIfTable(GetClassRoot(this)->GetIfTable()); mirror::Class::SetStatus(temp_klass, ClassStatus::kIdx, self); std::string descriptor(GetDescriptorForProxy(temp_klass.Get())); const size_t hash = ComputeModifiedUtf8Hash(descriptor.c_str()); @@ -4408,7 +4342,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& // They have as many virtual methods as the array auto h_methods = hs.NewHandle(soa.Decode>(methods)); - DCHECK_EQ(h_methods->GetClass(), mirror::Method::ArrayClass()) + DCHECK_EQ(h_methods->GetClass(), GetClassRoot>()) << mirror::Class::PrettyClass(h_methods->GetClass()); const size_t num_virtual_methods = h_methods->GetLength(); @@ -4437,7 +4371,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& } // The super class is java.lang.reflect.Proxy - temp_klass->SetSuperClass(GetClassRoot(kJavaLangReflectProxy)); + temp_klass->SetSuperClass(GetClassRoot(this)); // Now effectively in the loaded state. mirror::Class::SetStatus(temp_klass, ClassStatus::kLoaded, self); self->AssertNoPendingException(); @@ -4525,12 +4459,13 @@ std::string ClassLinker::GetDescriptorForProxy(ObjPtr proxy_class void ClassLinker::CreateProxyConstructor(Handle klass, ArtMethod* out) { // Create constructor for Proxy that must initialize the method. - CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 21u); + ObjPtr proxy_class = GetClassRoot(this); + CHECK_EQ(proxy_class->NumDirectMethods(), 21u); // Find the (InvocationHandler)V method. The exact method offset varies depending // on which front-end compiler was used to build the libcore DEX files. - ArtMethod* proxy_constructor = GetClassRoot(kJavaLangReflectProxy)->FindConstructor( - "(Ljava/lang/reflect/InvocationHandler;)V", image_pointer_size_); + ArtMethod* proxy_constructor = + jni::DecodeArtMethod(WellKnownClasses::java_lang_reflect_Proxy_init); DCHECK(proxy_constructor != nullptr) << "Could not find method in java.lang.reflect.Proxy"; @@ -4853,24 +4788,29 @@ bool ClassLinker::InitializeClass(Thread* self, Handle klass, this, *dex_class_def); const DexFile& dex_file = *dex_cache->GetDexFile(); - const uint8_t* class_data = dex_file.GetClassData(*dex_class_def); - ClassDataItemIterator field_it(dex_file, class_data); + if (value_it.HasNext()) { - DCHECK(field_it.HasNextStaticField()); + ClassAccessor accessor(dex_file, *dex_class_def); CHECK(can_init_statics); - for ( ; value_it.HasNext(); value_it.Next(), field_it.Next()) { - ArtField* field = ResolveField( - field_it.GetMemberIndex(), dex_cache, class_loader, /* is_static */ true); + for (const ClassAccessor::Field& field : accessor.GetStaticFields()) { + if (!value_it.HasNext()) { + break; + } + ArtField* art_field = ResolveField(field.GetIndex(), + dex_cache, + class_loader, + /* is_static */ true); if (Runtime::Current()->IsActiveTransaction()) { - value_it.ReadValueToField(field); + value_it.ReadValueToField(art_field); } else { - value_it.ReadValueToField(field); + value_it.ReadValueToField(art_field); } if (self->IsExceptionPending()) { break; } - DCHECK(!value_it.HasNext() || field_it.HasNextStaticField()); + value_it.Next(); } + DCHECK(self->IsExceptionPending() || !value_it.HasNext()); } } @@ -5279,7 +5219,7 @@ void ClassLinker::FixupTemporaryDeclaringClass(ObjPtr temp_class, // Make sure the remembered set and mod-union tables know that we updated some of the native // roots. - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(new_class); + WriteBarrier::ForEveryFieldWrite(new_class); } void ClassLinker::RegisterClassLoader(ObjPtr class_loader) { @@ -5437,7 +5377,7 @@ bool ClassLinker::LinkClass(Thread* self, if (class_loader != nullptr) { // We updated the class in the class table, perform the write barrier so that the GC knows // about the change. - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader); + WriteBarrier::ForEveryFieldWrite(class_loader); } CHECK_EQ(existing, klass.Get()); if (log_new_roots_) { @@ -5527,7 +5467,8 @@ bool ClassLinker::LoadSuperAndInterfaces(Handle klass, const DexF bool ClassLinker::LinkSuperClass(Handle klass) { CHECK(!klass->IsPrimitive()); ObjPtr super = klass->GetSuperClass(); - if (klass.Get() == GetClassRoot(kJavaLangObject)) { + ObjPtr object_class = GetClassRoot(this); + if (klass.Get() == object_class) { if (super != nullptr) { ThrowClassFormatError(klass.Get(), "java.lang.Object must not have a superclass"); return false; @@ -5540,7 +5481,7 @@ bool ClassLinker::LinkSuperClass(Handle klass) { return false; } // Verify - if (klass->IsInterface() && super != GetClassRoot(kJavaLangObject)) { + if (klass->IsInterface() && super != object_class) { ThrowClassFormatError(klass.Get(), "Interfaces must have java.lang.Object as superclass"); return false; } @@ -5583,7 +5524,7 @@ bool ClassLinker::LinkSuperClass(Handle klass) { klass->SetClassFlags(klass->GetClassFlags() | reference_flags); } // Disallow custom direct subclasses of java.lang.ref.Reference. - if (init_done_ && super == GetClassRoot(kJavaLangRefReference)) { + if (init_done_ && super == GetClassRoot(this)) { ThrowLinkageError(klass.Get(), "Class %s attempts to subclass java.lang.ref.Reference, which is not allowed", klass->PrettyDescriptor().c_str()); @@ -5803,8 +5744,8 @@ bool ClassLinker::LinkVirtualMethods( klass->SetVTable(super_vtable); return true; } - vtable = hs.NewHandle(down_cast( - super_vtable->CopyOf(self, max_count))); + vtable = hs.NewHandle( + ObjPtr::DownCast(super_vtable->CopyOf(self, max_count))); if (UNLIKELY(vtable == nullptr)) { self->AssertPendingOOMException(); return false; @@ -5940,7 +5881,7 @@ bool ClassLinker::LinkVirtualMethods( // Shrink vtable if possible CHECK_LE(actual_count, max_count); if (actual_count < max_count) { - vtable.Assign(down_cast(vtable->CopyOf(self, actual_count))); + vtable.Assign(ObjPtr::DownCast(vtable->CopyOf(self, actual_count))); if (UNLIKELY(vtable == nullptr)) { self->AssertPendingOOMException(); return false; @@ -5948,13 +5889,13 @@ bool ClassLinker::LinkVirtualMethods( } klass->SetVTable(vtable.Get()); } else { - CHECK_EQ(klass.Get(), GetClassRoot(kJavaLangObject)); + CHECK_EQ(klass.Get(), GetClassRoot(this)); if (!IsUint<16>(num_virtual_methods)) { ThrowClassFormatError(klass.Get(), "Too many methods: %d", static_cast(num_virtual_methods)); return false; } - auto* vtable = AllocPointerArray(self, num_virtual_methods); + ObjPtr vtable = AllocPointerArray(self, num_virtual_methods); if (UNLIKELY(vtable == nullptr)) { self->AssertPendingOOMException(); return false; @@ -6172,7 +6113,7 @@ ArtMethod* ClassLinker::AddMethodToConflictTable(ObjPtr klass, // Note that there is a race in the presence of multiple threads and we may leak // memory from the LinearAlloc, but that's a tradeoff compared to using // atomic operations. - QuasiAtomic::ThreadFenceRelease(); + std::atomic_thread_fence(std::memory_order_release); new_conflict_method->SetImtConflictTable(new_table, image_pointer_size_); return new_conflict_method; } @@ -6198,7 +6139,8 @@ bool ClassLinker::AllocateIfTableMethodArrays(Thread* self, DCHECK(if_table != nullptr); DCHECK(if_table->GetMethodArray(i) != nullptr); // If we are working on a super interface, try extending the existing method array. - method_array = down_cast(if_table->GetMethodArray(i)->Clone(self)); + method_array = ObjPtr::DownCast(MakeObjPtr( + if_table->GetMethodArray(i)->Clone(self))); } else { method_array = AllocPointerArray(self, num_methods); } @@ -6462,7 +6404,7 @@ static bool NotSubinterfaceOfAny( // iftable must be large enough to hold all interfaces without changing its size. static size_t FillIfTable(ObjPtr iftable, size_t super_ifcount, - std::vector to_process) + std::vector> to_process) REQUIRES(Roles::uninterruptible_) REQUIRES_SHARED(Locks::mutator_lock_) { // This is the set of all class's already in the iftable. Used to make checking if a class has @@ -6602,11 +6544,11 @@ bool ClassLinker::SetupInterfaceLookupTable(Thread* self, Handle size_t new_ifcount; { ScopedAssertNoThreadSuspension nts("Copying mirror::Class*'s for FillIfTable"); - std::vector to_add; + std::vector> to_add; for (size_t i = 0; i < num_interfaces; i++) { ObjPtr interface = have_interfaces ? interfaces->Get(i) : mirror::Class::GetDirectInterface(self, klass.Get(), i); - to_add.push_back(interface.Ptr()); + to_add.push_back(interface); } new_ifcount = FillIfTable(iftable.Get(), super_ifcount, std::move(to_add)); @@ -6617,7 +6559,7 @@ bool ClassLinker::SetupInterfaceLookupTable(Thread* self, Handle // Shrink iftable in case duplicates were found if (new_ifcount < ifcount) { DCHECK_NE(num_interfaces, 0U); - iftable.Assign(down_cast( + iftable.Assign(ObjPtr::DownCast( iftable->CopyOf(self, new_ifcount * mirror::IfTable::kMax))); if (UNLIKELY(iftable == nullptr)) { self->AssertPendingOOMException(); @@ -7131,7 +7073,7 @@ ObjPtr ClassLinker::LinkInterfaceMethodsHelper::UpdateVtab default_conflict_methods_.size(); ObjPtr vtable = - down_cast(old_vtable->CopyOf(self_, new_vtable_count)); + ObjPtr::DownCast(old_vtable->CopyOf(self_, new_vtable_count)); if (UNLIKELY(vtable == nullptr)) { self_->AssertPendingOOMException(); return nullptr; @@ -7744,14 +7686,15 @@ void ClassLinker::CreateReferenceInstanceOffsets(Handle klass) { klass->SetReferenceInstanceOffsets(reference_offsets); } -ObjPtr ClassLinker::ResolveString(dex::StringIndex string_idx, - Handle dex_cache) { - DCHECK(dex_cache != nullptr); - Thread::PoisonObjectPointersIfDebug(); - ObjPtr resolved = dex_cache->GetResolvedString(string_idx); - if (resolved != nullptr) { - return resolved; - } +ObjPtr ClassLinker::DoResolveString(dex::StringIndex string_idx, + ObjPtr dex_cache) { + StackHandleScope<1> hs(Thread::Current()); + Handle h_dex_cache(hs.NewHandle(dex_cache)); + return DoResolveString(string_idx, h_dex_cache); +} + +ObjPtr ClassLinker::DoResolveString(dex::StringIndex string_idx, + Handle dex_cache) { const DexFile& dex_file = *dex_cache->GetDexFile(); uint32_t utf16_length; const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length); @@ -7762,13 +7705,9 @@ ObjPtr ClassLinker::ResolveString(dex::StringIndex string_idx, return string; } -ObjPtr ClassLinker::LookupString(dex::StringIndex string_idx, - ObjPtr dex_cache) { +ObjPtr ClassLinker::DoLookupString(dex::StringIndex string_idx, + ObjPtr dex_cache) { DCHECK(dex_cache != nullptr); - ObjPtr resolved = dex_cache->GetResolvedString(string_idx); - if (resolved != nullptr) { - return resolved; - } const DexFile& dex_file = *dex_cache->GetDexFile(); uint32_t utf16_length; const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length); @@ -7780,6 +7719,11 @@ ObjPtr ClassLinker::LookupString(dex::StringIndex string_idx, return string; } +ObjPtr ClassLinker::DoLookupResolvedType(dex::TypeIndex type_idx, + ObjPtr referrer) { + return DoLookupResolvedType(type_idx, referrer->GetDexCache(), referrer->GetClassLoader()); +} + ObjPtr ClassLinker::DoLookupResolvedType(dex::TypeIndex type_idx, ObjPtr dex_cache, ObjPtr class_loader) { @@ -7796,7 +7740,7 @@ ObjPtr ClassLinker::DoLookupResolvedType(dex::TypeIndex type_idx, DCHECK(self != nullptr); const size_t hash = ComputeModifiedUtf8Hash(descriptor); // Find the class in the loaded classes table. - type = LookupClass(self, descriptor, hash, class_loader.Ptr()); + type = LookupClass(self, descriptor, hash, class_loader); } if (type != nullptr) { if (type->IsResolved()) { @@ -7808,6 +7752,14 @@ ObjPtr ClassLinker::DoLookupResolvedType(dex::TypeIndex type_idx, return type; } +ObjPtr ClassLinker::DoResolveType(dex::TypeIndex type_idx, + ObjPtr referrer) { + StackHandleScope<2> hs(Thread::Current()); + Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); + Handle class_loader(hs.NewHandle(referrer->GetClassLoader())); + return DoResolveType(type_idx, dex_cache, class_loader); +} + ObjPtr ClassLinker::DoResolveType(dex::TypeIndex type_idx, Handle dex_cache, Handle class_loader) { @@ -7825,7 +7777,7 @@ ObjPtr ClassLinker::DoResolveType(dex::TypeIndex type_idx, // Convert a ClassNotFoundException to a NoClassDefFoundError. StackHandleScope<1> hs(self); Handle cause(hs.NewHandle(self->GetException())); - if (cause->InstanceOf(GetClassRoot(kJavaLangClassNotFoundException))) { + if (cause->InstanceOf(GetClassRoot(ClassRoot::kJavaLangClassNotFoundException, this))) { DCHECK(resolved == nullptr); // No Handle needed to preserve resolved. self->ClearException(); ThrowNoClassDefFoundError("Failed resolution of: %s", descriptor); @@ -8163,7 +8115,7 @@ ArtField* ClassLinker::FindResolvedFieldJLS(ObjPtr klass, ObjPtr ClassLinker::ResolveMethodType( Thread* self, - uint32_t proto_idx, + dex::ProtoIndex proto_idx, Handle dex_cache, Handle class_loader) { DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); @@ -8171,7 +8123,7 @@ ObjPtr ClassLinker::ResolveMethodType( ObjPtr resolved = dex_cache->GetResolvedMethodType(proto_idx); if (resolved != nullptr) { - return resolved.Ptr(); + return resolved; } StackHandleScope<4> hs(self); @@ -8192,8 +8144,7 @@ ObjPtr ClassLinker::ResolveMethodType( // other than by looking at the shorty ? const size_t num_method_args = strlen(dex_file.StringDataByIdx(proto_id.shorty_idx_)) - 1; - ObjPtr class_type = mirror::Class::GetJavaLangClass(); - ObjPtr array_of_class = FindArrayClass(self, &class_type); + ObjPtr array_of_class = GetClassRoot>(this); Handle> method_params(hs.NewHandle( mirror::ObjectArray::Alloc(self, array_of_class, num_method_args))); if (method_params == nullptr) { @@ -8225,7 +8176,7 @@ ObjPtr ClassLinker::ResolveMethodType( } ObjPtr ClassLinker::ResolveMethodType(Thread* self, - uint32_t proto_idx, + dex::ProtoIndex proto_idx, ArtMethod* referrer) { StackHandleScope<2> hs(self); Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); @@ -8240,29 +8191,34 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForField( DexFile::MethodHandleType handle_type = static_cast(method_handle.method_handle_type_); mirror::MethodHandle::Kind kind; + bool is_put; bool is_static; int32_t num_params; switch (handle_type) { case DexFile::MethodHandleType::kStaticPut: { kind = mirror::MethodHandle::Kind::kStaticPut; + is_put = true; is_static = true; num_params = 1; break; } case DexFile::MethodHandleType::kStaticGet: { kind = mirror::MethodHandle::Kind::kStaticGet; + is_put = false; is_static = true; num_params = 0; break; } case DexFile::MethodHandleType::kInstancePut: { kind = mirror::MethodHandle::Kind::kInstancePut; + is_put = true; is_static = false; num_params = 2; break; } case DexFile::MethodHandleType::kInstanceGet: { kind = mirror::MethodHandle::Kind::kInstanceGet; + is_put = false; is_static = false; num_params = 1; break; @@ -8284,17 +8240,20 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForField( ThrowIllegalAccessErrorField(referring_class, target_field); return nullptr; } + if (UNLIKELY(is_put && target_field->IsFinal())) { + ThrowIllegalAccessErrorField(referring_class, target_field); + return nullptr; + } } else { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; } StackHandleScope<4> hs(self); - ObjPtr class_type = mirror::Class::GetJavaLangClass(); - ObjPtr array_of_class = FindArrayClass(self, &class_type); + ObjPtr array_of_class = GetClassRoot>(this); Handle> method_params(hs.NewHandle( mirror::ObjectArray::Alloc(self, array_of_class, num_params))); - if (UNLIKELY(method_params.Get() == nullptr)) { + if (UNLIKELY(method_params == nullptr)) { DCHECK(self->IsExceptionPending()); return nullptr; } @@ -8469,8 +8428,7 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod( int32_t num_params = static_cast(shorty_length + receiver_count - 1); StackHandleScope<7> hs(self); - ObjPtr class_type = mirror::Class::GetJavaLangClass(); - ObjPtr array_of_class = FindArrayClass(self, &class_type); + ObjPtr array_of_class = GetClassRoot>(this); Handle> method_params(hs.NewHandle( mirror::ObjectArray::Alloc(self, array_of_class, num_params))); if (method_params.Get() == nullptr) { @@ -8643,67 +8601,10 @@ void ClassLinker::SetClassRoot(ClassRoot class_root, ObjPtr klass mirror::ObjectArray* class_roots = class_roots_.Read(); DCHECK(class_roots != nullptr); - DCHECK(class_roots->Get(class_root) == nullptr); - class_roots->Set(class_root, klass); -} - -const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) { - static const char* class_roots_descriptors[] = { - "Ljava/lang/Class;", - "Ljava/lang/Object;", - "[Ljava/lang/Class;", - "[Ljava/lang/Object;", - "Ljava/lang/String;", - "Ljava/lang/DexCache;", - "Ljava/lang/ref/Reference;", - "Ljava/lang/reflect/Constructor;", - "Ljava/lang/reflect/Field;", - "Ljava/lang/reflect/Method;", - "Ljava/lang/reflect/Proxy;", - "[Ljava/lang/String;", - "[Ljava/lang/reflect/Constructor;", - "[Ljava/lang/reflect/Field;", - "[Ljava/lang/reflect/Method;", - "Ljava/lang/invoke/CallSite;", - "Ljava/lang/invoke/MethodHandleImpl;", - "Ljava/lang/invoke/MethodHandles$Lookup;", - "Ljava/lang/invoke/MethodType;", - "Ljava/lang/invoke/VarHandle;", - "Ljava/lang/invoke/FieldVarHandle;", - "Ljava/lang/invoke/ArrayElementVarHandle;", - "Ljava/lang/invoke/ByteArrayViewVarHandle;", - "Ljava/lang/invoke/ByteBufferViewVarHandle;", - "Ljava/lang/ClassLoader;", - "Ljava/lang/Throwable;", - "Ljava/lang/ClassNotFoundException;", - "Ljava/lang/StackTraceElement;", - "Ldalvik/system/EmulatedStackFrame;", - "Z", - "B", - "C", - "D", - "F", - "I", - "J", - "S", - "V", - "[Z", - "[B", - "[C", - "[D", - "[F", - "[I", - "[J", - "[S", - "[Ljava/lang/StackTraceElement;", - "Ldalvik/system/ClassExt;", - }; - static_assert(arraysize(class_roots_descriptors) == size_t(kClassRootsMax), - "Mismatch between class descriptors and class-root enum"); - - const char* descriptor = class_roots_descriptors[class_root]; - CHECK(descriptor != nullptr); - return descriptor; + DCHECK_LT(static_cast(class_root), static_cast(ClassRoot::kMax)); + int32_t index = static_cast(class_root); + DCHECK(class_roots->Get(index) == nullptr); + class_roots->Set(index, klass); } jobject ClassLinker::CreateWellKnownClassLoader(Thread* self, @@ -8864,17 +8765,26 @@ void ClassLinker::VisitClassLoaders(ClassLoaderVisitor* visitor) const { } } +void ClassLinker::VisitAllocators(AllocatorVisitor* visitor) const { + for (const ClassLoaderData& data : class_loaders_) { + LinearAlloc* alloc = data.allocator; + if (alloc != nullptr && !visitor->Visit(alloc)) { + break; + } + } +} + void ClassLinker::InsertDexFileInToClassLoader(ObjPtr dex_file, ObjPtr class_loader) { DCHECK(dex_file != nullptr); Thread* const self = Thread::Current(); WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); - ClassTable* const table = ClassTableForClassLoader(class_loader.Ptr()); + ClassTable* const table = ClassTableForClassLoader(class_loader); DCHECK(table != nullptr); if (table->InsertStrongRoot(dex_file) && class_loader != nullptr) { // It was not already inserted, perform the write barrier to let the GC know the class loader's // class table was modified. - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader); + WriteBarrier::ForEveryFieldWrite(class_loader); } } @@ -9016,19 +8926,19 @@ class ClassLinker::FindVirtualMethodHolderVisitor : public ClassVisitor { const PointerSize pointer_size_; }; -mirror::Class* ClassLinker::GetHoldingClassOfCopiedMethod(ArtMethod* method) { +ObjPtr ClassLinker::GetHoldingClassOfCopiedMethod(ArtMethod* method) { ScopedTrace trace(__FUNCTION__); // Since this function is slow, have a trace to notify people. CHECK(method->IsCopied()); FindVirtualMethodHolderVisitor visitor(method, image_pointer_size_); VisitClasses(&visitor); - return visitor.holder_.Ptr(); + return visitor.holder_; } -mirror::IfTable* ClassLinker::AllocIfTable(Thread* self, size_t ifcount) { - return down_cast( +ObjPtr ClassLinker::AllocIfTable(Thread* self, size_t ifcount) { + return ObjPtr::DownCast(ObjPtr>( mirror::IfTable::Alloc(self, - GetClassRoot(kObjectArrayClass), - ifcount * mirror::IfTable::kMax)); + GetClassRoot>(this), + ifcount * mirror::IfTable::kMax))); } // Instantiate ResolveMethod. diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 94c3b39ca8621d0f9f9f0ac556c7e5bf72783e0e..e4d9c96696ee6374e090814d69d8348afd15bf16 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -27,6 +27,7 @@ #include "base/enums.h" #include "base/macros.h" #include "base/mutex.h" +#include "dex/class_accessor.h" #include "dex/dex_cache_resolved_classes.h" #include "dex/dex_file.h" #include "dex/dex_file_types.h" @@ -67,7 +68,10 @@ using MethodDexCachePair = NativeDexCachePair; using MethodDexCacheType = std::atomic; } // namespace mirror +class ArtField; +class ArtMethod; class ClassHierarchyAnalysis; +enum class ClassRoot : uint32_t; class ClassTable; template class Handle; class ImtConflictTable; @@ -97,61 +101,16 @@ class ClassLoaderVisitor { REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) = 0; }; -class ClassLinker { +class AllocatorVisitor { public: - // Well known mirror::Class roots accessed via GetClassRoot. - enum ClassRoot { - kJavaLangClass, - kJavaLangObject, - kClassArrayClass, - kObjectArrayClass, - kJavaLangString, - kJavaLangDexCache, - kJavaLangRefReference, - kJavaLangReflectConstructor, - kJavaLangReflectField, - kJavaLangReflectMethod, - kJavaLangReflectProxy, - kJavaLangStringArrayClass, - kJavaLangReflectConstructorArrayClass, - kJavaLangReflectFieldArrayClass, - kJavaLangReflectMethodArrayClass, - kJavaLangInvokeCallSite, - kJavaLangInvokeMethodHandleImpl, - kJavaLangInvokeMethodHandlesLookup, - kJavaLangInvokeMethodType, - kJavaLangInvokeVarHandle, - kJavaLangInvokeFieldVarHandle, - kJavaLangInvokeArrayElementVarHandle, - kJavaLangInvokeByteArrayViewVarHandle, - kJavaLangInvokeByteBufferViewVarHandle, - kJavaLangClassLoader, - kJavaLangThrowable, - kJavaLangClassNotFoundException, - kJavaLangStackTraceElement, - kDalvikSystemEmulatedStackFrame, - kPrimitiveBoolean, - kPrimitiveByte, - kPrimitiveChar, - kPrimitiveDouble, - kPrimitiveFloat, - kPrimitiveInt, - kPrimitiveLong, - kPrimitiveShort, - kPrimitiveVoid, - kBooleanArrayClass, - kByteArrayClass, - kCharArrayClass, - kDoubleArrayClass, - kFloatArrayClass, - kIntArrayClass, - kLongArrayClass, - kShortArrayClass, - kJavaLangStackTraceElementArrayClass, - kDalvikSystemClassExt, - kClassRootsMax, - }; + virtual ~AllocatorVisitor() {} + // Return true to continue visiting. + virtual bool Visit(LinearAlloc* alloc) + REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) = 0; +}; +class ClassLinker { + public: static constexpr bool kAppImageMayContainStrings = false; explicit ClassLinker(InternTable* intern_table); @@ -190,22 +149,22 @@ class ClassLinker { // Finds a class by its descriptor, loading it if necessary. // If class_loader is null, searches boot_class_path_. - mirror::Class* FindClass(Thread* self, - const char* descriptor, - Handle class_loader) + ObjPtr FindClass(Thread* self, + const char* descriptor, + Handle class_loader) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); // Finds a class by its descriptor using the "system" class loader, ie by searching the // boot_class_path_. - mirror::Class* FindSystemClass(Thread* self, const char* descriptor) + ObjPtr FindSystemClass(Thread* self, const char* descriptor) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_) { return FindClass(self, descriptor, ScopedNullHandle()); } // Finds the array class given for the element class. - mirror::Class* FindArrayClass(Thread* self, ObjPtr* element_class) + ObjPtr FindArrayClass(Thread* self, ObjPtr element_class) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); @@ -215,20 +174,20 @@ class ClassLinker { } // Define a new a class based on a ClassDef from a DexFile - mirror::Class* DefineClass(Thread* self, - const char* descriptor, - size_t hash, - Handle class_loader, - const DexFile& dex_file, - const DexFile::ClassDef& dex_class_def) + ObjPtr DefineClass(Thread* self, + const char* descriptor, + size_t hash, + Handle class_loader, + const DexFile& dex_file, + const DexFile::ClassDef& dex_class_def) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); // Finds a class by its descriptor, returning null if it isn't wasn't loaded // by the given 'class_loader'. - mirror::Class* LookupClass(Thread* self, - const char* descriptor, - ObjPtr class_loader) + ObjPtr LookupClass(Thread* self, + const char* descriptor, + ObjPtr class_loader) REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_); @@ -237,7 +196,7 @@ class ClassLinker { REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - mirror::Class* FindPrimitiveClass(char type) REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr FindPrimitiveClass(char type) REQUIRES_SHARED(Locks::mutator_lock_); void DumpForSigQuit(std::ostream& os) REQUIRES(!Locks::classlinker_classes_lock_); @@ -245,6 +204,16 @@ class ClassLinker { REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + // Resolve a String with the given index from the DexFile associated with the given `referrer`, + // storing the result in the DexCache. The `referrer` is used to identify the target DexCache + // to use for resolution. + ObjPtr ResolveString(dex::StringIndex string_idx, + ArtField* referrer) + REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr ResolveString(dex::StringIndex string_idx, + ArtMethod* referrer) + REQUIRES_SHARED(Locks::mutator_lock_); + // Resolve a String with the given index from the DexFile associated with the given DexCache, // storing the result in the DexCache. ObjPtr ResolveString(dex::StringIndex string_idx, @@ -263,10 +232,9 @@ class ClassLinker { ObjPtr ResolveType(dex::TypeIndex type_idx, ObjPtr referrer) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - - // Resolve a type with the given index from the DexFile associated with the given `referrer`, - // storing the result in the DexCache. The `referrer` is used to identify the target DexCache - // and ClassLoader to use for resolution. + ObjPtr ResolveType(dex::TypeIndex type_idx, ArtField* referrer) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); ObjPtr ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); @@ -286,10 +254,8 @@ class ClassLinker { ObjPtr LookupResolvedType(dex::TypeIndex type_idx, ObjPtr referrer) REQUIRES_SHARED(Locks::mutator_lock_); - - // Look up a resolved type with the given index from the DexFile associated with the given - // `referrer`, storing the result in the DexCache. The `referrer` is used to identify the - // target DexCache and ClassLoader to use for lookup. + ObjPtr LookupResolvedType(dex::TypeIndex type_idx, ArtField* referrer) + REQUIRES_SHARED(Locks::mutator_lock_); ObjPtr LookupResolvedType(dex::TypeIndex type_idx, ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_); @@ -408,14 +374,14 @@ class ClassLinker { // Resolve a method type with a given ID from the DexFile associated with a given DexCache // and ClassLoader, storing the result in the DexCache. ObjPtr ResolveMethodType(Thread* self, - uint32_t proto_idx, + dex::ProtoIndex proto_idx, Handle dex_cache, Handle class_loader) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); ObjPtr ResolveMethodType(Thread* self, - uint32_t proto_idx, + dex::ProtoIndex proto_idx, ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_); @@ -439,7 +405,7 @@ class ClassLinker { // Initializes classes that have instances in the image but that have // methods so they could not be initialized by the compiler. - void RunRootClinits() + void RunRootClinits(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); @@ -496,16 +462,16 @@ class ClassLinker { LinearAlloc* allocator, size_t length); - mirror::PointerArray* AllocPointerArray(Thread* self, size_t length) + ObjPtr AllocPointerArray(Thread* self, size_t length) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - mirror::IfTable* AllocIfTable(Thread* self, size_t ifcount) + ObjPtr AllocIfTable(Thread* self, size_t ifcount) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - mirror::ObjectArray* AllocStackTraceElementArray(Thread* self, - size_t length) + ObjPtr> AllocStackTraceElementArray(Thread* self, + size_t length) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); @@ -527,12 +493,12 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); - mirror::Class* CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa, - jstring name, - jobjectArray interfaces, - jobject loader, - jobjectArray methods, - jobjectArray throws) + ObjPtr CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa, + jstring name, + jobjectArray interfaces, + jobject loader, + jobjectArray methods, + jobjectArray throws) REQUIRES_SHARED(Locks::mutator_lock_); std::string GetDescriptorForProxy(ObjPtr proxy_class) REQUIRES_SHARED(Locks::mutator_lock_); @@ -544,10 +510,6 @@ class ClassLinker { pid_t GetClassesLockOwner(); // For SignalCatcher. pid_t GetDexLockOwner(); // For SignalCatcher. - mirror::Class* GetClassRoot(ClassRoot class_root) REQUIRES_SHARED(Locks::mutator_lock_); - - static const char* GetClassRootDescriptor(ClassRoot class_root); - // Is the given entry point quick code to run the resolution stub? bool IsQuickResolutionStub(const void* entry_point) const; @@ -579,7 +541,9 @@ class ClassLinker { // Attempts to insert a class into a class table. Returns null if // the class was inserted, otherwise returns an existing class with // the same descriptor and ClassLoader. - mirror::Class* InsertClass(const char* descriptor, ObjPtr klass, size_t hash) + ObjPtr InsertClass(const char* descriptor, + ObjPtr klass, + size_t hash) REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_); @@ -589,8 +553,10 @@ class ClassLinker { REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - mirror::ObjectArray* GetClassRoots() REQUIRES_SHARED(Locks::mutator_lock_) { - mirror::ObjectArray* class_roots = class_roots_.Read(); + template + ObjPtr> GetClassRoots() REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr> class_roots = + class_roots_.Read(); DCHECK(class_roots != nullptr); return class_roots; } @@ -694,6 +660,11 @@ class ClassLinker { REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + // Visit all of the allocators that belong to classloaders except boot classloader. + // This is used by 616-cha-unloading test to confirm memory reuse. + void VisitAllocators(AllocatorVisitor* visitor) const + REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_); + // Throw the class initialization failure recorded when first trying to initialize the given // class. void ThrowEarlierClassFailure(ObjPtr c, bool wrap_in_no_class_def = false) @@ -701,7 +672,7 @@ class ClassLinker { REQUIRES(!Locks::dex_lock_); // Get the actual holding class for a copied method. Pretty slow, don't call often. - mirror::Class* GetHoldingClassOfCopiedMethod(ArtMethod* method) + ObjPtr GetHoldingClassOfCopiedMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); // Returns null if not found. @@ -805,45 +776,41 @@ class ClassLinker { REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); // For early bootstrapping by Init - mirror::Class* AllocClass(Thread* self, - ObjPtr java_lang_Class, - uint32_t class_size) + ObjPtr AllocClass(Thread* self, + ObjPtr java_lang_Class, + uint32_t class_size) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - // Alloc* convenience functions to avoid needing to pass in mirror::Class* - // values that are known to the ClassLinker such as - // kObjectArrayClass and kJavaLangString etc. - mirror::Class* AllocClass(Thread* self, uint32_t class_size) + // Alloc* convenience functions to avoid needing to pass in ObjPtr + // values that are known to the ClassLinker such as classes corresponding to + // ClassRoot::kObjectArrayClass and ClassRoot::kJavaLangString etc. + ObjPtr AllocClass(Thread* self, uint32_t class_size) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - mirror::DexCache* AllocDexCache(ObjPtr* out_location, - Thread* self, - const DexFile& dex_file) + ObjPtr AllocDexCache(/*out*/ ObjPtr* out_location, + Thread* self, + const DexFile& dex_file) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); // Used for tests and AppendToBootClassPath. - mirror::DexCache* AllocAndInitializeDexCache(Thread* self, - const DexFile& dex_file, - LinearAlloc* linear_alloc) + ObjPtr AllocAndInitializeDexCache(Thread* self, + const DexFile& dex_file, + LinearAlloc* linear_alloc) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_) REQUIRES(!Roles::uninterruptible_); - mirror::Class* CreatePrimitiveClass(Thread* self, Primitive::Type type) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!Roles::uninterruptible_); - mirror::Class* InitializePrimitiveClass(ObjPtr primitive_class, - Primitive::Type type) + ObjPtr CreatePrimitiveClass(Thread* self, Primitive::Type type) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - mirror::Class* CreateArrayClass(Thread* self, - const char* descriptor, - size_t hash, - Handle class_loader) + ObjPtr CreateArrayClass(Thread* self, + const char* descriptor, + size_t hash, + Handle class_loader) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); @@ -869,18 +836,14 @@ class ClassLinker { const DexFile::ClassDef& dex_class_def, Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); - void LoadClassMembers(Thread* self, - const DexFile& dex_file, - const uint8_t* class_data, - Handle klass) - REQUIRES_SHARED(Locks::mutator_lock_); - void LoadField(const ClassDataItemIterator& it, Handle klass, ArtField* dst) + void LoadField(const ClassAccessor::Field& field, Handle klass, ArtField* dst) REQUIRES_SHARED(Locks::mutator_lock_); void LoadMethod(const DexFile& dex_file, - const ClassDataItemIterator& it, - Handle klass, ArtMethod* dst) + const ClassAccessor::Method& method, + Handle klass, + ArtMethod* dst) REQUIRES_SHARED(Locks::mutator_lock_); void FixupStaticTrampolines(ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_); @@ -895,7 +858,7 @@ class ClassLinker { const char* descriptor, size_t hash, Handle class_loader, - ObjPtr* result) + /*out*/ ObjPtr* result) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); @@ -921,12 +884,32 @@ class ClassLinker { REQUIRES(!Locks::dex_lock_); // Implementation of LookupResolvedType() called when the type was not found in the dex cache. + ObjPtr DoLookupResolvedType(dex::TypeIndex type_idx, + ObjPtr referrer) + REQUIRES_SHARED(Locks::mutator_lock_); ObjPtr DoLookupResolvedType(dex::TypeIndex type_idx, ObjPtr dex_cache, ObjPtr class_loader) REQUIRES_SHARED(Locks::mutator_lock_); + // Implementation of ResolveString() called when the string was not found in the dex cache. + ObjPtr DoResolveString(dex::StringIndex string_idx, + ObjPtr dex_cache) + REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr DoResolveString(dex::StringIndex string_idx, + Handle dex_cache) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Implementation of LookupString() called when the string was not found in the dex cache. + ObjPtr DoLookupString(dex::StringIndex string_idx, + ObjPtr dex_cache) + REQUIRES_SHARED(Locks::mutator_lock_); + // Implementation of ResolveType() called when the type was not found in the dex cache. + ObjPtr DoResolveType(dex::TypeIndex type_idx, + ObjPtr referrer) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); ObjPtr DoResolveType(dex::TypeIndex type_idx, Handle dex_cache, Handle class_loader) @@ -935,10 +918,10 @@ class ClassLinker { // Finds a class by its descriptor, returning NULL if it isn't wasn't loaded // by the given 'class_loader'. Uses the provided hash for the descriptor. - mirror::Class* LookupClass(Thread* self, - const char* descriptor, - size_t hash, - ObjPtr class_loader) + ObjPtr LookupClass(Thread* self, + const char* descriptor, + size_t hash, + ObjPtr class_loader) REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_); @@ -1209,7 +1192,9 @@ class ClassLinker { // when resolution has occurred. This happens in mirror::Class::SetStatus. As resolution may // retire a class, the version of the class in the table is returned and this may differ from // the class passed in. - mirror::Class* EnsureResolved(Thread* self, const char* descriptor, ObjPtr klass) + ObjPtr EnsureResolved(Thread* self, + const char* descriptor, + ObjPtr klass) WARN_UNUSED REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); @@ -1287,6 +1272,8 @@ class ClassLinker { ObjPtr class_loader) REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr GetArrayIfTable() REQUIRES_SHARED(Locks::mutator_lock_); + std::vector boot_class_path_; std::vector> boot_dex_files_; @@ -1316,9 +1303,6 @@ class ClassLinker { // Well known mirror::Class roots. GcRoot> class_roots_; - // The interface table used by all arrays. - GcRoot array_iftable_; - // A cache of the last FindArrayClass results. The cache serves to avoid creating array class // descriptors for the sake of performing FindClass. static constexpr size_t kFindArrayCacheSize = 16; @@ -1348,7 +1332,6 @@ class ClassLinker { friend class ImageDumper; // for DexLock friend struct linker::CompilationHelper; // For Compile in ImageTest. friend class linker::ImageWriter; // for GetClassRoots - friend class linker::OatWriter; // for boot image string/class table slot address lookup. friend class JniCompilerTest; // for GetRuntimeQuickGenericJniStub friend class JniInternalTest; // for GetRuntimeQuickGenericJniStub friend class VMClassLoader; // for LookupClass and FindClassInBaseDexClassLoader. diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 8cd0604ac31b7676150451749b7adcda6a0c90c8..e40f1dbcdf8e0261ce3851f8d89dadb9eb353bc9 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -25,6 +25,7 @@ #include "art_method-inl.h" #include "base/enums.h" #include "class_linker-inl.h" +#include "class_root.h" #include "common_runtime_test.h" #include "dex/dex_file_types.h" #include "dex/standard_dex_file.h" @@ -62,9 +63,10 @@ class ClassLinkerTest : public CommonRuntimeTest { Thread* self = Thread::Current(); EXPECT_TRUE(class_linker_->FindSystemClass(self, descriptor.c_str()) == nullptr); EXPECT_TRUE(self->IsExceptionPending()); - mirror::Object* exception = self->GetException(); + StackHandleScope<1> hs(self); + Handle exception = hs.NewHandle(self->GetException()); self->ClearException(); - mirror::Class* exception_class = + ObjPtr exception_class = class_linker_->FindSystemClass(self, "Ljava/lang/NoClassDefFoundError;"); EXPECT_TRUE(exception->InstanceOf(exception_class)); } @@ -75,7 +77,7 @@ class ClassLinkerTest : public CommonRuntimeTest { AssertPrimitiveClass(descriptor, class_linker_->FindSystemClass(self, descriptor.c_str())); } - void AssertPrimitiveClass(const std::string& descriptor, mirror::Class* primitive) + void AssertPrimitiveClass(const std::string& descriptor, ObjPtr primitive) REQUIRES_SHARED(Locks::mutator_lock_) { ASSERT_TRUE(primitive != nullptr); ASSERT_TRUE(primitive->GetClass() != nullptr); @@ -113,13 +115,13 @@ class ClassLinkerTest : public CommonRuntimeTest { EXPECT_EQ(kAccPublic | kAccFinal | kAccAbstract, primitive->GetAccessFlags()); } - void AssertObjectClass(mirror::Class* JavaLangObject) + void AssertObjectClass(ObjPtr JavaLangObject) REQUIRES_SHARED(Locks::mutator_lock_) { ASSERT_TRUE(JavaLangObject != nullptr); ASSERT_TRUE(JavaLangObject->GetClass() != nullptr); ASSERT_EQ(JavaLangObject->GetClass(), JavaLangObject->GetClass()->GetClass()); - EXPECT_EQ(JavaLangObject, JavaLangObject->GetClass()->GetSuperClass()); + EXPECT_OBJ_PTR_EQ(JavaLangObject, JavaLangObject->GetClass()->GetSuperClass()); std::string temp; ASSERT_STREQ(JavaLangObject->GetDescriptor(&temp), "Ljava/lang/Object;"); EXPECT_TRUE(JavaLangObject->GetSuperClass() == nullptr); @@ -172,7 +174,7 @@ class ClassLinkerTest : public CommonRuntimeTest { void AssertArrayClass(const std::string& array_descriptor, const std::string& component_type, - mirror::ClassLoader* class_loader) + ObjPtr class_loader) REQUIRES_SHARED(Locks::mutator_lock_) { Thread* self = Thread::Current(); StackHandleScope<2> hs(self); @@ -181,7 +183,7 @@ class ClassLinkerTest : public CommonRuntimeTest { hs.NewHandle(class_linker_->FindClass(self, array_descriptor.c_str(), loader))); std::string temp; EXPECT_STREQ(component_type.c_str(), array->GetComponentType()->GetDescriptor(&temp)); - EXPECT_EQ(class_loader, array->GetClassLoader()); + EXPECT_OBJ_PTR_EQ(loader.Get(), array->GetClassLoader()); EXPECT_EQ(kAccFinal | kAccAbstract, (array->GetAccessFlags() & (kAccFinal | kAccAbstract))); AssertArrayClass(array_descriptor, array); } @@ -196,7 +198,8 @@ class ClassLinkerTest : public CommonRuntimeTest { ASSERT_STREQ(array_descriptor.c_str(), array->GetDescriptor(&temp)); EXPECT_TRUE(array->GetSuperClass() != nullptr); Thread* self = Thread::Current(); - EXPECT_EQ(class_linker_->FindSystemClass(self, "Ljava/lang/Object;"), array->GetSuperClass()); + EXPECT_OBJ_PTR_EQ(class_linker_->FindSystemClass(self, "Ljava/lang/Object;"), + array->GetSuperClass()); EXPECT_TRUE(array->HasSuperClass()); ASSERT_TRUE(array->GetComponentType() != nullptr); ASSERT_GT(strlen(array->GetComponentType()->GetDescriptor(&temp)), 0U); @@ -230,11 +233,10 @@ class ClassLinkerTest : public CommonRuntimeTest { ObjPtr direct_interface1 = mirror::Class::GetDirectInterface(self, array.Get(), 1); EXPECT_STREQ(direct_interface1->GetDescriptor(&temp), "Ljava/io/Serializable;"); - ObjPtr array_ptr = array->GetComponentType(); - EXPECT_OBJ_PTR_EQ(class_linker_->FindArrayClass(self, &array_ptr), array.Get()); + EXPECT_OBJ_PTR_EQ(class_linker_->FindArrayClass(self, array->GetComponentType()), array.Get()); PointerSize pointer_size = class_linker_->GetImagePointerSize(); - mirror::Class* JavaLangObject = + ObjPtr JavaLangObject = class_linker_->FindSystemClass(self, "Ljava/lang/Object;"); ImTable* JavaLangObject_imt = JavaLangObject->GetImt(pointer_size); // IMT of a array class should be shared with the IMT of the java.lag.Object @@ -292,9 +294,9 @@ class ClassLinkerTest : public CommonRuntimeTest { } } EXPECT_EQ(klass->IsInterface(), !klass->HasVTable()); - mirror::IfTable* iftable = klass->GetIfTable(); + ObjPtr iftable = klass->GetIfTable(); for (int i = 0; i < klass->GetIfTableCount(); i++) { - mirror::Class* interface = iftable->GetInterface(i); + ObjPtr interface = iftable->GetInterface(i); ASSERT_TRUE(interface != nullptr); if (klass->IsInterface()) { EXPECT_EQ(0U, iftable->GetMethodArrayCount(i)); @@ -322,13 +324,13 @@ class ClassLinkerTest : public CommonRuntimeTest { for (ArtMethod& method : klass->GetDirectMethods(kRuntimePointerSize)) { AssertMethod(&method); EXPECT_TRUE(method.IsDirect()); - EXPECT_EQ(klass.Get(), method.GetDeclaringClass()); + EXPECT_OBJ_PTR_EQ(klass.Get(), method.GetDeclaringClass()); } for (ArtMethod& method : klass->GetDeclaredVirtualMethods(kRuntimePointerSize)) { AssertMethod(&method); EXPECT_FALSE(method.IsDirect()); - EXPECT_EQ(klass.Get(), method.GetDeclaringClass()); + EXPECT_OBJ_PTR_EQ(klass.Get(), method.GetDeclaringClass()); } for (ArtMethod& method : klass->GetCopiedMethods(kRuntimePointerSize)) { @@ -386,7 +388,7 @@ class ClassLinkerTest : public CommonRuntimeTest { ASSERT_EQ(end_ref_offset.Uint32Value(), current_ref_offset.Uint32Value()); uint32_t total_num_reference_instance_fields = 0; - mirror::Class* k = klass.Get(); + ObjPtr k = klass.Get(); while (k != nullptr) { total_num_reference_instance_fields += k->NumReferenceInstanceFields(); k = k->GetSuperClass(); @@ -400,7 +402,7 @@ class ClassLinkerTest : public CommonRuntimeTest { } } - void AssertDexFileClass(mirror::ClassLoader* class_loader, const std::string& descriptor) + void AssertDexFileClass(ObjPtr class_loader, const std::string& descriptor) REQUIRES_SHARED(Locks::mutator_lock_) { ASSERT_TRUE(descriptor != nullptr); Thread* self = Thread::Current(); @@ -409,8 +411,8 @@ class ClassLinkerTest : public CommonRuntimeTest { hs.NewHandle(class_linker_->FindSystemClass(self, descriptor.c_str()))); ASSERT_TRUE(klass != nullptr); std::string temp; - EXPECT_STREQ(descriptor.c_str(), klass.Get()->GetDescriptor(&temp)); - EXPECT_EQ(class_loader, klass->GetClassLoader()); + EXPECT_STREQ(descriptor.c_str(), klass->GetDescriptor(&temp)); + EXPECT_OBJ_PTR_EQ(class_loader, klass->GetClassLoader()); if (klass->IsPrimitive()) { AssertPrimitiveClass(descriptor, klass.Get()); } else if (klass->IsArrayClass()) { @@ -420,7 +422,7 @@ class ClassLinkerTest : public CommonRuntimeTest { } } - void AssertDexFile(const DexFile& dex, mirror::ClassLoader* class_loader) + void AssertDexFile(const DexFile& dex, ObjPtr class_loader) REQUIRES_SHARED(Locks::mutator_lock_) { // Verify all the classes defined in this file for (size_t i = 0; i < dex.NumClassDefs(); i++) { @@ -469,7 +471,7 @@ struct CheckOffsets { bool Check() REQUIRES_SHARED(Locks::mutator_lock_) { Thread* self = Thread::Current(); - mirror::Class* klass = + ObjPtr klass = Runtime::Current()->GetClassLinker()->FindSystemClass(self, class_descriptor.c_str()); CHECK(klass != nullptr) << class_descriptor; @@ -875,12 +877,13 @@ TEST_F(ClassLinkerTest, FindClassNested) { Handle class_loader( hs.NewHandle(soa.Decode(LoadDex("Nested")))); - mirror::Class* outer = class_linker_->FindClass(soa.Self(), "LNested;", class_loader); + ObjPtr outer = class_linker_->FindClass(soa.Self(), "LNested;", class_loader); ASSERT_TRUE(outer != nullptr); EXPECT_EQ(0U, outer->NumVirtualMethods()); EXPECT_EQ(1U, outer->NumDirectMethods()); - mirror::Class* inner = class_linker_->FindClass(soa.Self(), "LNested$Inner;", class_loader); + ObjPtr inner = + class_linker_->FindClass(soa.Self(), "LNested$Inner;", class_loader); ASSERT_TRUE(inner != nullptr); EXPECT_EQ(0U, inner->NumVirtualMethods()); EXPECT_EQ(1U, inner->NumDirectMethods()); @@ -902,23 +905,24 @@ TEST_F(ClassLinkerTest, FindClass_Primitives) { TEST_F(ClassLinkerTest, FindClass) { ScopedObjectAccess soa(Thread::Current()); - mirror::Class* JavaLangObject = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); - AssertObjectClass(JavaLangObject); + StackHandleScope<2> hs(soa.Self()); + Handle JavaLangObject = hs.NewHandle( + class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")); + AssertObjectClass(JavaLangObject.Get()); - StackHandleScope<1> hs(soa.Self()); Handle class_loader( hs.NewHandle(soa.Decode(LoadDex("MyClass")))); AssertNonExistentClass("LMyClass;"); - mirror::Class* MyClass = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader); + ObjPtr MyClass = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader); ASSERT_TRUE(MyClass != nullptr); ASSERT_TRUE(MyClass->GetClass() != nullptr); ASSERT_EQ(MyClass->GetClass(), MyClass->GetClass()->GetClass()); - EXPECT_EQ(JavaLangObject, MyClass->GetClass()->GetSuperClass()); + EXPECT_OBJ_PTR_EQ(JavaLangObject.Get(), MyClass->GetClass()->GetSuperClass()); std::string temp; ASSERT_STREQ(MyClass->GetDescriptor(&temp), "LMyClass;"); - EXPECT_TRUE(MyClass->GetSuperClass() == JavaLangObject); + EXPECT_OBJ_PTR_EQ(MyClass->GetSuperClass(), JavaLangObject.Get()); EXPECT_TRUE(MyClass->HasSuperClass()); - EXPECT_EQ(class_loader.Get(), MyClass->GetClassLoader()); + EXPECT_OBJ_PTR_EQ(class_loader.Get(), MyClass->GetClassLoader()); EXPECT_EQ(ClassStatus::kResolved, MyClass->GetStatus()); EXPECT_FALSE(MyClass->IsErroneous()); EXPECT_TRUE(MyClass->IsLoaded()); @@ -1057,8 +1061,9 @@ TEST_F(ClassLinkerTest, LibCore) { // start of the object TEST_F(ClassLinkerTest, ValidateObjectArrayElementsOffset) { ScopedObjectAccess soa(Thread::Current()); - mirror::Class* array_class = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); - mirror::ObjectArray* array = + ObjPtr array_class = + class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); + ObjPtr> array = mirror::ObjectArray::Alloc(soa.Self(), array_class, 0); uintptr_t data_offset = reinterpret_cast(array->GetRawData(sizeof(mirror::HeapReference), @@ -1074,27 +1079,27 @@ TEST_F(ClassLinkerTest, ValidatePrimitiveArrayElementsOffset) { ScopedObjectAccess soa(Thread::Current()); StackHandleScope<5> hs(soa.Self()); Handle long_array(hs.NewHandle(mirror::LongArray::Alloc(soa.Self(), 0))); - EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[J"), long_array->GetClass()); + EXPECT_OBJ_PTR_EQ(class_linker_->FindSystemClass(soa.Self(), "[J"), long_array->GetClass()); uintptr_t data_offset = reinterpret_cast(long_array->GetData()); EXPECT_TRUE(IsAligned<8>(data_offset)); // Longs require 8 byte alignment Handle double_array(hs.NewHandle(mirror::DoubleArray::Alloc(soa.Self(), 0))); - EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[D"), double_array->GetClass()); + EXPECT_OBJ_PTR_EQ(class_linker_->FindSystemClass(soa.Self(), "[D"), double_array->GetClass()); data_offset = reinterpret_cast(double_array->GetData()); EXPECT_TRUE(IsAligned<8>(data_offset)); // Doubles require 8 byte alignment Handle int_array(hs.NewHandle(mirror::IntArray::Alloc(soa.Self(), 0))); - EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[I"), int_array->GetClass()); + EXPECT_OBJ_PTR_EQ(class_linker_->FindSystemClass(soa.Self(), "[I"), int_array->GetClass()); data_offset = reinterpret_cast(int_array->GetData()); EXPECT_TRUE(IsAligned<4>(data_offset)); // Ints require 4 byte alignment Handle char_array(hs.NewHandle(mirror::CharArray::Alloc(soa.Self(), 0))); - EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[C"), char_array->GetClass()); + EXPECT_OBJ_PTR_EQ(class_linker_->FindSystemClass(soa.Self(), "[C"), char_array->GetClass()); data_offset = reinterpret_cast(char_array->GetData()); EXPECT_TRUE(IsAligned<2>(data_offset)); // Chars require 2 byte alignment Handle short_array(hs.NewHandle(mirror::ShortArray::Alloc(soa.Self(), 0))); - EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[S"), short_array->GetClass()); + EXPECT_OBJ_PTR_EQ(class_linker_->FindSystemClass(soa.Self(), "[S"), short_array->GetClass()); data_offset = reinterpret_cast(short_array->GetData()); EXPECT_TRUE(IsAligned<2>(data_offset)); // Shorts require 2 byte alignment @@ -1106,7 +1111,7 @@ TEST_F(ClassLinkerTest, ValidateBoxedTypes) { // This lets UnboxPrimitive avoid searching for the field by name at runtime. ScopedObjectAccess soa(Thread::Current()); ScopedNullHandle class_loader; - mirror::Class* c; + ObjPtr c; c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Boolean;", class_loader); EXPECT_STREQ("value", c->GetIFieldsPtr()->At(0).GetName()); c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Byte;", class_loader); @@ -1127,16 +1132,18 @@ TEST_F(ClassLinkerTest, ValidateBoxedTypes) { TEST_F(ClassLinkerTest, TwoClassLoadersOneClass) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); + StackHandleScope<3> hs(soa.Self()); Handle class_loader_1( hs.NewHandle(soa.Decode(LoadDex("MyClass")))); Handle class_loader_2( hs.NewHandle(soa.Decode(LoadDex("MyClass")))); - mirror::Class* MyClass_1 = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader_1); - mirror::Class* MyClass_2 = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader_2); + Handle MyClass_1 = hs.NewHandle( + class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader_1)); + ObjPtr MyClass_2 = + class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader_2); EXPECT_TRUE(MyClass_1 != nullptr); EXPECT_TRUE(MyClass_2 != nullptr); - EXPECT_NE(MyClass_1, MyClass_2); + EXPECT_OBJ_PTR_NE(MyClass_1.Get(), MyClass_2); } TEST_F(ClassLinkerTest, StaticFields) { @@ -1200,7 +1207,7 @@ TEST_F(ClassLinkerTest, StaticFields) { soa.Self(), statics.Get(), "s8", "Ljava/lang/String;"); EXPECT_EQ(s8->GetTypeAsPrimitiveType(), Primitive::kPrimNot); EXPECT_TRUE(s8->GetObject(statics.Get())->AsString()->Equals("android")); - mirror::String* str_value = mirror::String::AllocFromModifiedUtf8(soa.Self(), "robot"); + ObjPtr str_value = mirror::String::AllocFromModifiedUtf8(soa.Self(), "robot"); s8->SetObject(s8->GetDeclaringClass(), str_value); // TODO: Remove EXPECT_FALSE when GCC can handle EXPECT_EQ @@ -1300,7 +1307,8 @@ TEST_F(ClassLinkerTest, ResolveVerifyAndClinit) { StackHandleScope<1> hs(soa.Self()); Handle class_loader( hs.NewHandle(soa.Decode(jclass_loader))); - mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", class_loader); + ObjPtr klass = + class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", class_loader); ArtMethod* clinit = klass->FindClassInitializer(kRuntimePointerSize); ArtMethod* getS0 = klass->FindClassMethod("getS0", "()Ljava/lang/Object;", kRuntimePointerSize); @@ -1345,7 +1353,7 @@ TEST_F(ClassLinkerTest, ErroneousClass) { TEST_F(ClassLinkerTest, FinalizableBit) { ScopedObjectAccess soa(Thread::Current()); - mirror::Class* c; + ObjPtr c; // Object has a finalize method, but we know it's empty. c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); @@ -1380,18 +1388,17 @@ TEST_F(ClassLinkerTest, FinalizableBit) { TEST_F(ClassLinkerTest, ClassRootDescriptors) { ScopedObjectAccess soa(Thread::Current()); std::string temp; - for (int i = 0; i < ClassLinker::kClassRootsMax; i++) { - mirror::Class* klass = class_linker_->GetClassRoot(ClassLinker::ClassRoot(i)); + for (size_t i = 0; i < static_cast(ClassRoot::kMax); i++) { + ObjPtr klass = GetClassRoot(ClassRoot(i), class_linker_); EXPECT_GT(strlen(klass->GetDescriptor(&temp)), 0U); - EXPECT_STREQ(klass->GetDescriptor(&temp), - class_linker_->GetClassRootDescriptor(ClassLinker::ClassRoot(i))) << " i = " << i; + EXPECT_STREQ(klass->GetDescriptor(&temp), GetClassRootDescriptor(ClassRoot(i))) << " i = " << i; } } TEST_F(ClassLinkerTest, ValidatePredefinedClassSizes) { ScopedObjectAccess soa(Thread::Current()); ScopedNullHandle class_loader; - mirror::Class* c; + ObjPtr c; c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Class;", class_loader); ASSERT_TRUE(c != nullptr); @@ -1418,7 +1425,7 @@ static void CheckMethod(ArtMethod* method, bool verified) } } -static void CheckVerificationAttempted(mirror::Class* c, bool preverified) +static void CheckVerificationAttempted(ObjPtr c, bool preverified) REQUIRES_SHARED(Locks::mutator_lock_) { EXPECT_EQ((c->GetAccessFlags() & kAccVerificationAttempted) != 0U, preverified) << "Class " << mirror::Class::PrettyClass(c) << " not as expected"; @@ -1430,7 +1437,8 @@ static void CheckVerificationAttempted(mirror::Class* c, bool preverified) TEST_F(ClassLinkerTest, Preverified_InitializedBoot) { ScopedObjectAccess soa(Thread::Current()); - mirror::Class* JavaLangObject = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); + ObjPtr JavaLangObject = + class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); ASSERT_TRUE(JavaLangObject != nullptr); EXPECT_TRUE(JavaLangObject->IsInitialized()) << "Not testing already initialized class from the " "core"; @@ -1479,13 +1487,13 @@ TEST_F(ClassLinkerTest, IsBootStrapClassLoaded) { Handle jlo_class( hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"))); ASSERT_TRUE(jlo_class != nullptr); - EXPECT_TRUE(jlo_class.Get()->IsBootStrapClassLoaded()); + EXPECT_TRUE(jlo_class->IsBootStrapClassLoaded()); // Statics is not a bootstrap class. Handle statics( hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStatics;", class_loader))); ASSERT_TRUE(statics != nullptr); - EXPECT_FALSE(statics.Get()->IsBootStrapClassLoaded()); + EXPECT_FALSE(statics->IsBootStrapClassLoaded()); } // Regression test for b/26799552. @@ -1563,13 +1571,13 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) { Handle string_class(hs.NewHandle(class_linker_->FindClass(soa.Self(), "Ljava/lang/String;", class_loader))); - ASSERT_EQ(string_class.Get(), method1_type->GetRType()); - ASSERT_EQ(string_class.Get(), method1_type->GetPTypes()->Get(0)); + ASSERT_OBJ_PTR_EQ(string_class.Get(), method1_type->GetRType()); + ASSERT_OBJ_PTR_EQ(string_class.Get(), method1_type->GetPTypes()->Get(0)); // Resolve the method type again and assert that we get back the same value. Handle method1_type2 = hs.NewHandle( class_linker_->ResolveMethodType(soa.Self(), method1_id.proto_idx_, dex_cache, class_loader)); - ASSERT_EQ(method1_type.Get(), method1_type2.Get()); + ASSERT_OBJ_PTR_EQ(method1_type.Get(), method1_type2.Get()); // Resolve the MethodType associated with a different method signature // and assert it's different. @@ -1582,7 +1590,7 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) { const DexFile::MethodId& method2_id = dex_file.GetMethodId(method2->GetDexMethodIndex()); Handle method2_type = hs.NewHandle( class_linker_->ResolveMethodType(soa.Self(), method2_id.proto_idx_, dex_cache, class_loader)); - ASSERT_TRUE(method1_type.Get() != method2_type.Get()); + ASSERT_OBJ_PTR_NE(method1_type.Get(), method2_type.Get()); } // Verify that ClassLinker's CreateWellknownClassLoader works as expected @@ -1599,18 +1607,18 @@ TEST_F(ClassLinkerTest, CreateWellKnownClassLoader) { TEST_F(ClassLinkerTest, PrettyClass) { ScopedObjectAccess soa(Thread::Current()); EXPECT_EQ("null", mirror::Class::PrettyClass(nullptr)); - mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); + ObjPtr c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); ASSERT_TRUE(c != nullptr); - mirror::Object* o = mirror::ObjectArray::Alloc(soa.Self(), c, 0); + ObjPtr o = mirror::ObjectArray::Alloc(soa.Self(), c, 0); EXPECT_EQ("java.lang.Class", mirror::Class::PrettyClass(o->GetClass())); } TEST_F(ClassLinkerTest, PrettyClassAndClassLoader) { ScopedObjectAccess soa(Thread::Current()); EXPECT_EQ("null", mirror::Class::PrettyClassAndClassLoader(nullptr)); - mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); + ObjPtr c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); ASSERT_TRUE(c != nullptr); - mirror::Object* o = mirror::ObjectArray::Alloc(soa.Self(), c, 0); + ObjPtr o = mirror::ObjectArray::Alloc(soa.Self(), c, 0); EXPECT_EQ("java.lang.Class", mirror::Class::PrettyClassAndClassLoader(o->GetClass())); } @@ -1619,8 +1627,8 @@ TEST_F(ClassLinkerTest, PrettyField) { ScopedObjectAccess soa(Thread::Current()); EXPECT_EQ("null", ArtField::PrettyField(nullptr)); - mirror::Class* java_lang_String = class_linker_->FindSystemClass(soa.Self(), - "Ljava/lang/String;"); + ObjPtr java_lang_String = class_linker_->FindSystemClass(soa.Self(), + "Ljava/lang/String;"); ArtField* f; f = java_lang_String->FindDeclaredInstanceField("count", "I"); @@ -1630,7 +1638,7 @@ TEST_F(ClassLinkerTest, PrettyField) { TEST_F(ClassLinkerTest, JniShortName_JniLongName) { ScopedObjectAccess soa(Thread::Current()); - mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/String;"); + ObjPtr c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/String;"); ASSERT_TRUE(c != nullptr); ArtMethod* m; @@ -1682,7 +1690,7 @@ class ClassLinkerClassLoaderTest : public ClassLinkerTest { ASSERT_TRUE(klass != nullptr) << descriptor; Handle expected_class_loader( hs.NewHandle(soa.Decode(expected_class_loader_obj))); - ASSERT_EQ(klass->GetClassLoader(), expected_class_loader.Get()); + ASSERT_OBJ_PTR_EQ(klass->GetClassLoader(), expected_class_loader.Get()); } } }; diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index 4afc44cb9198f8d106b0024db8711815d71e07e4..2bd541118b07275e05a7a697b922fa0dcd93d529 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -25,7 +25,7 @@ #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "handle_scope-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "oat_file_assistant.h" #include "obj_ptr-inl.h" #include "runtime.h" @@ -672,9 +672,10 @@ static bool IsAbsoluteLocation(const std::string& location) { return !location.empty() && location[0] == '/'; } -bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec, - bool verify_names, - bool verify_checksums) const { +ClassLoaderContext::VerificationResult ClassLoaderContext::VerifyClassLoaderContextMatch( + const std::string& context_spec, + bool verify_names, + bool verify_checksums) const { if (verify_names || verify_checksums) { DCHECK(dex_files_open_attempted_); DCHECK(dex_files_open_result_); @@ -683,15 +684,21 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex ClassLoaderContext expected_context; if (!expected_context.Parse(context_spec, verify_checksums)) { LOG(WARNING) << "Invalid class loader context: " << context_spec; - return false; + return VerificationResult::kMismatch; } // Special shared library contexts always match. They essentially instruct the runtime // to ignore the class path check because the oat file is known to be loaded in different // contexts. OatFileManager will further verify if the oat file can be loaded based on the // collision check. - if (special_shared_library_ || expected_context.special_shared_library_) { - return true; + if (expected_context.special_shared_library_) { + // Special case where we are the only entry in the class path. + if (class_loader_chain_.size() == 1 && class_loader_chain_[0].classpath.size() == 0) { + return VerificationResult::kVerifies; + } + return VerificationResult::kForcedToSkipChecks; + } else if (special_shared_library_) { + return VerificationResult::kForcedToSkipChecks; } if (expected_context.class_loader_chain_.size() != class_loader_chain_.size()) { @@ -699,7 +706,7 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << expected_context.class_loader_chain_.size() << ", actual=" << class_loader_chain_.size() << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } for (size_t i = 0; i < class_loader_chain_.size(); i++) { @@ -710,14 +717,14 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << ". expected=" << GetClassLoaderTypeName(expected_info.type) << ", found=" << GetClassLoaderTypeName(info.type) << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } if (info.classpath.size() != expected_info.classpath.size()) { LOG(WARNING) << "ClassLoaderContext classpath size mismatch for position " << i << ". expected=" << expected_info.classpath.size() << ", found=" << info.classpath.size() << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } if (verify_checksums) { @@ -772,7 +779,7 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << ". expected=" << expected_info.classpath[k] << ", found=" << info.classpath[k] << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } // Compare the checksums. @@ -781,11 +788,11 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << ". expected=" << expected_info.checksums[k] << ", found=" << info.checksums[k] << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } } } - return true; + return VerificationResult::kVerifies; } jclass ClassLoaderContext::GetClassLoaderClass(ClassLoaderType type) { diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h index 1c83007f417d6c185626661aacfffc7622190236..a4268aa09a1fa70f5f1aeb2e8db4e1c93a6cf6b3 100644 --- a/runtime/class_loader_context.h +++ b/runtime/class_loader_context.h @@ -22,8 +22,10 @@ #include "arch/instruction_set.h" #include "base/dchecked_vector.h" +#include "dex/dex_file.h" #include "handle_scope.h" #include "mirror/class_loader.h" +#include "oat_file.h" #include "scoped_thread_state_change.h" namespace art { @@ -34,6 +36,18 @@ class OatFile; // Utility class which holds the class loader context used during compilation/verification. class ClassLoaderContext { public: + enum class VerificationResult { + kVerifies, + kForcedToSkipChecks, + kMismatch, + }; + + enum ClassLoaderType { + kInvalidClassLoader = 0, + kPathClassLoader = 1, + kDelegateLastClassLoader = 2 + }; + ~ClassLoaderContext(); // Opens requested class path files and appends them to ClassLoaderInfo::opened_dex_files. @@ -109,7 +123,7 @@ class ClassLoaderContext { // This should be called after OpenDexFiles(). // Names are only verified if verify_names is true. // Checksums are only verified if verify_checksums is true. - bool VerifyClassLoaderContextMatch(const std::string& context_spec, + VerificationResult VerifyClassLoaderContextMatch(const std::string& context_spec, bool verify_names = true, bool verify_checksums = true) const; @@ -141,12 +155,6 @@ class ClassLoaderContext { static std::unique_ptr Default(); private: - enum ClassLoaderType { - kInvalidClassLoader = 0, - kPathClassLoader = 1, - kDelegateLastClassLoader = 2 - }; - struct ClassLoaderInfo { // The type of this class loader. ClassLoaderType type; diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc index 4689ae4c3f884abd011141e1d0130a574623f8e9..5e3f48c100790007c605921a7fb8a596fc73d6e0 100644 --- a/runtime/class_loader_context_test.cc +++ b/runtime/class_loader_context_test.cc @@ -608,6 +608,17 @@ TEST_F(ClassLoaderContextTest, CreateContextForClassLoader) { VerifyClassLoaderPCLFromTestDex(context.get(), 3, "ForClassLoaderA"); } + +TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextFirstElement) { + std::string context_spec = "PCL[]"; + std::unique_ptr context = ParseContextWithChecksums(context_spec); + ASSERT_TRUE(context != nullptr); + PretendContextOpenedDexFiles(context.get()); + // Ensure that the special shared library marks as verified for the first thing in the class path. + ASSERT_EQ(context->VerifyClassLoaderContextMatch(OatFile::kSpecialSharedLibrary), + ClassLoaderContext::VerificationResult::kVerifies); +} + TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) { std::string context_spec = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890]"; std::unique_ptr context = ParseContextWithChecksums(context_spec); @@ -619,28 +630,36 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) { VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex"); VerifyClassLoaderDLC(context.get(), 1, "c.dex"); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_spec)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec), + ClassLoaderContext::VerificationResult::kVerifies); std::string wrong_class_loader_type = "PCL[a.dex*123:b.dex*456];PCL[c.dex*890]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_type)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_type), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_class_loader_order = "DLC[c.dex*890];PCL[a.dex*123:b.dex*456]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_order)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_order), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_classpath_order = "PCL[b.dex*456:a.dex*123];DLC[c.dex*890]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_classpath_order)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_classpath_order), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_checksum = "PCL[a.dex*999:b.dex*456];DLC[c.dex*890]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_checksum)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_checksum), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_extra_class_loader = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890];PCL[d.dex*321]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_extra_classpath = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890:d.dex*321]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_classpath)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_classpath), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_spec = "PCL[a.dex*999:b.dex*456];DLC["; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_spec)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_spec), + ClassLoaderContext::VerificationResult::kMismatch); } TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { @@ -652,7 +671,8 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { std::unique_ptr context = CreateContextForClassLoader(class_loader_d); std::string context_with_no_base_dir = context->EncodeContextForOatFile(""); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_no_base_dir)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_no_base_dir), + ClassLoaderContext::VerificationResult::kVerifies); std::string dex_location = GetTestDexFileName("ForClassLoaderA"); size_t pos = dex_location.rfind('/'); @@ -661,7 +681,8 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { std::string context_with_base_dir = context->EncodeContextForOatFile(parent); ASSERT_NE(context_with_base_dir, context_with_no_base_dir); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_base_dir)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_base_dir), + ClassLoaderContext::VerificationResult::kVerifies); } TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultidex) { @@ -669,7 +690,8 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultide std::unique_ptr context = CreateContextForClassLoader(class_loader); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile(""))); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile("")), + ClassLoaderContext::VerificationResult::kVerifies); } } // namespace art diff --git a/runtime/class_loader_utils.h b/runtime/class_loader_utils.h index 1439f116364ad80de16e54af36c68eba91df9188..78ad568d2537cf555088c1e70ee810fcf464d057 100644 --- a/runtime/class_loader_utils.h +++ b/runtime/class_loader_utils.h @@ -20,7 +20,7 @@ #include "art_field-inl.h" #include "base/mutex.h" #include "handle_scope.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "native/dalvik_system_DexFile.h" #include "scoped_thread_state_change-inl.h" @@ -147,8 +147,14 @@ inline void VisitClassLoaderDexFiles(ScopedObjectAccessAlreadyRunnable& soa, Handle class_loader, Visitor fn) REQUIRES_SHARED(Locks::mutator_lock_) { - auto helper = [&fn](const art::DexFile* dex_file, void** ATTRIBUTE_UNUSED) + auto helper = [&fn](const art::DexFile* dex_file, void** ret) REQUIRES_SHARED(Locks::mutator_lock_) { +#ifdef __clang_analyzer__ + *ret = nullptr; +#else + UNUSED(ret); +#endif + return fn(dex_file); }; VisitClassLoaderDexFiles(soa, diff --git a/runtime/class_root.cc b/runtime/class_root.cc new file mode 100644 index 0000000000000000000000000000000000000000..08820b0c6181eb1cc80c4113b4d61fde1287d853 --- /dev/null +++ b/runtime/class_root.cc @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "class_root.h" + +namespace art { + +const char* GetClassRootDescriptor(ClassRoot class_root) { + static const char* class_roots_descriptors[] = { +#define CLASS_ROOT_DESCRIPTOR(name, descriptor, mirror_type) descriptor, + CLASS_ROOT_LIST(CLASS_ROOT_DESCRIPTOR) +#undef CLASS_ROOT_DESCRIPTOR + }; + static_assert(arraysize(class_roots_descriptors) == static_cast(ClassRoot::kMax), + "Mismatch between class descriptors and class-root enum"); + + DCHECK_LT(static_cast(class_root), static_cast(ClassRoot::kMax)); + const char* descriptor = class_roots_descriptors[static_cast(class_root)]; + CHECK(descriptor != nullptr); + return descriptor; +} + +} // namespace art diff --git a/runtime/class_root.h b/runtime/class_root.h new file mode 100644 index 0000000000000000000000000000000000000000..4aa9801ab48955396597f0f06382ff45940bbe68 --- /dev/null +++ b/runtime/class_root.h @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_CLASS_ROOT_H_ +#define ART_RUNTIME_CLASS_ROOT_H_ + +#include "class_linker.h" +#include "mirror/class.h" +#include "mirror/object_array-inl.h" +#include "obj_ptr-inl.h" +#include "runtime.h" + +namespace art { + +namespace mirror { +class ArrayElementVarHandle; +class ByteArrayViewVarHandle; +class ByteBufferViewVarHandle; +class CallSite; +class ClassExt; +class ClassLoader; +class Constructor; +class DexCache; +class EmulatedStackFrame; +class Field; +class FieldVarHandle; +class Method; +class MethodHandleImpl; +class MethodHandlesLookup; +class MethodType; +class Object; +class Proxy; +template class PrimitiveArray; +class Reference; +class StackTraceElement; +class String; +class Throwable; +class VarHandle; +} // namespace mirror + +#define CLASS_ROOT_LIST(M) \ + M(kJavaLangClass, "Ljava/lang/Class;", mirror::Class) \ + M(kJavaLangObject, "Ljava/lang/Object;", mirror::Object) \ + M(kClassArrayClass, "[Ljava/lang/Class;", mirror::ObjectArray) \ + M(kObjectArrayClass, "[Ljava/lang/Object;", mirror::ObjectArray) \ + M(kJavaLangString, "Ljava/lang/String;", mirror::String) \ + M(kJavaLangDexCache, "Ljava/lang/DexCache;", mirror::DexCache) \ + M(kJavaLangRefReference, "Ljava/lang/ref/Reference;", mirror::Reference) \ + M(kJavaLangReflectConstructor, "Ljava/lang/reflect/Constructor;", mirror::Constructor) \ + M(kJavaLangReflectField, "Ljava/lang/reflect/Field;", mirror::Field) \ + M(kJavaLangReflectMethod, "Ljava/lang/reflect/Method;", mirror::Method) \ + M(kJavaLangReflectProxy, "Ljava/lang/reflect/Proxy;", mirror::Proxy) \ + M(kJavaLangStringArrayClass, "[Ljava/lang/String;", mirror::ObjectArray) \ + M(kJavaLangReflectConstructorArrayClass, "[Ljava/lang/reflect/Constructor;", mirror::ObjectArray) \ + M(kJavaLangReflectFieldArrayClass, "[Ljava/lang/reflect/Field;", mirror::ObjectArray) \ + M(kJavaLangReflectMethodArrayClass, "[Ljava/lang/reflect/Method;", mirror::ObjectArray) \ + M(kJavaLangInvokeCallSite, "Ljava/lang/invoke/CallSite;", mirror::CallSite) \ + M(kJavaLangInvokeMethodHandle, "Ljava/lang/invoke/MethodHandle;", mirror::MethodHandle) \ + M(kJavaLangInvokeMethodHandleImpl, "Ljava/lang/invoke/MethodHandleImpl;", mirror::MethodHandleImpl) \ + M(kJavaLangInvokeMethodHandlesLookup, "Ljava/lang/invoke/MethodHandles$Lookup;", mirror::MethodHandlesLookup) \ + M(kJavaLangInvokeMethodType, "Ljava/lang/invoke/MethodType;", mirror::MethodType) \ + M(kJavaLangInvokeVarHandle, "Ljava/lang/invoke/VarHandle;", mirror::VarHandle) \ + M(kJavaLangInvokeFieldVarHandle, "Ljava/lang/invoke/FieldVarHandle;", mirror::FieldVarHandle) \ + M(kJavaLangInvokeArrayElementVarHandle, "Ljava/lang/invoke/ArrayElementVarHandle;", mirror::ArrayElementVarHandle) \ + M(kJavaLangInvokeByteArrayViewVarHandle, "Ljava/lang/invoke/ByteArrayViewVarHandle;", mirror::ByteArrayViewVarHandle) \ + M(kJavaLangInvokeByteBufferViewVarHandle, "Ljava/lang/invoke/ByteBufferViewVarHandle;", mirror::ByteBufferViewVarHandle) \ + M(kJavaLangClassLoader, "Ljava/lang/ClassLoader;", mirror::ClassLoader) \ + M(kJavaLangThrowable, "Ljava/lang/Throwable;", mirror::Throwable) \ + M(kJavaLangClassNotFoundException, "Ljava/lang/ClassNotFoundException;", detail::NoMirrorType) \ + M(kJavaLangStackTraceElement, "Ljava/lang/StackTraceElement;", mirror::StackTraceElement) \ + M(kDalvikSystemEmulatedStackFrame, "Ldalvik/system/EmulatedStackFrame;", mirror::EmulatedStackFrame) \ + M(kPrimitiveBoolean, "Z", detail::NoMirrorType) \ + M(kPrimitiveByte, "B", detail::NoMirrorType) \ + M(kPrimitiveChar, "C", detail::NoMirrorType) \ + M(kPrimitiveDouble, "D", detail::NoMirrorType) \ + M(kPrimitiveFloat, "F", detail::NoMirrorType) \ + M(kPrimitiveInt, "I", detail::NoMirrorType) \ + M(kPrimitiveLong, "J", detail::NoMirrorType) \ + M(kPrimitiveShort, "S", detail::NoMirrorType) \ + M(kPrimitiveVoid, "V", detail::NoMirrorType) \ + M(kBooleanArrayClass, "[Z", mirror::PrimitiveArray) \ + M(kByteArrayClass, "[B", mirror::PrimitiveArray) \ + M(kCharArrayClass, "[C", mirror::PrimitiveArray) \ + M(kDoubleArrayClass, "[D", mirror::PrimitiveArray) \ + M(kFloatArrayClass, "[F", mirror::PrimitiveArray) \ + M(kIntArrayClass, "[I", mirror::PrimitiveArray) \ + M(kLongArrayClass, "[J", mirror::PrimitiveArray) \ + M(kShortArrayClass, "[S", mirror::PrimitiveArray) \ + M(kJavaLangStackTraceElementArrayClass, "[Ljava/lang/StackTraceElement;", mirror::ObjectArray) \ + M(kDalvikSystemClassExt, "Ldalvik/system/ClassExt;", mirror::ClassExt) + +// Well known mirror::Class roots accessed via ClassLinker::GetClassRoots(). +enum class ClassRoot : uint32_t { +#define CLASS_ROOT_ENUMERATOR(name, descriptor, mirror_type) name, + CLASS_ROOT_LIST(CLASS_ROOT_ENUMERATOR) +#undef CLASS_ROOT_ENUMERATOR + kMax, +}; + +const char* GetClassRootDescriptor(ClassRoot class_root); + +template +inline ObjPtr GetClassRoot( + ClassRoot class_root, + ObjPtr> class_roots) REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(class_roots != nullptr); + if (kReadBarrierOption == kWithReadBarrier) { + // With read barrier all references must point to the to-space. + // Without read barrier, this check could fail. + DCHECK_EQ(class_roots, Runtime::Current()->GetClassLinker()->GetClassRoots()); + } + DCHECK_LT(static_cast(class_root), static_cast(ClassRoot::kMax)); + int32_t index = static_cast(class_root); + ObjPtr klass = + class_roots->GetWithoutChecks(index); + DCHECK(klass != nullptr); + return klass; +} + +template +inline ObjPtr GetClassRoot(ClassRoot class_root, ClassLinker* linker) + REQUIRES_SHARED(Locks::mutator_lock_) { + return GetClassRoot(class_root, linker->GetClassRoots()); +} + +template +inline ObjPtr GetClassRoot(ClassRoot class_root) + REQUIRES_SHARED(Locks::mutator_lock_) { + return GetClassRoot(class_root, Runtime::Current()->GetClassLinker()); +} + +namespace detail { + +class ClassNotFoundExceptionTag; +template struct NoMirrorType; + +template +struct ClassRootSelector; // No definition for unspecialized ClassRoot selector. + +#define SPECIALIZE_CLASS_ROOT_SELECTOR(name, descriptor, mirror_type) \ + template <> \ + struct ClassRootSelector { \ + static constexpr ClassRoot value = ClassRoot::name; \ + }; + +CLASS_ROOT_LIST(SPECIALIZE_CLASS_ROOT_SELECTOR) + +#undef SPECIALIZE_CLASS_ROOT_SELECTOR + +} // namespace detail + +template +inline ObjPtr GetClassRoot(ObjPtr> class_roots) + REQUIRES_SHARED(Locks::mutator_lock_) { + return GetClassRoot(detail::ClassRootSelector::value, class_roots); +} + +template +inline ObjPtr GetClassRoot(ClassLinker* linker) + REQUIRES_SHARED(Locks::mutator_lock_) { + return GetClassRoot(detail::ClassRootSelector::value, linker); +} + +template +inline ObjPtr GetClassRoot() REQUIRES_SHARED(Locks::mutator_lock_) { + return GetClassRoot(detail::ClassRootSelector::value); +} + +} // namespace art + +#endif // ART_RUNTIME_CLASS_ROOT_H_ diff --git a/runtime/class_table-inl.h b/runtime/class_table-inl.h index c59e2e881ddbd9a1f23ddb1d9cb7cc8c9590f28a..6b6fe341e07a7e67c010240fe450bf938ec3b064 100644 --- a/runtime/class_table-inl.h +++ b/runtime/class_table-inl.h @@ -20,6 +20,7 @@ #include "class_table.h" #include "gc_root-inl.h" +#include "mirror/class.h" #include "oat_file.h" namespace art { @@ -88,7 +89,7 @@ bool ClassTable::Visit(const Visitor& visitor) { template inline mirror::Class* ClassTable::TableSlot::Read() const { - const uint32_t before = data_.LoadRelaxed(); + const uint32_t before = data_.load(std::memory_order_relaxed); ObjPtr const before_ptr(ExtractPtr(before)); ObjPtr const after_ptr( GcRoot(before_ptr).Read()); @@ -102,7 +103,7 @@ inline mirror::Class* ClassTable::TableSlot::Read() const { template inline void ClassTable::TableSlot::VisitRoot(const Visitor& visitor) const { - const uint32_t before = data_.LoadRelaxed(); + const uint32_t before = data_.load(std::memory_order_relaxed); ObjPtr before_ptr(ExtractPtr(before)); GcRoot root(before_ptr); visitor.VisitRoot(root.AddressWithoutBarrier()); diff --git a/runtime/class_table.cc b/runtime/class_table.cc index e313ec5dd743a62f507b47c6fa586b70fc1e91fe..a233357249a175b2fe9143021bfd7687de1dd533 100644 --- a/runtime/class_table.cc +++ b/runtime/class_table.cc @@ -37,7 +37,7 @@ bool ClassTable::Contains(ObjPtr klass) { ReaderMutexLock mu(Thread::Current(), lock_); TableSlot slot(klass); for (ClassSet& class_set : classes_) { - auto it = class_set.Find(slot); + auto it = class_set.find(slot); if (it != class_set.end()) { return it->Read() == klass; } @@ -49,7 +49,7 @@ mirror::Class* ClassTable::LookupByDescriptor(ObjPtr klass) { ReaderMutexLock mu(Thread::Current(), lock_); TableSlot slot(klass); for (ClassSet& class_set : classes_) { - auto it = class_set.Find(slot); + auto it = class_set.find(slot); if (it != class_set.end()) { return it->Read(); } @@ -119,14 +119,14 @@ size_t ClassTable::NumReferencedZygoteClasses() const { ReaderMutexLock mu(Thread::Current(), lock_); size_t sum = 0; for (size_t i = 0; i < classes_.size() - 1; ++i) { - sum += classes_[i].Size(); + sum += classes_[i].size(); } return sum; } size_t ClassTable::NumReferencedNonZygoteClasses() const { ReaderMutexLock mu(Thread::Current(), lock_); - return classes_.back().Size(); + return classes_.back().size(); } mirror::Class* ClassTable::Lookup(const char* descriptor, size_t hash) { @@ -145,12 +145,12 @@ ObjPtr ClassTable::TryInsert(ObjPtr klass) { TableSlot slot(klass); WriterMutexLock mu(Thread::Current(), lock_); for (ClassSet& class_set : classes_) { - auto it = class_set.Find(slot); + auto it = class_set.find(slot); if (it != class_set.end()) { return it->Read(); } } - classes_.back().Insert(slot); + classes_.back().insert(slot); return klass; } @@ -163,12 +163,12 @@ void ClassTable::Insert(ObjPtr klass) { void ClassTable::CopyWithoutLocks(const ClassTable& source_table) { if (kIsDebugBuild) { for (ClassSet& class_set : classes_) { - CHECK(class_set.Empty()); + CHECK(class_set.empty()); } } for (const ClassSet& class_set : source_table.classes_) { for (const TableSlot& slot : class_set) { - classes_.back().Insert(slot); + classes_.back().insert(slot); } } } @@ -187,9 +187,9 @@ bool ClassTable::Remove(const char* descriptor) { DescriptorHashPair pair(descriptor, ComputeModifiedUtf8Hash(descriptor)); WriterMutexLock mu(Thread::Current(), lock_); for (ClassSet& class_set : classes_) { - auto it = class_set.Find(pair); + auto it = class_set.find(pair); if (it != class_set.end()) { - class_set.Erase(it); + class_set.erase(it); return true; } } @@ -268,7 +268,7 @@ size_t ClassTable::WriteToMemory(uint8_t* ptr) const { // default in case classes were pruned. for (const ClassSet& class_set : classes_) { for (const TableSlot& root : class_set) { - combined.Insert(root); + combined.insert(root); } } const size_t ret = combined.WriteToMemory(ptr); diff --git a/runtime/class_table.h b/runtime/class_table.h index 19c29b5768f565795980595cd578f5628a2cd8fe..0b08041dbd380a6204420a5ac1ae3a105e31c139 100644 --- a/runtime/class_table.h +++ b/runtime/class_table.h @@ -53,14 +53,14 @@ class ClassTable { public: TableSlot() : data_(0u) {} - TableSlot(const TableSlot& copy) : data_(copy.data_.LoadRelaxed()) {} + TableSlot(const TableSlot& copy) : data_(copy.data_.load(std::memory_order_relaxed)) {} explicit TableSlot(ObjPtr klass); TableSlot(ObjPtr klass, uint32_t descriptor_hash); TableSlot& operator=(const TableSlot& copy) { - data_.StoreRelaxed(copy.data_.LoadRelaxed()); + data_.store(copy.data_.load(std::memory_order_relaxed), std::memory_order_relaxed); return *this; } @@ -69,7 +69,7 @@ class ClassTable { } uint32_t Hash() const { - return MaskHash(data_.LoadRelaxed()); + return MaskHash(data_.load(std::memory_order_relaxed)); } static uint32_t MaskHash(uint32_t hash) { @@ -295,7 +295,6 @@ class ClassTable { std::vector oat_files_ GUARDED_BY(lock_); friend class linker::ImageWriter; // for InsertWithoutLocks. - friend class linker::OatWriter; // for boot class TableSlot address lookup. }; } // namespace art diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h index 37e074d552360eeeeb0d27873c320473e297924a..c29043e7c6835fa3a891dcef2b4a8172bd40959c 100644 --- a/runtime/common_dex_operations.h +++ b/runtime/common_dex_operations.h @@ -29,6 +29,7 @@ #include "instrumentation.h" #include "interpreter/shadow_frame.h" #include "interpreter/unstarted_runtime.h" +#include "jvalue-inl.h" #include "mirror/class.h" #include "mirror/object.h" #include "obj_ptr-inl.h" @@ -204,7 +205,12 @@ ALWAYS_INLINE bool DoFieldPutCommon(Thread* self, HandleWrapperObjPtr h_obj(hs.NewHandleWrapper(&obj)); field_class = field->ResolveType(); } - if (!reg->VerifierInstanceOf(field_class.Ptr())) { + // ArtField::ResolveType() may fail as evidenced with a dexing bug (b/78788577). + if (UNLIKELY(field_class.IsNull())) { + Thread::Current()->AssertPendingException(); + return false; + } + if (UNLIKELY(!reg->VerifierInstanceOf(field_class.Ptr()))) { // This should never happen. std::string temp1, temp2, temp3; self->ThrowNewExceptionF("Ljava/lang/InternalError;", diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 05159e253d89efbf75af8541d2be585a7f4f6d7e..be39631e447710d9942b2041b938f0924a54e754 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -30,6 +30,7 @@ #include "base/file_utils.h" #include "base/logging.h" #include "base/macros.h" +#include "base/mem_map.h" #include "base/mutex.h" #include "base/os.h" #include "base/runtime_debug.h" @@ -47,9 +48,8 @@ #include "gtest/gtest.h" #include "handle_scope-inl.h" #include "interpreter/unstarted_runtime.h" -#include "java_vm_ext.h" -#include "jni_internal.h" -#include "mem_map.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "native/dalvik_system_DexFile.h" @@ -67,6 +67,7 @@ int main(int argc, char **argv) { art::Locks::Init(); art::InitLogging(argv, art::Runtime::Abort); + art::MemMap::Init(); LOG(INFO) << "Running main() from common_runtime_test.cc..."; testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); @@ -76,69 +77,6 @@ namespace art { using android::base::StringPrintf; -ScratchFile::ScratchFile() { - // ANDROID_DATA needs to be set - CHECK_NE(static_cast(nullptr), getenv("ANDROID_DATA")) << - "Are you subclassing RuntimeTest?"; - filename_ = getenv("ANDROID_DATA"); - filename_ += "/TmpFile-XXXXXX"; - int fd = mkstemp(&filename_[0]); - CHECK_NE(-1, fd) << strerror(errno) << " for " << filename_; - file_.reset(new File(fd, GetFilename(), true)); -} - -ScratchFile::ScratchFile(const ScratchFile& other, const char* suffix) - : ScratchFile(other.GetFilename() + suffix) {} - -ScratchFile::ScratchFile(const std::string& filename) : filename_(filename) { - int fd = open(filename_.c_str(), O_RDWR | O_CREAT, 0666); - CHECK_NE(-1, fd); - file_.reset(new File(fd, GetFilename(), true)); -} - -ScratchFile::ScratchFile(File* file) { - CHECK(file != nullptr); - filename_ = file->GetPath(); - file_.reset(file); -} - -ScratchFile::ScratchFile(ScratchFile&& other) { - *this = std::move(other); -} - -ScratchFile& ScratchFile::operator=(ScratchFile&& other) { - if (GetFile() != other.GetFile()) { - std::swap(filename_, other.filename_); - std::swap(file_, other.file_); - } - return *this; -} - -ScratchFile::~ScratchFile() { - Unlink(); -} - -int ScratchFile::GetFd() const { - return file_->Fd(); -} - -void ScratchFile::Close() { - if (file_.get() != nullptr) { - if (file_->FlushCloseOrErase() != 0) { - PLOG(WARNING) << "Error closing scratch file."; - } - } -} - -void ScratchFile::Unlink() { - if (!OS::FileExists(filename_.c_str())) { - return; - } - Close(); - int unlink_result = unlink(filename_.c_str()); - CHECK_EQ(0, unlink_result); -} - static bool unstarted_initialized_ = false; CommonRuntimeTestImpl::CommonRuntimeTestImpl() @@ -151,124 +89,6 @@ CommonRuntimeTestImpl::~CommonRuntimeTestImpl() { runtime_.reset(); } -void CommonRuntimeTestImpl::SetUpAndroidRoot() { - if (IsHost()) { - // $ANDROID_ROOT is set on the device, but not necessarily on the host. - // But it needs to be set so that icu4c can find its locale data. - const char* android_root_from_env = getenv("ANDROID_ROOT"); - if (android_root_from_env == nullptr) { - // Use ANDROID_HOST_OUT for ANDROID_ROOT if it is set. - const char* android_host_out = getenv("ANDROID_HOST_OUT"); - if (android_host_out != nullptr) { - setenv("ANDROID_ROOT", android_host_out, 1); - } else { - // Build it from ANDROID_BUILD_TOP or cwd - std::string root; - const char* android_build_top = getenv("ANDROID_BUILD_TOP"); - if (android_build_top != nullptr) { - root += android_build_top; - } else { - // Not set by build server, so default to current directory - char* cwd = getcwd(nullptr, 0); - setenv("ANDROID_BUILD_TOP", cwd, 1); - root += cwd; - free(cwd); - } -#if defined(__linux__) - root += "/out/host/linux-x86"; -#elif defined(__APPLE__) - root += "/out/host/darwin-x86"; -#else -#error unsupported OS -#endif - setenv("ANDROID_ROOT", root.c_str(), 1); - } - } - setenv("LD_LIBRARY_PATH", ":", 0); // Required by java.lang.System.. - - // Not set by build server, so default - if (getenv("ANDROID_HOST_OUT") == nullptr) { - setenv("ANDROID_HOST_OUT", getenv("ANDROID_ROOT"), 1); - } - } -} - -void CommonRuntimeTestImpl::SetUpAndroidData(std::string& android_data) { - // On target, Cannot use /mnt/sdcard because it is mounted noexec, so use subdir of dalvik-cache - if (IsHost()) { - const char* tmpdir = getenv("TMPDIR"); - if (tmpdir != nullptr && tmpdir[0] != 0) { - android_data = tmpdir; - } else { - android_data = "/tmp"; - } - } else { - android_data = "/data/dalvik-cache"; - } - android_data += "/art-data-XXXXXX"; - if (mkdtemp(&android_data[0]) == nullptr) { - PLOG(FATAL) << "mkdtemp(\"" << &android_data[0] << "\") failed"; - } - setenv("ANDROID_DATA", android_data.c_str(), 1); -} - -void CommonRuntimeTestImpl::TearDownAndroidData(const std::string& android_data, - bool fail_on_error) { - if (fail_on_error) { - ASSERT_EQ(rmdir(android_data.c_str()), 0); - } else { - rmdir(android_data.c_str()); - } -} - -// Helper - find directory with the following format: -// ${ANDROID_BUILD_TOP}/${subdir1}/${subdir2}-${version}/${subdir3}/bin/ -static std::string GetAndroidToolsDir(const std::string& subdir1, - const std::string& subdir2, - const std::string& subdir3) { - std::string root; - const char* android_build_top = getenv("ANDROID_BUILD_TOP"); - if (android_build_top != nullptr) { - root = android_build_top; - } else { - // Not set by build server, so default to current directory - char* cwd = getcwd(nullptr, 0); - setenv("ANDROID_BUILD_TOP", cwd, 1); - root = cwd; - free(cwd); - } - - std::string toolsdir = root + "/" + subdir1; - std::string founddir; - DIR* dir; - if ((dir = opendir(toolsdir.c_str())) != nullptr) { - float maxversion = 0; - struct dirent* entry; - while ((entry = readdir(dir)) != nullptr) { - std::string format = subdir2 + "-%f"; - float version; - if (std::sscanf(entry->d_name, format.c_str(), &version) == 1) { - if (version > maxversion) { - maxversion = version; - founddir = toolsdir + "/" + entry->d_name + "/" + subdir3 + "/bin/"; - } - } - } - closedir(dir); - } - - if (founddir.empty()) { - ADD_FAILURE() << "Cannot find Android tools directory."; - } - return founddir; -} - -std::string CommonRuntimeTestImpl::GetAndroidHostToolsDir() { - return GetAndroidToolsDir("prebuilts/gcc/linux-x86/host", - "x86_64-linux-glibc2.15", - "x86_64-linux"); -} - std::string CommonRuntimeTestImpl::GetAndroidTargetToolsDir(InstructionSet isa) { switch (isa) { case InstructionSet::kArm: @@ -297,38 +117,8 @@ std::string CommonRuntimeTestImpl::GetAndroidTargetToolsDir(InstructionSet isa) return ""; } -std::string CommonRuntimeTestImpl::GetCoreArtLocation() { - return GetCoreFileLocation("art"); -} - -std::string CommonRuntimeTestImpl::GetCoreOatLocation() { - return GetCoreFileLocation("oat"); -} - -std::unique_ptr CommonRuntimeTestImpl::LoadExpectSingleDexFile( - const char* location) { - std::vector> dex_files; - std::string error_msg; - MemMap::Init(); - static constexpr bool kVerifyChecksum = true; - const ArtDexFileLoader dex_file_loader; - if (!dex_file_loader.Open( - location, location, /* verify */ true, kVerifyChecksum, &error_msg, &dex_files)) { - LOG(FATAL) << "Could not open .dex file '" << location << "': " << error_msg << "\n"; - UNREACHABLE(); - } else { - CHECK_EQ(1U, dex_files.size()) << "Expected only one dex file in " << location; - return std::move(dex_files[0]); - } -} - void CommonRuntimeTestImpl::SetUp() { - SetUpAndroidRoot(); - SetUpAndroidData(android_data_); - dalvik_cache_.append(android_data_.c_str()); - dalvik_cache_.append("/dalvik-cache"); - int mkdir_result = mkdir(dalvik_cache_.c_str(), 0700); - ASSERT_EQ(mkdir_result, 0); + CommonArtTestImpl::SetUp(); std::string min_heap_string(StringPrintf("-Xms%zdm", gc::Heap::kDefaultInitialSize / MB)); std::string max_heap_string(StringPrintf("-Xmx%zdm", gc::Heap::kDefaultMaximumSize / MB)); @@ -392,7 +182,7 @@ void CommonRuntimeTestImpl::FinalizeSetup() { { ScopedObjectAccess soa(Thread::Current()); - class_linker_->RunRootClinits(); + runtime_->RunRootClinits(soa.Self()); } // We're back in native, take the opportunity to initialize well known classes. @@ -406,80 +196,13 @@ void CommonRuntimeTestImpl::FinalizeSetup() { runtime_->GetHeap()->SetMinIntervalHomogeneousSpaceCompactionByOom(0U); } -void CommonRuntimeTestImpl::ClearDirectory(const char* dirpath, bool recursive) { - ASSERT_TRUE(dirpath != nullptr); - DIR* dir = opendir(dirpath); - ASSERT_TRUE(dir != nullptr); - dirent* e; - struct stat s; - while ((e = readdir(dir)) != nullptr) { - if ((strcmp(e->d_name, ".") == 0) || (strcmp(e->d_name, "..") == 0)) { - continue; - } - std::string filename(dirpath); - filename.push_back('/'); - filename.append(e->d_name); - int stat_result = lstat(filename.c_str(), &s); - ASSERT_EQ(0, stat_result) << "unable to stat " << filename; - if (S_ISDIR(s.st_mode)) { - if (recursive) { - ClearDirectory(filename.c_str()); - int rmdir_result = rmdir(filename.c_str()); - ASSERT_EQ(0, rmdir_result) << filename; - } - } else { - int unlink_result = unlink(filename.c_str()); - ASSERT_EQ(0, unlink_result) << filename; - } - } - closedir(dir); -} - void CommonRuntimeTestImpl::TearDown() { - const char* android_data = getenv("ANDROID_DATA"); - ASSERT_TRUE(android_data != nullptr); - ClearDirectory(dalvik_cache_.c_str()); - int rmdir_cache_result = rmdir(dalvik_cache_.c_str()); - ASSERT_EQ(0, rmdir_cache_result); - TearDownAndroidData(android_data_, true); - dalvik_cache_.clear(); - + CommonArtTestImpl::TearDown(); if (runtime_ != nullptr) { runtime_->GetHeap()->VerifyHeap(); // Check for heap corruption after the test } } -static std::string GetDexFileName(const std::string& jar_prefix, bool host) { - std::string path; - if (host) { - const char* host_dir = getenv("ANDROID_HOST_OUT"); - CHECK(host_dir != nullptr); - path = host_dir; - } else { - path = GetAndroidRoot(); - } - - std::string suffix = host - ? "-hostdex" // The host version. - : "-testdex"; // The unstripped target version. - - return StringPrintf("%s/framework/%s%s.jar", path.c_str(), jar_prefix.c_str(), suffix.c_str()); -} - -std::vector CommonRuntimeTestImpl::GetLibCoreDexFileNames() { - return std::vector({GetDexFileName("core-oj", IsHost()), - GetDexFileName("core-libart", IsHost())}); -} - -std::string CommonRuntimeTestImpl::GetTestAndroidRoot() { - if (IsHost()) { - const char* host_dir = getenv("ANDROID_HOST_OUT"); - CHECK(host_dir != nullptr); - return host_dir; - } - return GetAndroidRoot(); -} - // Check that for target builds we have ART_TARGET_NATIVETEST_DIR set. #ifdef ART_TARGET #ifndef ART_TARGET_NATIVETEST_DIR @@ -491,47 +214,6 @@ std::string CommonRuntimeTestImpl::GetTestAndroidRoot() { #define ART_TARGET_NATIVETEST_DIR_STRING "" #endif -std::string CommonRuntimeTestImpl::GetTestDexFileName(const char* name) const { - CHECK(name != nullptr); - std::string filename; - if (IsHost()) { - filename += getenv("ANDROID_HOST_OUT"); - filename += "/framework/"; - } else { - filename += ART_TARGET_NATIVETEST_DIR_STRING; - } - filename += "art-gtest-"; - filename += name; - filename += ".jar"; - return filename; -} - -std::vector> CommonRuntimeTestImpl::OpenTestDexFiles( - const char* name) { - std::string filename = GetTestDexFileName(name); - static constexpr bool kVerifyChecksum = true; - std::string error_msg; - const ArtDexFileLoader dex_file_loader; - std::vector> dex_files; - bool success = dex_file_loader.Open(filename.c_str(), - filename.c_str(), - /* verify */ true, - kVerifyChecksum, - &error_msg, &dex_files); - CHECK(success) << "Failed to open '" << filename << "': " << error_msg; - for (auto& dex_file : dex_files) { - CHECK_EQ(PROT_READ, dex_file->GetPermissions()); - CHECK(dex_file->IsReadOnly()); - } - return dex_files; -} - -std::unique_ptr CommonRuntimeTestImpl::OpenTestDexFile(const char* name) { - std::vector> vector = OpenTestDexFiles(name); - EXPECT_EQ(1U, vector.size()); - return std::move(vector[0]); -} - std::vector CommonRuntimeTestImpl::GetDexFiles(jobject jclass_loader) { ScopedObjectAccess soa(Thread::Current()); @@ -658,43 +340,6 @@ jobject CommonRuntimeTestImpl::LoadDexInDelegateLastClassLoader(const std::strin parent_loader); } -std::string CommonRuntimeTestImpl::GetCoreFileLocation(const char* suffix) { - CHECK(suffix != nullptr); - - std::string location; - if (IsHost()) { - const char* host_dir = getenv("ANDROID_HOST_OUT"); - CHECK(host_dir != nullptr); - location = StringPrintf("%s/framework/core.%s", host_dir, suffix); - } else { - location = StringPrintf("/data/art-test/core.%s", suffix); - } - - return location; -} - -std::string CommonRuntimeTestImpl::CreateClassPath( - const std::vector>& dex_files) { - CHECK(!dex_files.empty()); - std::string classpath = dex_files[0]->GetLocation(); - for (size_t i = 1; i < dex_files.size(); i++) { - classpath += ":" + dex_files[i]->GetLocation(); - } - return classpath; -} - -std::string CommonRuntimeTestImpl::CreateClassPathWithChecksums( - const std::vector>& dex_files) { - CHECK(!dex_files.empty()); - std::string classpath = dex_files[0]->GetLocation() + "*" + - std::to_string(dex_files[0]->GetLocationChecksum()); - for (size_t i = 1; i < dex_files.size(); i++) { - classpath += ":" + dex_files[i]->GetLocation() + "*" + - std::to_string(dex_files[i]->GetLocationChecksum()); - } - return classpath; -} - void CommonRuntimeTestImpl::FillHeap(Thread* self, ClassLinker* class_linker, VariableSizedHandleScope* handle_scope) { diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index 7fc70e294fdcb59d636387805dccb9c2010784dc..234b66a862de5dfd552571511eafd313182820c8 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -25,12 +25,13 @@ #include #include "arch/instruction_set.h" +#include "base/common_art_test.h" +#include "base/globals.h" #include "base/mutex.h" #include "base/os.h" #include "base/unix_file/fd_file.h" #include "dex/art_dex_file_loader.h" #include "dex/compact_dex_level.h" -#include "globals.h" // TODO: Add inl file and avoid including inl. #include "obj_ptr-inl.h" #include "scoped_thread_state_change-inl.h" @@ -55,64 +56,13 @@ typedef std::vector> RuntimeOptions; class Thread; class VariableSizedHandleScope; -class ScratchFile { - public: - ScratchFile(); - - explicit ScratchFile(const std::string& filename); - - ScratchFile(const ScratchFile& other, const char* suffix); - - ScratchFile(ScratchFile&& other); - - ScratchFile& operator=(ScratchFile&& other); - - explicit ScratchFile(File* file); - - ~ScratchFile(); - - const std::string& GetFilename() const { - return filename_; - } - - File* GetFile() const { - return file_.get(); - } - - int GetFd() const; - - void Close(); - void Unlink(); - - private: - std::string filename_; - std::unique_ptr file_; -}; - -class CommonRuntimeTestImpl { +class CommonRuntimeTestImpl : public CommonArtTestImpl { public: CommonRuntimeTestImpl(); virtual ~CommonRuntimeTestImpl(); - static void SetUpAndroidRoot(); - - // Note: setting up ANDROID_DATA may create a temporary directory. If this is used in a - // non-derived class, be sure to also call the corresponding tear-down below. - static void SetUpAndroidData(std::string& android_data); - - static void TearDownAndroidData(const std::string& android_data, bool fail_on_error); - // Gets the paths of the libcore dex files. - static std::vector GetLibCoreDexFileNames(); - - // Returns bin directory which contains host's prebuild tools. - static std::string GetAndroidHostToolsDir(); - - // Returns bin directory which contains target's prebuild tools. static std::string GetAndroidTargetToolsDir(InstructionSet isa); - // Retuerns the filename for a test dex (i.e. XandY or ManyMethods). - std::string GetTestDexFileName(const char* name) const; - // A helper function to fill the heap. static void FillHeap(Thread* self, ClassLinker* class_linker, @@ -157,26 +107,6 @@ class CommonRuntimeTestImpl { // Called after the runtime is created. virtual void PostRuntimeCreate() {} - static bool IsHost() { - return !kIsTargetBuild; - } - - // File location to core.art, e.g. $ANDROID_HOST_OUT/system/framework/core.art - static std::string GetCoreArtLocation(); - - // File location to core.oat, e.g. $ANDROID_HOST_OUT/system/framework/core.oat - static std::string GetCoreOatLocation(); - - std::unique_ptr LoadExpectSingleDexFile(const char* location); - - void ClearDirectory(const char* dirpath, bool recursive = true); - - std::string GetTestAndroidRoot(); - - std::vector> OpenTestDexFiles(const char* name); - - std::unique_ptr OpenTestDexFile(const char* name); - // Loads the test dex file identified by the given dex_name into a PathClassLoader. // Returns the created class loader. jobject LoadDex(const char* dex_name) REQUIRES_SHARED(Locks::mutator_lock_); @@ -191,9 +121,6 @@ class CommonRuntimeTestImpl { jclass loader_class, jobject parent_loader); - std::string android_data_; - std::string dalvik_cache_; - std::unique_ptr runtime_; // The class_linker_, java_lang_dex_file_, and boot_class_path_ are all @@ -221,20 +148,6 @@ class CommonRuntimeTestImpl { // Called to finish up runtime creation and filling test fields. By default runs root // initializers, initialize well-known classes, and creates the heap thread pool. virtual void FinalizeSetup(); - - // Creates the class path string for the given dex files (the list of dex file locations - // separated by ':'). - std::string CreateClassPath( - const std::vector>& dex_files); - // Same as CreateClassPath but add the dex file checksum after each location. The separator - // is '*'. - std::string CreateClassPathWithChecksums( - const std::vector>& dex_files); - - private: - static std::string GetCoreFileLocation(const char* suffix); - - std::vector> loaded_dex_files_; }; template @@ -278,9 +191,15 @@ class CheckJniAbortCatcher { DISALLOW_COPY_AND_ASSIGN(CheckJniAbortCatcher); }; -#define TEST_DISABLED_FOR_TARGET() \ - if (kIsTargetBuild) { \ - printf("WARNING: TEST DISABLED FOR TARGET\n"); \ +#define TEST_DISABLED_FOR_ARM() \ + if (kRuntimeISA == InstructionSet::kArm || kRuntimeISA == InstructionSet::kThumb2) { \ + printf("WARNING: TEST DISABLED FOR ARM\n"); \ + return; \ + } + +#define TEST_DISABLED_FOR_ARM64() \ + if (kRuntimeISA == InstructionSet::kArm64) { \ + printf("WARNING: TEST DISABLED FOR ARM64\n"); \ return; \ } @@ -290,6 +209,12 @@ class CheckJniAbortCatcher { return; \ } +#define TEST_DISABLED_FOR_MIPS64() \ + if (kRuntimeISA == InstructionSet::kMips64) { \ + printf("WARNING: TEST DISABLED FOR MIPS64\n"); \ + return; \ + } + #define TEST_DISABLED_FOR_X86() \ if (kRuntimeISA == InstructionSet::kX86) { \ printf("WARNING: TEST DISABLED FOR X86\n"); \ @@ -308,41 +233,18 @@ class CheckJniAbortCatcher { return; \ } -#define TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS() \ - if (!kHostStaticBuildEnabled) { \ - printf("WARNING: TEST DISABLED FOR NON-STATIC HOST BUILDS\n"); \ - return; \ - } - -#define TEST_DISABLED_FOR_MEMORY_TOOL() \ - if (RUNNING_ON_MEMORY_TOOL > 0) { \ - printf("WARNING: TEST DISABLED FOR MEMORY TOOL\n"); \ - return; \ - } - -#define TEST_DISABLED_FOR_MEMORY_TOOL_VALGRIND() \ - if (RUNNING_ON_MEMORY_TOOL > 0 && kMemoryToolIsValgrind) { \ - printf("WARNING: TEST DISABLED FOR MEMORY TOOL VALGRIND\n"); \ - return; \ - } - -#define TEST_DISABLED_FOR_MEMORY_TOOL_ASAN() \ - if (RUNNING_ON_MEMORY_TOOL > 0 && !kMemoryToolIsValgrind) { \ - printf("WARNING: TEST DISABLED FOR MEMORY TOOL ASAN\n"); \ +#define TEST_DISABLED_FOR_HEAP_POISONING() \ + if (kPoisonHeapReferences) { \ + printf("WARNING: TEST DISABLED FOR HEAP POISONING\n"); \ return; \ } -#define TEST_DISABLED_FOR_COMPACT_DEX() \ - if (kDefaultCompactDexLevel != CompactDexLevel::kCompactDexLevelNone) { \ - printf("WARNING: TEST DISABLED FOR COMPACT DEX\n"); \ +#define TEST_DISABLED_FOR_MEMORY_TOOL_WITH_HEAP_POISONING_WITHOUT_READ_BARRIERS() \ + if (kRunningOnMemoryTool && kPoisonHeapReferences && !kEmitCompilerReadBarrier) { \ + printf("WARNING: TEST DISABLED FOR MEMORY TOOL WITH HEAP POISONING WITHOUT READ BARRIERS\n"); \ return; \ } -#define TEST_DISABLED_FOR_HEAP_POISONING() \ - if (kPoisonHeapReferences) { \ - printf("WARNING: TEST DISABLED FOR HEAP POISONING\n"); \ - return; \ - } } // namespace art #endif // ART_RUNTIME_COMMON_RUNTIME_TEST_H_ diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index b72dc3ff4c3866cc6d1ba7765b659e5f364d7ee0..657a78bd2fc6819cf6e4fd967d9b15e1cfacc5d4 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -878,13 +878,16 @@ void ThrowVerifyError(ObjPtr referrer, const char* fmt, ...) { // WrongMethodTypeException -void ThrowWrongMethodTypeException(mirror::MethodType* expected_type, - mirror::MethodType* actual_type) { - ThrowException("Ljava/lang/invoke/WrongMethodTypeException;", - nullptr, - StringPrintf("Expected %s but was %s", - expected_type->PrettyDescriptor().c_str(), - actual_type->PrettyDescriptor().c_str()).c_str()); +void ThrowWrongMethodTypeException(ObjPtr expected_type, + ObjPtr actual_type) { + ThrowWrongMethodTypeException(expected_type->PrettyDescriptor(), actual_type->PrettyDescriptor()); +} + +void ThrowWrongMethodTypeException(const std::string& expected_descriptor, + const std::string& actual_descriptor) { + std::ostringstream msg; + msg << "Expected " << expected_descriptor << " but was " << actual_descriptor; + ThrowException("Ljava/lang/invoke/WrongMethodTypeException;", nullptr, msg.str().c_str()); } } // namespace art diff --git a/runtime/common_throws.h b/runtime/common_throws.h index e9baa4fef0bf33fb789387d0523fa125829c5981..6acff6f222a7ae9c36e7bff2d92853af56e22d50 100644 --- a/runtime/common_throws.h +++ b/runtime/common_throws.h @@ -270,8 +270,12 @@ void ThrowVerifyError(ObjPtr referrer, const char* fmt, ...) // WrongMethodTypeException -void ThrowWrongMethodTypeException(mirror::MethodType* callee_type, - mirror::MethodType* callsite_type) +void ThrowWrongMethodTypeException(ObjPtr callee_type, + ObjPtr callsite_type) + REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; + +void ThrowWrongMethodTypeException(const std::string& expected_descriptor, + const std::string& actual_descriptor) REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; } // namespace art diff --git a/runtime/debug_print.cc b/runtime/debug_print.cc index c5bb4d57e60f4b0933edd62116a4518ac932864f..cb334b569f50ce78d698f34ce846a0c7de2cab20 100644 --- a/runtime/debug_print.cc +++ b/runtime/debug_print.cc @@ -37,7 +37,7 @@ std::string DescribeSpace(ObjPtr klass) { std::ostringstream oss; gc::Heap* heap = Runtime::Current()->GetHeap(); gc::space::ContinuousSpace* cs = - heap->FindContinuousSpaceFromObject(klass.Ptr(), /* fail_ok */ true); + heap->FindContinuousSpaceFromObject(klass, /* fail_ok */ true); if (cs != nullptr) { if (cs->IsImageSpace()) { gc::space::ImageSpace* ispace = cs->AsImageSpace(); diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 0481c03d52dace7da07168b27f8ce946425d1830..f75f47c075ed9c2abd3042db97849265c5a7886c 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -51,7 +51,7 @@ #include "handle_scope-inl.h" #include "jdwp/jdwp_priv.h" #include "jdwp/object_registry.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "jvalue-inl.h" #include "mirror/class-inl.h" #include "mirror/class.h" @@ -1388,7 +1388,7 @@ JDWP::JdwpError Dbg::CreateObject(JDWP::RefTypeId class_id, JDWP::ObjectId* new_ *new_object_id = 0; return JDWP::ERR_OUT_OF_MEMORY; } - *new_object_id = gRegistry->Add(new_object.Ptr()); + *new_object_id = gRegistry->Add(new_object); return JDWP::ERR_NONE; } @@ -1404,10 +1404,9 @@ JDWP::JdwpError Dbg::CreateArrayObject(JDWP::RefTypeId array_class_id, uint32_t return error; } Thread* self = Thread::Current(); - gc::Heap* heap = Runtime::Current()->GetHeap(); - mirror::Array* new_array = mirror::Array::Alloc(self, c, length, - c->GetComponentSizeShift(), - heap->GetCurrentAllocator()); + gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); + ObjPtr new_array = + mirror::Array::Alloc(self, c, length, c->GetComponentSizeShift(), allocator_type); if (new_array == nullptr) { DCHECK(self->IsExceptionPending()); self->ClearException(); @@ -1849,7 +1848,7 @@ static JValue GetArtFieldValue(ArtField* f, mirror::Object* o) return field_value; case Primitive::kPrimNot: - field_value.SetL(f->GetObject(o).Ptr()); + field_value.SetL(f->GetObject(o)); return field_value; case Primitive::kPrimVoid: @@ -4359,9 +4358,11 @@ bool Dbg::DdmHandleChunk(JNIEnv* env, WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch, type, dataArray.get(), 0, data.size())); if (env->ExceptionCheck()) { - LOG(INFO) << StringPrintf("Exception thrown by dispatcher for 0x%08x", type); - env->ExceptionDescribe(); - env->ExceptionClear(); + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + LOG(INFO) << StringPrintf("Exception thrown by dispatcher for 0x%08x", type) << std::endl + << self->GetException()->Dump(); + self->ClearException(); return false; } @@ -4405,10 +4406,11 @@ bool Dbg::DdmHandleChunk(JNIEnv* env, reinterpret_cast(out_data->data())); if (env->ExceptionCheck()) { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); LOG(INFO) << StringPrintf("Exception thrown when reading response data from dispatcher 0x%08x", - type); - env->ExceptionDescribe(); - env->ExceptionClear(); + type) << std::endl << self->GetException()->Dump(); + self->ClearException(); return false; } diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc index 6f3354b724f9e7f488fc305243a627724492d783..9358cbe5a96479126857b73144ed8678354c5c11 100644 --- a/runtime/dex/dex_file_annotations.cc +++ b/runtime/dex/dex_file_annotations.cc @@ -23,12 +23,14 @@ #include "art_field-inl.h" #include "art_method-inl.h" #include "class_linker-inl.h" +#include "class_root.h" #include "dex/dex_file-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "jvalue-inl.h" #include "mirror/field.h" #include "mirror/method.h" #include "oat_file.h" +#include "obj_ptr-inl.h" #include "reflection.h" #include "thread.h" #include "well_known_classes.h" @@ -116,9 +118,9 @@ class ClassData { DISALLOW_COPY_AND_ASSIGN(ClassData); }; -mirror::Object* CreateAnnotationMember(const ClassData& klass, - Handle annotation_class, - const uint8_t** annotation) +ObjPtr CreateAnnotationMember(const ClassData& klass, + Handle annotation_class, + const uint8_t** annotation) REQUIRES_SHARED(Locks::mutator_lock_); bool IsVisibilityCompatible(uint32_t actual, uint32_t expected) { @@ -333,7 +335,7 @@ const DexFile::AnnotationSetItem* FindAnnotationSetForClass(const ClassData& kla return dex_file.GetClassAnnotationSet(annotations_dir); } -mirror::Object* ProcessEncodedAnnotation(const ClassData& klass, const uint8_t** annotation) +ObjPtr ProcessEncodedAnnotation(const ClassData& klass, const uint8_t** annotation) REQUIRES_SHARED(Locks::mutator_lock_) { uint32_t type_index = DecodeUnsignedLeb128(annotation); uint32_t size = DecodeUnsignedLeb128(annotation); @@ -355,13 +357,13 @@ mirror::Object* ProcessEncodedAnnotation(const ClassData& klass, const uint8_t** } ObjPtr annotation_member_class = - soa.Decode(WellKnownClasses::libcore_reflect_AnnotationMember).Ptr(); - mirror::Class* annotation_member_array_class = - class_linker->FindArrayClass(self, &annotation_member_class); + soa.Decode(WellKnownClasses::libcore_reflect_AnnotationMember); + ObjPtr annotation_member_array_class = + class_linker->FindArrayClass(self, annotation_member_class); if (annotation_member_array_class == nullptr) { return nullptr; } - mirror::ObjectArray* element_array = nullptr; + ObjPtr> element_array = nullptr; if (size > 0) { element_array = mirror::ObjectArray::Alloc(self, annotation_member_array_class, size); @@ -373,7 +375,7 @@ mirror::Object* ProcessEncodedAnnotation(const ClassData& klass, const uint8_t** Handle> h_element_array(hs.NewHandle(element_array)); for (uint32_t i = 0; i < size; ++i) { - mirror::Object* new_member = CreateAnnotationMember(klass, annotation_class, annotation); + ObjPtr new_member = CreateAnnotationMember(klass, annotation_class, annotation); if (new_member == nullptr) { return nullptr; } @@ -605,7 +607,7 @@ bool ProcessAnnotationValue(const ClassData& klass, return false; } if (!component_type->IsPrimitive()) { - mirror::Object* obj = new_annotation_value.value_.GetL(); + ObjPtr obj = new_annotation_value.value_.GetL(); new_array->AsObjectArray()-> SetWithoutChecks(i, obj); } else { @@ -682,20 +684,20 @@ bool ProcessAnnotationValue(const ClassData& klass, *annotation_ptr = annotation; if (result_style == DexFile::kAllObjects && primitive_type != Primitive::kPrimVoid) { - element_object = BoxPrimitive(primitive_type, annotation_value->value_).Ptr(); + element_object = BoxPrimitive(primitive_type, annotation_value->value_); set_object = true; } if (set_object) { - annotation_value->value_.SetL(element_object.Ptr()); + annotation_value->value_.SetL(element_object); } return true; } -mirror::Object* CreateAnnotationMember(const ClassData& klass, - Handle annotation_class, - const uint8_t** annotation) { +ObjPtr CreateAnnotationMember(const ClassData& klass, + Handle annotation_class, + const uint8_t** annotation) { const DexFile& dex_file = klass.GetDexFile(); Thread* self = Thread::Current(); ScopedObjectAccessUnchecked soa(self); @@ -799,7 +801,7 @@ const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet( return nullptr; } -mirror::Object* GetAnnotationObjectFromAnnotationSet( +ObjPtr GetAnnotationObjectFromAnnotationSet( const ClassData& klass, const DexFile::AnnotationSetItem* annotation_set, uint32_t visibility, @@ -814,11 +816,11 @@ mirror::Object* GetAnnotationObjectFromAnnotationSet( return ProcessEncodedAnnotation(klass, &annotation); } -mirror::Object* GetAnnotationValue(const ClassData& klass, - const DexFile::AnnotationItem* annotation_item, - const char* annotation_name, - Handle array_class, - uint32_t expected_type) +ObjPtr GetAnnotationValue(const ClassData& klass, + const DexFile::AnnotationItem* annotation_item, + const char* annotation_name, + Handle array_class, + uint32_t expected_type) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile& dex_file = klass.GetDexFile(); const uint8_t* annotation = @@ -847,7 +849,8 @@ mirror::Object* GetAnnotationValue(const ClassData& klass, return annotation_value.value_.GetL(); } -mirror::ObjectArray* GetSignatureValue(const ClassData& klass, +static ObjPtr> GetSignatureValue( + const ClassData& klass, const DexFile::AnnotationSetItem* annotation_set) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile& dex_file = klass.GetDexFile(); @@ -858,13 +861,10 @@ mirror::ObjectArray* GetSignatureValue(const ClassData& klass, if (annotation_item == nullptr) { return nullptr; } - ObjPtr string_class = mirror::String::GetJavaLangString(); - Handle string_array_class(hs.NewHandle( - Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &string_class))); - if (string_array_class == nullptr) { - return nullptr; - } - mirror::Object* obj = + Handle string_array_class = + hs.NewHandle(GetClassRoot>()); + DCHECK(string_array_class != nullptr); + ObjPtr obj = GetAnnotationValue(klass, annotation_item, "value", string_array_class, DexFile::kDexAnnotationArray); if (obj == nullptr) { @@ -873,24 +873,22 @@ mirror::ObjectArray* GetSignatureValue(const ClassData& klass, return obj->AsObjectArray(); } -mirror::ObjectArray* GetThrowsValue(const ClassData& klass, - const DexFile::AnnotationSetItem* annotation_set) +ObjPtr> GetThrowsValue( + const ClassData& klass, + const DexFile::AnnotationSetItem* annotation_set) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile& dex_file = klass.GetDexFile(); - StackHandleScope<1> hs(Thread::Current()); const DexFile::AnnotationItem* annotation_item = SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/Throws;", DexFile::kDexVisibilitySystem); if (annotation_item == nullptr) { return nullptr; } - ObjPtr class_class = mirror::Class::GetJavaLangClass(); - Handle class_array_class(hs.NewHandle( - Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &class_class))); - if (class_array_class == nullptr) { - return nullptr; - } - mirror::Object* obj = + StackHandleScope<1> hs(Thread::Current()); + Handle class_array_class = + hs.NewHandle(GetClassRoot>()); + DCHECK(class_array_class != nullptr); + ObjPtr obj = GetAnnotationValue(klass, annotation_item, "value", class_array_class, DexFile::kDexAnnotationArray); if (obj == nullptr) { @@ -899,7 +897,7 @@ mirror::ObjectArray* GetThrowsValue(const ClassData& klass, return obj->AsObjectArray(); } -mirror::ObjectArray* ProcessAnnotationSet( +ObjPtr> ProcessAnnotationSet( const ClassData& klass, const DexFile::AnnotationSetItem* annotation_set, uint32_t visibility) @@ -930,7 +928,7 @@ mirror::ObjectArray* ProcessAnnotationSet( continue; } const uint8_t* annotation = annotation_item->annotation_; - mirror::Object* annotation_obj = ProcessEncodedAnnotation(klass, &annotation); + ObjPtr annotation_obj = ProcessEncodedAnnotation(klass, &annotation); if (annotation_obj != nullptr) { result->SetWithoutChecks(dest_index, annotation_obj); ++dest_index; @@ -943,21 +941,21 @@ mirror::ObjectArray* ProcessAnnotationSet( return result.Get(); } - mirror::ObjectArray* trimmed_result = + ObjPtr> trimmed_result = mirror::ObjectArray::Alloc(self, annotation_array_class.Get(), dest_index); if (trimmed_result == nullptr) { return nullptr; } for (uint32_t i = 0; i < dest_index; ++i) { - mirror::Object* obj = result->GetWithoutChecks(i); + ObjPtr obj = result->GetWithoutChecks(i); trimmed_result->SetWithoutChecks(i, obj); } return trimmed_result; } -mirror::ObjectArray* ProcessAnnotationSetRefList( +ObjPtr> ProcessAnnotationSetRefList( const ClassData& klass, const DexFile::AnnotationSetRefList* set_ref_list, uint32_t size) @@ -968,8 +966,8 @@ mirror::ObjectArray* ProcessAnnotationSetRefList( StackHandleScope<1> hs(self); ObjPtr annotation_array_class = soa.Decode(WellKnownClasses::java_lang_annotation_Annotation__array); - mirror::Class* annotation_array_array_class = - Runtime::Current()->GetClassLinker()->FindArrayClass(self, &annotation_array_class); + ObjPtr annotation_array_array_class = + Runtime::Current()->GetClassLinker()->FindArrayClass(self, annotation_array_class); if (annotation_array_array_class == nullptr) { return nullptr; } @@ -982,8 +980,9 @@ mirror::ObjectArray* ProcessAnnotationSetRefList( for (uint32_t index = 0; index < size; ++index) { const DexFile::AnnotationSetRefItem* set_ref_item = &set_ref_list->list_[index]; const DexFile::AnnotationSetItem* set_item = dex_file.GetSetRefItemItem(set_ref_item); - mirror::Object* annotation_set = ProcessAnnotationSet(klass, set_item, - DexFile::kDexVisibilityRuntime); + ObjPtr annotation_set = ProcessAnnotationSet(klass, + set_item, + DexFile::kDexVisibilityRuntime); if (annotation_set == nullptr) { return nullptr; } @@ -995,7 +994,8 @@ mirror::ObjectArray* ProcessAnnotationSetRefList( namespace annotations { -mirror::Object* GetAnnotationForField(ArtField* field, Handle annotation_class) { +ObjPtr GetAnnotationForField(ArtField* field, + Handle annotation_class) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForField(field); if (annotation_set == nullptr) { return nullptr; @@ -1008,14 +1008,14 @@ mirror::Object* GetAnnotationForField(ArtField* field, Handle ann annotation_class); } -mirror::ObjectArray* GetAnnotationsForField(ArtField* field) { +ObjPtr> GetAnnotationsForField(ArtField* field) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForField(field); StackHandleScope<1> hs(Thread::Current()); const ClassData field_class(hs, field); return ProcessAnnotationSet(field_class, annotation_set, DexFile::kDexVisibilityRuntime); } -mirror::ObjectArray* GetSignatureAnnotationForField(ArtField* field) { +ObjPtr> GetSignatureAnnotationForField(ArtField* field) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForField(field); if (annotation_set == nullptr) { return nullptr; @@ -1037,7 +1037,7 @@ bool IsFieldAnnotationPresent(ArtField* field, Handle annotation_ return annotation_item != nullptr; } -mirror::Object* GetAnnotationDefaultValue(ArtMethod* method) { +ObjPtr GetAnnotationDefaultValue(ArtMethod* method) { const ClassData klass(method); const DexFile* dex_file = &klass.GetDexFile(); const DexFile::AnnotationsDirectoryItem* annotations_dir = @@ -1081,7 +1081,8 @@ mirror::Object* GetAnnotationDefaultValue(ArtMethod* method) { return annotation_value.value_.GetL(); } -mirror::Object* GetAnnotationForMethod(ArtMethod* method, Handle annotation_class) { +ObjPtr GetAnnotationForMethod(ArtMethod* method, + Handle annotation_class) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method); if (annotation_set == nullptr) { return nullptr; @@ -1090,14 +1091,14 @@ mirror::Object* GetAnnotationForMethod(ArtMethod* method, Handle DexFile::kDexVisibilityRuntime, annotation_class); } -mirror::ObjectArray* GetAnnotationsForMethod(ArtMethod* method) { +ObjPtr> GetAnnotationsForMethod(ArtMethod* method) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method); return ProcessAnnotationSet(ClassData(method), annotation_set, DexFile::kDexVisibilityRuntime); } -mirror::ObjectArray* GetExceptionTypesForMethod(ArtMethod* method) { +ObjPtr> GetExceptionTypesForMethod(ArtMethod* method) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method); if (annotation_set == nullptr) { return nullptr; @@ -1105,7 +1106,7 @@ mirror::ObjectArray* GetExceptionTypesForMethod(ArtMethod* method return GetThrowsValue(ClassData(method), annotation_set); } -mirror::ObjectArray* GetParameterAnnotations(ArtMethod* method) { +ObjPtr> GetParameterAnnotations(ArtMethod* method) { const DexFile* dex_file = method->GetDexFile(); const DexFile::ParameterAnnotationsItem* parameter_annotations = FindAnnotationsItemForMethod(method); @@ -1136,9 +1137,9 @@ uint32_t GetNumberOfAnnotatedMethodParameters(ArtMethod* method) { return set_ref_list->size_; } -mirror::Object* GetAnnotationForMethodParameter(ArtMethod* method, - uint32_t parameter_idx, - Handle annotation_class) { +ObjPtr GetAnnotationForMethodParameter(ArtMethod* method, + uint32_t parameter_idx, + Handle annotation_class) { const DexFile* dex_file = method->GetDexFile(); const DexFile::ParameterAnnotationsItem* parameter_annotations = FindAnnotationsItemForMethod(method); @@ -1165,9 +1166,10 @@ mirror::Object* GetAnnotationForMethodParameter(ArtMethod* method, annotation_class); } -bool GetParametersMetadataForMethod(ArtMethod* method, - MutableHandle>* names, - MutableHandle* access_flags) { +bool GetParametersMetadataForMethod( + ArtMethod* method, + /*out*/ MutableHandle>* names, + /*out*/ MutableHandle* access_flags) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method); if (annotation_set == nullptr) { @@ -1187,12 +1189,10 @@ bool GetParametersMetadataForMethod(ArtMethod* method, StackHandleScope<4> hs(Thread::Current()); // Extract the parameters' names String[]. - ObjPtr string_class = mirror::String::GetJavaLangString(); - Handle string_array_class(hs.NewHandle( - Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &string_class))); - if (UNLIKELY(string_array_class == nullptr)) { - return false; - } + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + Handle string_array_class = + hs.NewHandle(GetClassRoot>(class_linker)); + DCHECK(string_array_class != nullptr); ClassData data(method); Handle names_obj = @@ -1206,10 +1206,8 @@ bool GetParametersMetadataForMethod(ArtMethod* method, } // Extract the parameters' access flags int[]. - Handle int_array_class(hs.NewHandle(mirror::IntArray::GetArrayClass())); - if (UNLIKELY(int_array_class == nullptr)) { - return false; - } + Handle int_array_class(hs.NewHandle(GetClassRoot(class_linker))); + DCHECK(int_array_class != nullptr); Handle access_flags_obj = hs.NewHandle(GetAnnotationValue(data, annotation_item, @@ -1220,12 +1218,12 @@ bool GetParametersMetadataForMethod(ArtMethod* method, return false; } - names->Assign(names_obj.Get()->AsObjectArray()); - access_flags->Assign(access_flags_obj.Get()->AsIntArray()); + names->Assign(names_obj->AsObjectArray()); + access_flags->Assign(access_flags_obj->AsIntArray()); return true; } -mirror::ObjectArray* GetSignatureAnnotationForMethod(ArtMethod* method) { +ObjPtr> GetSignatureAnnotationForMethod(ArtMethod* method) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method); if (annotation_set == nullptr) { return nullptr; @@ -1307,8 +1305,8 @@ uint32_t GetNativeMethodAnnotationAccessFlags(const DexFile& dex_file, return access_flags; } -mirror::Object* GetAnnotationForClass(Handle klass, - Handle annotation_class) { +ObjPtr GetAnnotationForClass(Handle klass, + Handle annotation_class) { ClassData data(klass); const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { @@ -1320,13 +1318,13 @@ mirror::Object* GetAnnotationForClass(Handle klass, annotation_class); } -mirror::ObjectArray* GetAnnotationsForClass(Handle klass) { +ObjPtr> GetAnnotationsForClass(Handle klass) { ClassData data(klass); const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); return ProcessAnnotationSet(data, annotation_set, DexFile::kDexVisibilityRuntime); } -mirror::ObjectArray* GetDeclaredClasses(Handle klass) { +ObjPtr> GetDeclaredClasses(Handle klass) { ClassData data(klass); const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { @@ -1339,13 +1337,10 @@ mirror::ObjectArray* GetDeclaredClasses(Handle kla return nullptr; } StackHandleScope<1> hs(Thread::Current()); - ObjPtr class_class = mirror::Class::GetJavaLangClass(); - Handle class_array_class(hs.NewHandle( - Runtime::Current()->GetClassLinker()->FindArrayClass(hs.Self(), &class_class))); - if (class_array_class == nullptr) { - return nullptr; - } - mirror::Object* obj = + Handle class_array_class = + hs.NewHandle(GetClassRoot>()); + DCHECK(class_array_class != nullptr); + ObjPtr obj = GetAnnotationValue(data, annotation_item, "value", class_array_class, DexFile::kDexAnnotationArray); if (obj == nullptr) { @@ -1354,7 +1349,7 @@ mirror::ObjectArray* GetDeclaredClasses(Handle kla return obj->AsObjectArray(); } -mirror::Class* GetDeclaringClass(Handle klass) { +ObjPtr GetDeclaringClass(Handle klass) { ClassData data(klass); const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { @@ -1366,17 +1361,19 @@ mirror::Class* GetDeclaringClass(Handle klass) { if (annotation_item == nullptr) { return nullptr; } - mirror::Object* obj = GetAnnotationValue(data, annotation_item, "value", - ScopedNullHandle(), - DexFile::kDexAnnotationType); + ObjPtr obj = GetAnnotationValue(data, + annotation_item, + "value", + ScopedNullHandle(), + DexFile::kDexAnnotationType); if (obj == nullptr) { return nullptr; } return obj->AsClass(); } -mirror::Class* GetEnclosingClass(Handle klass) { - mirror::Class* declaring_class = GetDeclaringClass(klass); +ObjPtr GetEnclosingClass(Handle klass) { + ObjPtr declaring_class = GetDeclaringClass(klass); if (declaring_class != nullptr) { return declaring_class; } @@ -1420,7 +1417,7 @@ mirror::Class* GetEnclosingClass(Handle klass) { return method->GetDeclaringClass(); } -mirror::Object* GetEnclosingMethod(Handle klass) { +ObjPtr GetEnclosingMethod(Handle klass) { ClassData data(klass); const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { @@ -1438,7 +1435,7 @@ mirror::Object* GetEnclosingMethod(Handle klass) { DexFile::kDexAnnotationMethod); } -bool GetInnerClass(Handle klass, mirror::String** name) { +bool GetInnerClass(Handle klass, /*out*/ ObjPtr* name) { ClassData data(klass); const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { @@ -1505,7 +1502,8 @@ bool GetInnerClassFlags(Handle klass, uint32_t* flags) { return true; } -mirror::ObjectArray* GetSignatureAnnotationForClass(Handle klass) { +ObjPtr> GetSignatureAnnotationForClass( + Handle klass) { ClassData data(klass); const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { diff --git a/runtime/dex/dex_file_annotations.h b/runtime/dex/dex_file_annotations.h index 4bb0d75a57445380957456ba60c657c8dbddc394..bde7891091829b1a1f9d216a79d053fe3af36dec 100644 --- a/runtime/dex/dex_file_annotations.h +++ b/runtime/dex/dex_file_annotations.h @@ -22,6 +22,7 @@ #include "handle.h" #include "mirror/dex_cache.h" #include "mirror/object_array.h" +#include "obj_ptr.h" namespace art { @@ -35,37 +36,39 @@ class ClassLinker; namespace annotations { // Field annotations. -mirror::Object* GetAnnotationForField(ArtField* field, Handle annotation_class) +ObjPtr GetAnnotationForField(ArtField* field, + Handle annotation_class) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::ObjectArray* GetAnnotationsForField(ArtField* field) +ObjPtr> GetAnnotationsForField(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::ObjectArray* GetSignatureAnnotationForField(ArtField* field) +ObjPtr> GetSignatureAnnotationForField(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_); bool IsFieldAnnotationPresent(ArtField* field, Handle annotation_class) REQUIRES_SHARED(Locks::mutator_lock_); // Method annotations. -mirror::Object* GetAnnotationDefaultValue(ArtMethod* method) +ObjPtr GetAnnotationDefaultValue(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::Object* GetAnnotationForMethod(ArtMethod* method, Handle annotation_class) +ObjPtr GetAnnotationForMethod(ArtMethod* method, + Handle annotation_class) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::ObjectArray* GetAnnotationsForMethod(ArtMethod* method) +ObjPtr> GetAnnotationsForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::ObjectArray* GetExceptionTypesForMethod(ArtMethod* method) +ObjPtr> GetExceptionTypesForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::ObjectArray* GetParameterAnnotations(ArtMethod* method) +ObjPtr> GetParameterAnnotations(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); uint32_t GetNumberOfAnnotatedMethodParameters(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::Object* GetAnnotationForMethodParameter(ArtMethod* method, - uint32_t parameter_idx, - Handle annotation_class) +ObjPtr GetAnnotationForMethodParameter(ArtMethod* method, + uint32_t parameter_idx, + Handle annotation_class) REQUIRES_SHARED(Locks::mutator_lock_); -bool GetParametersMetadataForMethod(ArtMethod* method, - MutableHandle>* names, - MutableHandle* access_flags) - REQUIRES_SHARED(Locks::mutator_lock_); -mirror::ObjectArray* GetSignatureAnnotationForMethod(ArtMethod* method) +bool GetParametersMetadataForMethod( + ArtMethod* method, + /*out*/ MutableHandle>* names, + /*out*/ MutableHandle* access_flags) REQUIRES_SHARED(Locks::mutator_lock_); +ObjPtr> GetSignatureAnnotationForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); // Check whether `method` is annotated with `annotation_class`. // If `lookup_in_resolved_boot_classes` is true, look up any of the @@ -85,25 +88,25 @@ uint32_t GetNativeMethodAnnotationAccessFlags(const DexFile& dex_file, uint32_t method_index); // Class annotations. -mirror::Object* GetAnnotationForClass(Handle klass, +ObjPtr GetAnnotationForClass(Handle klass, Handle annotation_class) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::ObjectArray* GetAnnotationsForClass(Handle klass) +ObjPtr> GetAnnotationsForClass(Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::ObjectArray* GetDeclaredClasses(Handle klass) +ObjPtr> GetDeclaredClasses(Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::Class* GetDeclaringClass(Handle klass) +ObjPtr GetDeclaringClass(Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::Class* GetEnclosingClass(Handle klass) +ObjPtr GetEnclosingClass(Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::Object* GetEnclosingMethod(Handle klass) +ObjPtr GetEnclosingMethod(Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); -bool GetInnerClass(Handle klass, mirror::String** name) +bool GetInnerClass(Handle klass, /*out*/ ObjPtr* name) REQUIRES_SHARED(Locks::mutator_lock_); bool GetInnerClassFlags(Handle klass, uint32_t* flags) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::ObjectArray* GetSignatureAnnotationForClass(Handle klass) - REQUIRES_SHARED(Locks::mutator_lock_); +ObjPtr> GetSignatureAnnotationForClass( + Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); const char* GetSourceDebugExtension(Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); bool IsClassAnnotationPresent(Handle klass, diff --git a/runtime/dex_register_location.cc b/runtime/dex_register_location.cc new file mode 100644 index 0000000000000000000000000000000000000000..f3b09733b940f31d544077151c2a41f37e12555a --- /dev/null +++ b/runtime/dex_register_location.cc @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dex_register_location.h" + +namespace art { + +std::ostream& operator<<(std::ostream& stream, DexRegisterLocation::Kind kind) { + return stream << "Kind<" << static_cast(kind) << ">"; +} + +std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation& reg) { + using Kind = DexRegisterLocation::Kind; + switch (reg.GetKind()) { + case Kind::kInvalid: + return stream << "Invalid"; + case Kind::kNone: + return stream << "None"; + case Kind::kInStack: + return stream << "sp+" << reg.GetValue(); + case Kind::kInRegister: + return stream << "r" << reg.GetValue(); + case Kind::kInRegisterHigh: + return stream << "r" << reg.GetValue() << "/hi"; + case Kind::kInFpuRegister: + return stream << "f" << reg.GetValue(); + case Kind::kInFpuRegisterHigh: + return stream << "f" << reg.GetValue() << "/hi"; + case Kind::kConstant: + return stream << "#" << reg.GetValue(); + default: + return stream << "DexRegisterLocation(" << static_cast(reg.GetKind()) + << "," << reg.GetValue() << ")"; + } +} + +} // namespace art diff --git a/runtime/dex_register_location.h b/runtime/dex_register_location.h new file mode 100644 index 0000000000000000000000000000000000000000..98b4d41e2d06500a8b7344cfe0358eabc579fc78 --- /dev/null +++ b/runtime/dex_register_location.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_DEX_REGISTER_LOCATION_H_ +#define ART_RUNTIME_DEX_REGISTER_LOCATION_H_ + +#include +#include + +#include "base/dchecked_vector.h" +#include "base/memory_region.h" + +namespace art { + +// Dex register location container used by DexRegisterMap and StackMapStream. +class DexRegisterLocation { + public: + enum class Kind : int32_t { + kInvalid = -2, // only used internally during register map decoding. + kNone = -1, // vreg has not been set. + kInStack, // vreg is on the stack, value holds the stack offset. + kConstant, // vreg is a constant value. + kInRegister, // vreg is in low 32 bits of a core physical register. + kInRegisterHigh, // vreg is in high 32 bits of a core physical register. + kInFpuRegister, // vreg is in low 32 bits of an FPU register. + kInFpuRegisterHigh, // vreg is in high 32 bits of an FPU register. + }; + + DexRegisterLocation(Kind kind, int32_t value) : kind_(kind), value_(value) {} + + static DexRegisterLocation None() { return DexRegisterLocation(Kind::kNone, 0); } + static DexRegisterLocation Invalid() { return DexRegisterLocation(Kind::kInvalid, 0); } + + bool IsLive() const { return kind_ != Kind::kNone; } + + Kind GetKind() const { return kind_; } + + int32_t GetValue() const { return value_; } + + bool operator==(DexRegisterLocation other) const { + return kind_ == other.kind_ && value_ == other.value_; + } + + bool operator!=(DexRegisterLocation other) const { + return !(*this == other); + } + + int32_t GetStackOffsetInBytes() const { + DCHECK(kind_ == Kind::kInStack); + return value_; + } + + int32_t GetConstant() const { + DCHECK(kind_ == Kind::kConstant); + return value_; + } + + int32_t GetMachineRegister() const { + DCHECK(kind_ == Kind::kInRegister || + kind_ == Kind::kInRegisterHigh || + kind_ == Kind::kInFpuRegister || + kind_ == Kind::kInFpuRegisterHigh); + return value_; + } + + private: + DexRegisterLocation() {} + + Kind kind_; + int32_t value_; + + friend class DexRegisterMap; // Allow creation of uninitialized array of locations. +}; + +std::ostream& operator<<(std::ostream& stream, DexRegisterLocation::Kind kind); +std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation& reg); + +} // namespace art + +#endif // ART_RUNTIME_DEX_REGISTER_LOCATION_H_ diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc index 5994bed3e95118faac90f3ec2805a4bcfa1333d4..f8388f315d2d6f454e4e660bc42abac68f2805be 100644 --- a/runtime/dexopt_test.cc +++ b/runtime/dexopt_test.cc @@ -21,12 +21,12 @@ #include #include "base/file_utils.h" +#include "base/mem_map.h" #include "common_runtime_test.h" #include "compiler_callbacks.h" #include "dex2oat_environment_test.h" #include "dexopt_test.h" #include "gc/space/image_space.h" -#include "mem_map.h" namespace art { void DexoptTest::SetUp() { diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc index 719b4af293919c650a5a89b8419fbd773785e48b..026b5da7480f20666b61d50d6dc7dbbc20d92a58 100644 --- a/runtime/elf_file.cc +++ b/runtime/elf_file.cc @@ -1073,6 +1073,29 @@ bool ElfFileImpl::GetLoadedSize(size_t* size, std::string* error_msg) return true; } +static InstructionSet GetInstructionSetFromELF(uint16_t e_machine, uint32_t e_flags) { + switch (e_machine) { + case EM_ARM: + return InstructionSet::kArm; + case EM_AARCH64: + return InstructionSet::kArm64; + case EM_386: + return InstructionSet::kX86; + case EM_X86_64: + return InstructionSet::kX86_64; + case EM_MIPS: { + if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R2 || + (e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) { + return InstructionSet::kMips; + } else if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_64R6) { + return InstructionSet::kMips64; + } + break; + } + } + return InstructionSet::kNone; +} + template bool ElfFileImpl::Load(File* file, bool executable, diff --git a/runtime/elf_file_impl.h b/runtime/elf_file_impl.h index 3143df537f00acac323d33616aca56c55b681e6f..a5808e27baad919f988cef9746926177aff6a6d4 100644 --- a/runtime/elf_file_impl.h +++ b/runtime/elf_file_impl.h @@ -24,7 +24,7 @@ // Explicitly include our own elf.h to avoid Linux and other dependencies. #include "./elf.h" -#include "mem_map.h" +#include "base/mem_map.h" namespace art { diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 137eb4fe1ef2ed2502bd7d3b7539bcb97b9563e9..40ef10f904548fa56efeb63a650a57d175c83574 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -31,7 +31,7 @@ #include "imt_conflict_table.h" #include "imtable-inl.h" #include "indirect_reference_table.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/array.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" @@ -46,9 +46,7 @@ namespace art { inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method, const MethodInfo& method_info, - const InlineInfo& inline_info, - const InlineInfoEncoding& encoding, - uint8_t inlining_depth) + const BitTableRange& inline_infos) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(!outer_method->IsObsolete()); @@ -57,25 +55,29 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method, // suspended while executing it. ScopedAssertNoThreadSuspension sants(__FUNCTION__); - if (inline_info.EncodesArtMethodAtDepth(encoding, inlining_depth)) { - return inline_info.GetArtMethodAtDepth(encoding, inlining_depth); - } + { + InlineInfo inline_info = inline_infos.back(); + + if (inline_info.EncodesArtMethod()) { + return inline_info.GetArtMethod(); + } - uint32_t method_index = inline_info.GetMethodIndexAtDepth(encoding, method_info, inlining_depth); - if (inline_info.GetDexPcAtDepth(encoding, inlining_depth) == static_cast(-1)) { - // "charAt" special case. It is the only non-leaf method we inline across dex files. - ArtMethod* inlined_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt); - DCHECK_EQ(inlined_method->GetDexMethodIndex(), method_index); - return inlined_method; + uint32_t method_index = inline_info.GetMethodIndex(method_info); + if (inline_info.GetDexPc() == static_cast(-1)) { + // "charAt" special case. It is the only non-leaf method we inline across dex files. + ArtMethod* inlined_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt); + DCHECK_EQ(inlined_method->GetDexMethodIndex(), method_index); + return inlined_method; + } } // Find which method did the call in the inlining hierarchy. ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); ArtMethod* method = outer_method; - for (uint32_t depth = 0, end = inlining_depth + 1u; depth != end; ++depth) { - DCHECK(!inline_info.EncodesArtMethodAtDepth(encoding, depth)); - DCHECK_NE(inline_info.GetDexPcAtDepth(encoding, depth), static_cast(-1)); - method_index = inline_info.GetMethodIndexAtDepth(encoding, method_info, depth); + for (InlineInfo inline_info : inline_infos) { + DCHECK(!inline_info.EncodesArtMethod()); + DCHECK_NE(inline_info.GetDexPc(), static_cast(-1)); + uint32_t method_index = inline_info.GetMethodIndex(method_info); ArtMethod* inlined_method = class_linker->LookupResolvedMethod(method_index, method->GetDexCache(), method->GetClassLoader()); @@ -269,14 +271,14 @@ inline mirror::Class* CheckArrayAlloc(dex::TypeIndex type_idx, // check. template ALWAYS_INLINE -inline mirror::Array* AllocArrayFromCode(dex::TypeIndex type_idx, - int32_t component_count, - ArtMethod* method, - Thread* self, - gc::AllocatorType allocator_type) { +inline ObjPtr AllocArrayFromCode(dex::TypeIndex type_idx, + int32_t component_count, + ArtMethod* method, + Thread* self, + gc::AllocatorType allocator_type) { bool slow_path = false; - mirror::Class* klass = CheckArrayAlloc(type_idx, component_count, method, - &slow_path); + ObjPtr klass = + CheckArrayAlloc(type_idx, component_count, method, &slow_path); if (UNLIKELY(slow_path)) { if (klass == nullptr) { return nullptr; @@ -307,7 +309,7 @@ inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* klass, // No need to retry a slow-path allocation as the above code won't cause a GC or thread // suspension. return mirror::Array::Alloc(self, klass, component_count, - klass->GetComponentSizeShift(), allocator_type); + klass->GetComponentSizeShift(), allocator_type).Ptr(); } template @@ -744,33 +746,6 @@ inline ObjPtr ResolveVerifyAndClinit(dex::TypeIndex type_idx, return h_class.Get(); } -static inline ObjPtr ResolveString(ClassLinker* class_linker, - dex::StringIndex string_idx, - ArtMethod* referrer) - REQUIRES_SHARED(Locks::mutator_lock_) { - Thread::PoisonObjectPointersIfDebug(); - ObjPtr string = referrer->GetDexCache()->GetResolvedString(string_idx); - if (UNLIKELY(string == nullptr)) { - StackHandleScope<1> hs(Thread::Current()); - Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); - string = class_linker->ResolveString(string_idx, dex_cache); - } - return string; -} - -inline ObjPtr ResolveStringFromCode(ArtMethod* referrer, - dex::StringIndex string_idx) { - Thread::PoisonObjectPointersIfDebug(); - ObjPtr string = referrer->GetDexCache()->GetResolvedString(string_idx); - if (UNLIKELY(string == nullptr)) { - StackHandleScope<1> hs(Thread::Current()); - Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - string = class_linker->ResolveString(string_idx, dex_cache); - } - return string; -} - inline void UnlockJniSynchronizedMethod(jobject locked, Thread* self) { // Save any pending exception over monitor exit call. mirror::Throwable* saved_exception = nullptr; diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index ffa138d5b1933950624b1876293bd5c7cf266486..0c61965908bee2d71468463b4de0bbc19b259079 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -26,7 +26,7 @@ #include "entrypoints/quick/callee_save_frame.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/card_table-inl.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "mirror/class-inl.h" #include "mirror/method.h" #include "mirror/object-inl.h" @@ -74,11 +74,11 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, cons } for (size_t i = 0; i < args.size(); ++i) { if (shorty[i + 1] == 'L') { - jobject val = args.at(i).l; + jobject val = args[i].l; soa.Env()->SetObjectArrayElement(args_jobj, i, val); } else { JValue jv; - jv.SetJ(args.at(i).j); + jv.SetJ(args[i].j); mirror::Object* val = BoxPrimitive(Primitive::GetType(shorty[i + 1]), jv).Ptr(); if (val == nullptr) { CHECK(soa.Self()->IsExceptionPending()); @@ -180,10 +180,10 @@ static inline std::pair DoGetCalleeSaveMethodOuterCallerA ArtMethod** sp, CalleeSaveType type) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(type)); - const size_t callee_frame_size = GetCalleeSaveFrameSize(kRuntimeISA, type); + const size_t callee_frame_size = RuntimeCalleeSaveFrame::GetFrameSize(type); auto** caller_sp = reinterpret_cast( reinterpret_cast(sp) + callee_frame_size); - const size_t callee_return_pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA, type); + const size_t callee_return_pc_offset = RuntimeCalleeSaveFrame::GetReturnPcOffset(type); uintptr_t caller_pc = *reinterpret_cast( (reinterpret_cast(sp) + callee_return_pc_offset)); ArtMethod* outer_method = *caller_sp; @@ -201,18 +201,13 @@ static inline ArtMethod* DoGetCalleeSaveMethodCaller(ArtMethod* outer_method, DCHECK(current_code != nullptr); DCHECK(current_code->IsOptimized()); uintptr_t native_pc_offset = current_code->NativeQuickPcOffset(caller_pc); - CodeInfo code_info = current_code->GetOptimizedCodeInfo(); + CodeInfo code_info(current_code); MethodInfo method_info = current_code->GetOptimizedMethodInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); DCHECK(stack_map.IsValid()); - if (stack_map.HasInlineInfo(encoding.stack_map.encoding)) { - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); - caller = GetResolvedMethod(outer_method, - method_info, - inline_info, - encoding.inline_info.encoding, - inline_info.GetDepth(encoding.inline_info.encoding) - 1); + BitTableRange inline_infos = code_info.GetInlineInfosOf(stack_map); + if (!inline_infos.empty()) { + caller = GetResolvedMethod(outer_method, method_info, inline_infos); } } if (kIsDebugBuild && do_caller_check) { @@ -260,4 +255,26 @@ ArtMethod* GetCalleeSaveOuterMethod(Thread* self, CalleeSaveType type) { return DoGetCalleeSaveMethodOuterCallerAndPc(sp, type).first; } +ObjPtr ResolveMethodHandleFromCode(ArtMethod* referrer, + uint32_t method_handle_idx) { + Thread::PoisonObjectPointersIfDebug(); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + return class_linker->ResolveMethodHandle(Thread::Current(), method_handle_idx, referrer); +} + +ObjPtr ResolveMethodTypeFromCode(ArtMethod* referrer, + dex::ProtoIndex proto_idx) { + Thread::PoisonObjectPointersIfDebug(); + ObjPtr method_type = + referrer->GetDexCache()->GetResolvedMethodType(proto_idx); + if (UNLIKELY(method_type == nullptr)) { + StackHandleScope<2> hs(Thread::Current()); + Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); + Handle class_loader(hs.NewHandle(referrer->GetClassLoader())); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + method_type = class_linker->ResolveMethodType(hs.Self(), proto_idx, dex_cache, class_loader); + } + return method_type; +} + } // namespace art diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index eb32153b164f757430f1c8997a3efc3c189bf489..9d70b03dfa15ff050ee7f3e2a9bdaf496c577350 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -34,6 +34,8 @@ namespace art { namespace mirror { class Array; class Class; +class MethodHandle; +class MethodType; class Object; class String; } // namespace mirror @@ -85,11 +87,11 @@ ALWAYS_INLINE inline mirror::Class* CheckArrayAlloc(dex::TypeIndex type_idx, // When verification/compiler hasn't been able to verify access, optionally perform an access // check. template -ALWAYS_INLINE inline mirror::Array* AllocArrayFromCode(dex::TypeIndex type_idx, - int32_t component_count, - ArtMethod* method, - Thread* self, - gc::AllocatorType allocator_type) +ALWAYS_INLINE inline ObjPtr AllocArrayFromCode(dex::TypeIndex type_idx, + int32_t component_count, + ArtMethod* method, + Thread* self, + gc::AllocatorType allocator_type) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); @@ -151,8 +153,12 @@ inline ObjPtr ResolveVerifyAndClinit(dex::TypeIndex type_idx, REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); -inline ObjPtr ResolveStringFromCode(ArtMethod* referrer, - dex::StringIndex string_idx) +ObjPtr ResolveMethodHandleFromCode(ArtMethod* referrer, + uint32_t method_handle_idx) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Roles::uninterruptible_); + +ObjPtr ResolveMethodTypeFromCode(ArtMethod* referrer, dex::ProtoIndex proto_idx) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc index 780e221129c8901cdc724a5b8cd3b62edc375e6f..a4083a4f8168bb1f26f2e5f0b728238a4980569a 100644 --- a/runtime/entrypoints/jni/jni_entrypoints.cc +++ b/runtime/entrypoints/jni/jni_entrypoints.cc @@ -18,7 +18,7 @@ #include "art_method-inl.h" #include "entrypoints/entrypoint_utils.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "mirror/object-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread.h" diff --git a/runtime/entrypoints/quick/callee_save_frame.h b/runtime/entrypoints/quick/callee_save_frame.h index ef27ca325c0d46909b5f46eab10fc47628c1a0af..1e309071f6ec6d7904c01dd22f815afcb881af46 100644 --- a/runtime/entrypoints/quick/callee_save_frame.h +++ b/runtime/entrypoints/quick/callee_save_frame.h @@ -21,16 +21,17 @@ #include "base/callee_save_type.h" #include "base/enums.h" #include "base/mutex.h" +#include "quick/quick_method_frame_info.h" #include "thread-inl.h" // Specific frame size code is in architecture-specific files. We include this to compile-time // specialize the code. -#include "arch/arm/quick_method_frame_info_arm.h" -#include "arch/arm64/quick_method_frame_info_arm64.h" -#include "arch/mips/quick_method_frame_info_mips.h" -#include "arch/mips64/quick_method_frame_info_mips64.h" -#include "arch/x86/quick_method_frame_info_x86.h" -#include "arch/x86_64/quick_method_frame_info_x86_64.h" +#include "arch/arm/callee_save_frame_arm.h" +#include "arch/arm64/callee_save_frame_arm64.h" +#include "arch/mips/callee_save_frame_mips.h" +#include "arch/mips64/callee_save_frame_mips64.h" +#include "arch/x86/callee_save_frame_x86.h" +#include "arch/x86_64/callee_save_frame_x86_64.h" namespace art { class ArtMethod; @@ -67,57 +68,28 @@ class ScopedQuickEntrypointChecks { bool exit_check_; }; -static constexpr size_t GetCalleeSaveFrameSize(InstructionSet isa, CalleeSaveType type) { - switch (isa) { - case InstructionSet::kArm: - case InstructionSet::kThumb2: - return arm::ArmCalleeSaveFrameSize(type); - case InstructionSet::kArm64: - return arm64::Arm64CalleeSaveFrameSize(type); - case InstructionSet::kMips: - return mips::MipsCalleeSaveFrameSize(type); - case InstructionSet::kMips64: - return mips64::Mips64CalleeSaveFrameSize(type); - case InstructionSet::kX86: - return x86::X86CalleeSaveFrameSize(type); - case InstructionSet::kX86_64: - return x86_64::X86_64CalleeSaveFrameSize(type); - case InstructionSet::kNone: - LOG(FATAL) << "kNone has no frame size"; - UNREACHABLE(); - } - LOG(FATAL) << "Unknown ISA " << isa; - UNREACHABLE(); -} - -// Note: this specialized statement is sanity-checked in the quick-trampoline gtest. -static constexpr PointerSize GetConstExprPointerSize(InstructionSet isa) { - switch (isa) { - case InstructionSet::kArm: - case InstructionSet::kThumb2: - return kArmPointerSize; - case InstructionSet::kArm64: - return kArm64PointerSize; - case InstructionSet::kMips: - return kMipsPointerSize; - case InstructionSet::kMips64: - return kMips64PointerSize; - case InstructionSet::kX86: - return kX86PointerSize; - case InstructionSet::kX86_64: - return kX86_64PointerSize; - case InstructionSet::kNone: - LOG(FATAL) << "kNone has no pointer size"; - UNREACHABLE(); - } - LOG(FATAL) << "Unknown ISA " << isa; - UNREACHABLE(); -} - -// Note: this specialized statement is sanity-checked in the quick-trampoline gtest. -static constexpr size_t GetCalleeSaveReturnPcOffset(InstructionSet isa, CalleeSaveType type) { - return GetCalleeSaveFrameSize(isa, type) - static_cast(GetConstExprPointerSize(isa)); -} +namespace detail { + +template +struct CSFSelector; // No definition for unspecialized callee save frame selector. + +// Note: kThumb2 is never the kRuntimeISA. +template <> +struct CSFSelector { using type = arm::ArmCalleeSaveFrame; }; +template <> +struct CSFSelector { using type = arm64::Arm64CalleeSaveFrame; }; +template <> +struct CSFSelector { using type = mips::MipsCalleeSaveFrame; }; +template <> +struct CSFSelector { using type = mips64::Mips64CalleeSaveFrame; }; +template <> +struct CSFSelector { using type = x86::X86CalleeSaveFrame; }; +template <> +struct CSFSelector { using type = x86_64::X86_64CalleeSaveFrame; }; + +} // namespace detail + +using RuntimeCalleeSaveFrame = detail::CSFSelector::type; } // namespace art diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc index ed5885f2240fb13e092d0a692c585d119a191473..257cd41bc8ba6eb2e200be0d8ea18a3243f3e4a3 100644 --- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc @@ -25,6 +25,7 @@ #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" +#include "mirror/string-inl.h" namespace art { @@ -83,6 +84,13 @@ extern "C" mirror::Object* artAllocObjectFromCodeInitialized##suffix##suffix2( \ REQUIRES_SHARED(Locks::mutator_lock_) { \ return artAllocObjectFromCode(klass, self); \ } \ +extern "C" mirror::String* artAllocStringObject##suffix##suffix2( \ + mirror::Class* klass, Thread* self) \ + REQUIRES_SHARED(Locks::mutator_lock_) { \ + /* The klass arg is so it matches the ABI of the other object alloc callbacks. */ \ + DCHECK(klass->IsStringClass()) << klass->PrettyClass(); \ + return mirror::String::AllocEmptyString(self, allocator_type); \ +} \ extern "C" mirror::Array* artAllocArrayFromCodeResolved##suffix##suffix2( \ mirror::Class* klass, int32_t component_count, Thread* self) \ REQUIRES_SHARED(Locks::mutator_lock_) { \ @@ -137,6 +145,7 @@ extern "C" void* art_quick_alloc_array_resolved64##suffix(mirror::Class* klass, extern "C" void* art_quick_alloc_object_resolved##suffix(mirror::Class* klass); \ extern "C" void* art_quick_alloc_object_initialized##suffix(mirror::Class* klass); \ extern "C" void* art_quick_alloc_object_with_checks##suffix(mirror::Class* klass); \ +extern "C" void* art_quick_alloc_string_object##suffix(mirror::Class* klass); \ extern "C" void* art_quick_alloc_string_from_bytes##suffix(void*, int32_t, int32_t, int32_t); \ extern "C" void* art_quick_alloc_string_from_chars##suffix(int32_t, int32_t, void*); \ extern "C" void* art_quick_alloc_string_from_string##suffix(void*); \ @@ -148,6 +157,7 @@ extern "C" void* art_quick_alloc_array_resolved64##suffix##_instrumented(mirror: extern "C" void* art_quick_alloc_object_resolved##suffix##_instrumented(mirror::Class* klass); \ extern "C" void* art_quick_alloc_object_initialized##suffix##_instrumented(mirror::Class* klass); \ extern "C" void* art_quick_alloc_object_with_checks##suffix##_instrumented(mirror::Class* klass); \ +extern "C" void* art_quick_alloc_string_object##suffix##_instrumented(mirror::Class* klass); \ extern "C" void* art_quick_alloc_string_from_bytes##suffix##_instrumented(void*, int32_t, int32_t, int32_t); \ extern "C" void* art_quick_alloc_string_from_chars##suffix##_instrumented(int32_t, int32_t, void*); \ extern "C" void* art_quick_alloc_string_from_string##suffix##_instrumented(void*); \ @@ -161,6 +171,7 @@ void SetQuickAllocEntryPoints##suffix(QuickEntryPoints* qpoints, bool instrument qpoints->pAllocObjectResolved = art_quick_alloc_object_resolved##suffix##_instrumented; \ qpoints->pAllocObjectInitialized = art_quick_alloc_object_initialized##suffix##_instrumented; \ qpoints->pAllocObjectWithChecks = art_quick_alloc_object_with_checks##suffix##_instrumented; \ + qpoints->pAllocStringObject = art_quick_alloc_string_object##suffix##_instrumented; \ qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix##_instrumented; \ qpoints->pAllocStringFromChars = art_quick_alloc_string_from_chars##suffix##_instrumented; \ qpoints->pAllocStringFromString = art_quick_alloc_string_from_string##suffix##_instrumented; \ @@ -173,6 +184,7 @@ void SetQuickAllocEntryPoints##suffix(QuickEntryPoints* qpoints, bool instrument qpoints->pAllocObjectResolved = art_quick_alloc_object_resolved##suffix; \ qpoints->pAllocObjectInitialized = art_quick_alloc_object_initialized##suffix; \ qpoints->pAllocObjectWithChecks = art_quick_alloc_object_with_checks##suffix; \ + qpoints->pAllocStringObject = art_quick_alloc_string_object##suffix; \ qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix; \ qpoints->pAllocStringFromChars = art_quick_alloc_string_from_chars##suffix; \ qpoints->pAllocStringFromString = art_quick_alloc_string_from_string##suffix; \ diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h index 2d0932a0c40dc31e607f179c444819d3e372884a..938489b7309b132fb7b669684a1c2af5f4a7330a 100644 --- a/runtime/entrypoints/quick/quick_default_externs.h +++ b/runtime/entrypoints/quick/quick_default_externs.h @@ -37,6 +37,8 @@ extern "C" void art_quick_check_instance_of(art::mirror::Object*, art::mirror::C extern "C" void* art_quick_initialize_static_storage(uint32_t); extern "C" void* art_quick_initialize_type(uint32_t); extern "C" void* art_quick_initialize_type_and_verify_access(uint32_t); +extern "C" void* art_quick_resolve_method_handle(uint32_t); +extern "C" void* art_quick_resolve_method_type(uint32_t); extern "C" void* art_quick_resolve_string(uint32_t); // Field entrypoints. @@ -112,9 +114,9 @@ extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, vo 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 8c908004632d0351dce3093f6c5874ae58669c9b..5dcece42088b58ad626049c302f0e353653b3878 100644 --- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h +++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h @@ -37,6 +37,8 @@ static void DefaultInitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qp qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage; qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access; qpoints->pInitializeType = art_quick_initialize_type; + qpoints->pResolveMethodHandle = art_quick_resolve_method_handle; + qpoints->pResolveMethodType = art_quick_resolve_method_type; qpoints->pResolveString = art_quick_resolve_string; // Field @@ -104,6 +106,7 @@ static void DefaultInitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qp 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_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index cfb427f1ac01579bc7981938b1d0fb994e7dcc5a..85d633f6a6c40db541264b8d08a86e40cab84b91 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -64,7 +64,7 @@ static void StoreObjectInBss(ArtMethod* outer_method, << oat_file->GetLocation(); } if (class_loader != nullptr) { - runtime->GetHeap()->WriteBarrierEveryFieldOf(class_loader); + WriteBarrier::ForEveryFieldWrite(class_loader); } else { runtime->GetClassLinker()->WriteBarrierForBootOatFileBssRoots(oat_file); } @@ -183,13 +183,35 @@ extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type return result.Ptr(); } +extern "C" mirror::MethodHandle* artResolveMethodHandleFromCode(uint32_t method_handle_idx, + Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); + auto caller_and_outer = + GetCalleeSaveMethodCallerAndOuterMethod(self, CalleeSaveType::kSaveEverything); + ArtMethod* caller = caller_and_outer.caller; + ObjPtr result = ResolveMethodHandleFromCode(caller, method_handle_idx); + return result.Ptr(); +} + +extern "C" mirror::MethodType* artResolveMethodTypeFromCode(uint32_t proto_idx, Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); + auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, + CalleeSaveType::kSaveEverything); + ArtMethod* caller = caller_and_outer.caller; + ObjPtr result = ResolveMethodTypeFromCode(caller, dex::ProtoIndex(proto_idx)); + return result.Ptr(); +} + extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, CalleeSaveType::kSaveEverything); ArtMethod* caller = caller_and_outer.caller; - ObjPtr result = ResolveStringFromCode(caller, dex::StringIndex(string_idx)); + ObjPtr result = + Runtime::Current()->GetClassLinker()->ResolveString(dex::StringIndex(string_idx), caller); if (LIKELY(result != nullptr) && CanReferenceBss(caller_and_outer.outer_method, caller)) { StoreStringInBss(caller_and_outer.outer_method, dex::StringIndex(string_idx), result); } diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h index 48a56f2fbf94ab409c3b1995a57f8b0b4601899e..4ce954c48a77cb6ac75df83ebc13957222618fa6 100644 --- a/runtime/entrypoints/quick/quick_entrypoints_list.h +++ b/runtime/entrypoints/quick/quick_entrypoints_list.h @@ -28,6 +28,9 @@ V(AllocObjectResolved, void*, mirror::Class*) \ V(AllocObjectInitialized, void*, mirror::Class*) \ V(AllocObjectWithChecks, void*, mirror::Class*) \ + /* NB Class argument is purely to match the ABI of the other object alloc entrypoints. It is */ \ + /* not actually used for anything. */ \ + V(AllocStringObject, void*, mirror::Class*) \ V(AllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t) \ V(AllocStringFromChars, void*, int32_t, int32_t, void*) \ V(AllocStringFromString, void*, void*) \ @@ -38,6 +41,8 @@ V(InitializeStaticStorage, void*, uint32_t) \ V(InitializeTypeAndVerifyAccess, void*, uint32_t) \ V(InitializeType, void*, uint32_t) \ + V(ResolveMethodHandle, void*, uint32_t) \ + V(ResolveMethodType, void*, uint32_t) \ V(ResolveString, void*, uint32_t) \ \ V(Set8Instance, int, uint32_t, void*, int8_t) \ @@ -132,6 +137,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_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc index 9b0756b529b968ced522c058d0d65bc10d3c03ff..ba7fb6b9db51a58d115df7de2cb296c090e0f29a 100644 --- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc @@ -16,8 +16,11 @@ #include "art_method-inl.h" #include "callee_save_frame.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_instruction-inl.h" #include "common_throws.h" #include "mirror/object-inl.h" +#include "nth_caller_visitor.h" #include "thread.h" #include "well_known_classes.h" @@ -112,6 +115,26 @@ extern "C" NO_RETURN void artThrowClassCastException(mirror::Class* dest_type, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); + if (dest_type == nullptr) { + // Find the target class for check cast using the bitstring check (dest_type == null). + NthCallerVisitor visitor(self, 0u); + visitor.WalkStack(); + DCHECK(visitor.caller != nullptr); + uint32_t dex_pc = visitor.GetDexPc(); + CodeItemDataAccessor accessor(*visitor.caller->GetDexFile(), visitor.caller->GetCodeItem()); + const Instruction& check_cast = accessor.InstructionAt(dex_pc); + DCHECK_EQ(check_cast.Opcode(), Instruction::CHECK_CAST); + dex::TypeIndex type_index(check_cast.VRegB_21c()); + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + dest_type = linker->LookupResolvedType(type_index, visitor.caller).Ptr(); + CHECK(dest_type != nullptr) << "Target class should have been previously resolved: " + << visitor.caller->GetDexFile()->PrettyType(type_index); + CHECK(!dest_type->IsAssignableFrom(src_type)) + << " " << std::hex << dest_type->PrettyDescriptor() << ";" << dest_type->Depth() + << "/" << dest_type->GetField32(mirror::Class::StatusOffset()) + << " <: " << src_type->PrettyDescriptor() << ";" << src_type->Depth() + << "/" << src_type->GetField32(mirror::Class::StatusOffset()); + } DCHECK(!dest_type->IsAssignableFrom(src_type)); ThrowClassCastException(dest_type, src_type); self->QuickDeliverException(); diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 56ccfaee870ff9f51720870af39afc9648eda1f7..505e183ced95946e056aef2c31faec395f4d80c9 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -19,6 +19,7 @@ #include "base/enums.h" #include "callee_save_frame.h" #include "common_throws.h" +#include "class_root.h" #include "debug_print.h" #include "debugger.h" #include "dex/dex_file-inl.h" @@ -26,6 +27,7 @@ #include "dex/dex_instruction-inl.h" #include "dex/method_reference.h" #include "entrypoints/entrypoint_utils-inl.h" +#include "entrypoints/quick/callee_save_frame.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/card_table-inl.h" #include "imt_conflict_table.h" @@ -33,7 +35,10 @@ #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 "jit/jit_code_cache.h" #include "linear_alloc.h" #include "method_handles.h" #include "mirror/class-inl.h" @@ -42,6 +47,7 @@ #include "mirror/method_handle_impl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" +#include "mirror/var_handle.h" #include "oat_file.h" #include "oat_quick_method_header.h" #include "quick_exception_handler.h" @@ -49,6 +55,7 @@ #include "scoped_thread_state_change-inl.h" #include "stack.h" #include "thread-inl.h" +#include "var_handles.h" #include "well_known_classes.h" namespace art { @@ -59,7 +66,16 @@ class QuickArgumentVisitor { static constexpr size_t kBytesStackArgLocation = 4; // Frame size in bytes of a callee-save frame for RefsAndArgs. static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_FrameSize = - GetCalleeSaveFrameSize(kRuntimeISA, CalleeSaveType::kSaveRefsAndArgs); + RuntimeCalleeSaveFrame::GetFrameSize(CalleeSaveType::kSaveRefsAndArgs); + // Offset of first GPR arg. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = + RuntimeCalleeSaveFrame::GetGpr1Offset(CalleeSaveType::kSaveRefsAndArgs); + // Offset of first FPR arg. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = + RuntimeCalleeSaveFrame::GetFpr1Offset(CalleeSaveType::kSaveRefsAndArgs); + // Offset of return address. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_ReturnPcOffset = + RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveRefsAndArgs); #if defined(__arm__) // The callee save frame is pointed to by SP. // | argN | | @@ -87,12 +103,6 @@ class QuickArgumentVisitor { static constexpr size_t kNumQuickGprArgs = 3; static constexpr size_t kNumQuickFprArgs = 16; static constexpr bool kGprFprLockstep = false; - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = - arm::ArmCalleeSaveFpr1Offset(CalleeSaveType::kSaveRefsAndArgs); // Offset of first FPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = - arm::ArmCalleeSaveGpr1Offset(CalleeSaveType::kSaveRefsAndArgs); // Offset of first GPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = - arm::ArmCalleeSaveLrOffset(CalleeSaveType::kSaveRefsAndArgs); // Offset of return address. static size_t GprIndexToGprOffset(uint32_t gpr_index) { return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA); } @@ -125,15 +135,6 @@ class QuickArgumentVisitor { static constexpr size_t kNumQuickGprArgs = 7; // 7 arguments passed in GPRs. static constexpr size_t kNumQuickFprArgs = 8; // 8 arguments passed in FPRs. static constexpr bool kGprFprLockstep = false; - // Offset of first FPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = - arm64::Arm64CalleeSaveFpr1Offset(CalleeSaveType::kSaveRefsAndArgs); - // Offset of first GPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = - arm64::Arm64CalleeSaveGpr1Offset(CalleeSaveType::kSaveRefsAndArgs); - // Offset of return address. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = - arm64::Arm64CalleeSaveLrOffset(CalleeSaveType::kSaveRefsAndArgs); static size_t GprIndexToGprOffset(uint32_t gpr_index) { return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA); } @@ -177,9 +178,6 @@ class QuickArgumentVisitor { // passed only in even numbered registers and each // double occupies two registers. static constexpr bool kGprFprLockstep = false; - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 8; // Offset of first FPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 56; // Offset of first GPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 108; // Offset of return address. static size_t GprIndexToGprOffset(uint32_t gpr_index) { return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA); } @@ -221,9 +219,6 @@ class QuickArgumentVisitor { static constexpr size_t kNumQuickFprArgs = 7; // 7 arguments passed in FPRs. static constexpr bool kGprFprLockstep = true; - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 24; // Offset of first FPR arg (F13). - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 80; // Offset of first GPR arg (A1). - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 200; // Offset of return address. static size_t GprIndexToGprOffset(uint32_t gpr_index) { return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA); } @@ -254,9 +249,6 @@ class QuickArgumentVisitor { static constexpr size_t kNumQuickGprArgs = 3; // 3 arguments passed in GPRs. static constexpr size_t kNumQuickFprArgs = 4; // 4 arguments passed in FPRs. static constexpr bool kGprFprLockstep = false; - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 4; // Offset of first FPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 4 + 4*8; // Offset of first GPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 28 + 4*8; // Offset of return address. static size_t GprIndexToGprOffset(uint32_t gpr_index) { return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA); } @@ -296,9 +288,6 @@ class QuickArgumentVisitor { static constexpr size_t kNumQuickGprArgs = 5; // 5 arguments passed in GPRs. static constexpr size_t kNumQuickFprArgs = 8; // 8 arguments passed in FPRs. static constexpr bool kGprFprLockstep = false; - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 16; // Offset of first FPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 80 + 4*8; // Offset of first GPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 168 + 4*8; // Offset of return address. static size_t GprIndexToGprOffset(uint32_t gpr_index) { switch (gpr_index) { case 0: return (4 * GetBytesPerGprSpillLocation(kRuntimeISA)); @@ -345,8 +334,8 @@ class QuickArgumentVisitor { static uint32_t GetCallingDexPc(ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK((*sp)->IsCalleeSaveMethod()); - const size_t callee_frame_size = GetCalleeSaveFrameSize(kRuntimeISA, - CalleeSaveType::kSaveRefsAndArgs); + constexpr size_t callee_frame_size = + RuntimeCalleeSaveFrame::GetFrameSize(CalleeSaveType::kSaveRefsAndArgs); ArtMethod** caller_sp = reinterpret_cast( reinterpret_cast(sp) + callee_frame_size); uintptr_t outer_pc = QuickArgumentVisitor::GetCallingPc(sp); @@ -354,52 +343,26 @@ class QuickArgumentVisitor { uintptr_t outer_pc_offset = current_code->NativeQuickPcOffset(outer_pc); if (current_code->IsOptimized()) { - CodeInfo code_info = current_code->GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(outer_pc_offset, encoding); + CodeInfo code_info(current_code); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(outer_pc_offset); DCHECK(stack_map.IsValid()); - if (stack_map.HasInlineInfo(encoding.stack_map.encoding)) { - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); - return inline_info.GetDexPcAtDepth(encoding.inline_info.encoding, - inline_info.GetDepth(encoding.inline_info.encoding)-1); + BitTableRange inline_infos = code_info.GetInlineInfosOf(stack_map); + if (!inline_infos.empty()) { + return inline_infos.back().GetDexPc(); } else { - return stack_map.GetDexPc(encoding.stack_map.encoding); + return stack_map.GetDexPc(); } } else { return current_code->ToDexPc(*caller_sp, outer_pc); } } - static bool GetInvokeType(ArtMethod** sp, InvokeType* invoke_type, uint32_t* dex_method_index) - REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK((*sp)->IsCalleeSaveMethod()); - const size_t callee_frame_size = GetCalleeSaveFrameSize(kRuntimeISA, - CalleeSaveType::kSaveRefsAndArgs); - ArtMethod** caller_sp = reinterpret_cast( - reinterpret_cast(sp) + callee_frame_size); - uintptr_t outer_pc = QuickArgumentVisitor::GetCallingPc(sp); - const OatQuickMethodHeader* current_code = (*caller_sp)->GetOatQuickMethodHeader(outer_pc); - if (!current_code->IsOptimized()) { - return false; - } - uintptr_t outer_pc_offset = current_code->NativeQuickPcOffset(outer_pc); - CodeInfo code_info = current_code->GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - MethodInfo method_info = current_code->GetOptimizedMethodInfo(); - InvokeInfo invoke(code_info.GetInvokeInfoForNativePcOffset(outer_pc_offset, encoding)); - if (invoke.IsValid()) { - *invoke_type = static_cast(invoke.GetInvokeType(encoding.invoke_info.encoding)); - *dex_method_index = invoke.GetMethodIndex(encoding.invoke_info.encoding, method_info); - return true; - } - return false; - } - // For the given quick ref and args quick frame, return the caller's PC. static uintptr_t GetCallingPc(ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK((*sp)->IsCalleeSaveMethod()); - uint8_t* lr = reinterpret_cast(sp) + kQuickCalleeSaveFrame_RefAndArgs_LrOffset; - return *reinterpret_cast(lr); + uint8_t* return_adress_spill = + reinterpret_cast(sp) + kQuickCalleeSaveFrame_RefAndArgs_ReturnPcOffset; + return *reinterpret_cast(return_adress_spill); } QuickArgumentVisitor(ArtMethod** sp, bool is_static, const char* shorty, @@ -961,8 +924,36 @@ extern "C" uint64_t artQuickProxyInvokeHandler( jobject interface_method_jobj = soa.AddLocalReference(interface_reflect_method); // All naked Object*s should now be in jobjects, so its safe to go into the main invoke code - // that performs allocations. + // that performs allocations or instrumentation events. + instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation(); + if (instr->HasMethodEntryListeners()) { + instr->MethodEnterEvent(soa.Self(), + soa.Decode(rcvr_jobj).Ptr(), + proxy_method, + 0); + if (soa.Self()->IsExceptionPending()) { + instr->MethodUnwindEvent(self, + soa.Decode(rcvr_jobj).Ptr(), + proxy_method, + 0); + return 0; + } + } JValue result = InvokeProxyInvocationHandler(soa, shorty, rcvr_jobj, interface_method_jobj, args); + if (soa.Self()->IsExceptionPending()) { + if (instr->HasMethodUnwindListeners()) { + instr->MethodUnwindEvent(self, + soa.Decode(rcvr_jobj).Ptr(), + proxy_method, + 0); + } + } else if (instr->HasMethodExitListeners()) { + instr->MethodExitEvent(self, + soa.Decode(rcvr_jobj).Ptr(), + proxy_method, + 0, + result); + } return result.GetJ(); } @@ -1114,11 +1105,27 @@ extern "C" const void* artInstrumentationMethodEntryFromCode(ArtMethod* method, // that part. ScopedQuickEntrypointChecks sqec(self, kIsDebugBuild, false); instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + DCHECK(!method->IsProxyMethod()) + << "Proxy method " << method->PrettyMethod() + << " (declaring class: " << method->GetDeclaringClass()->PrettyClass() << ")" + << " should not hit instrumentation entrypoint."; if (instrumentation->IsDeoptimized(method)) { result = GetQuickToInterpreterBridge(); } else { - result = instrumentation->GetQuickCodeFor(method, kRuntimePointerSize); - DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(result)); + // This will get the entry point either from the oat file, the JIT or the appropriate bridge + // method if none of those can be found. + result = instrumentation->GetCodeForInvoke(method); + jit::Jit* jit = Runtime::Current()->GetJit(); + DCHECK_NE(result, GetQuickInstrumentationEntryPoint()) << method->PrettyMethod(); + DCHECK(jit == nullptr || + // Native methods come through here in Interpreter entrypoints. We might not have + // disabled jit-gc but that is fine since we won't return jit-code for native methods. + method->IsNative() || + !jit->GetCodeCache()->GetGarbageCollectCode()); + DCHECK(!method->IsNative() || + jit == nullptr || + !jit->GetCodeCache()->ContainsPc(result)) + << method->PrettyMethod() << " code will jump to possibly cleaned up jit code!"; } bool interpreter_entry = (result == GetQuickToInterpreterBridge()); @@ -1157,8 +1164,8 @@ extern "C" TwoWordReturn artInstrumentationMethodExitFromCode(Thread* self, CHECK(!self->IsExceptionPending()) << "Enter instrumentation exit stub with pending exception " << self->GetException()->Dump(); // Compute address of return PC and sanity check that it currently holds 0. - size_t return_pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA, - CalleeSaveType::kSaveEverything); + constexpr size_t return_pc_offset = + RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveEverything); uintptr_t* return_pc = reinterpret_cast(reinterpret_cast(sp) + return_pc_offset); CHECK_EQ(*return_pc, 0U); @@ -1210,10 +1217,10 @@ static void DumpB74410240DebugData(ArtMethod** sp) REQUIRES_SHARED(Locks::mutato constexpr CalleeSaveType type = CalleeSaveType::kSaveRefsAndArgs; CHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(type)); - const size_t callee_frame_size = GetCalleeSaveFrameSize(kRuntimeISA, type); + constexpr size_t callee_frame_size = RuntimeCalleeSaveFrame::GetFrameSize(type); auto** caller_sp = reinterpret_cast( reinterpret_cast(sp) + callee_frame_size); - const size_t callee_return_pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA, type); + constexpr size_t callee_return_pc_offset = RuntimeCalleeSaveFrame::GetReturnPcOffset(type); uintptr_t caller_pc = *reinterpret_cast( (reinterpret_cast(sp) + callee_return_pc_offset)); ArtMethod* outer_method = *caller_sp; @@ -1228,12 +1235,11 @@ static void DumpB74410240DebugData(ArtMethod** sp) REQUIRES_SHARED(Locks::mutato CHECK(current_code != nullptr); CHECK(current_code->IsOptimized()); uintptr_t native_pc_offset = current_code->NativeQuickPcOffset(caller_pc); - CodeInfo code_info = current_code->GetOptimizedCodeInfo(); + CodeInfo code_info(current_code); MethodInfo method_info = current_code->GetOptimizedMethodInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); CHECK(stack_map.IsValid()); - uint32_t dex_pc = stack_map.GetDexPc(encoding.stack_map.encoding); + uint32_t dex_pc = stack_map.GetDexPc(); // Log the outer method and its associated dex file and class table pointer which can be used // to find out if the inlined methods were defined by other dex file(s) or class loader(s). @@ -1247,40 +1253,35 @@ static void DumpB74410240DebugData(ArtMethod** sp) REQUIRES_SHARED(Locks::mutato LOG(FATAL_WITHOUT_ABORT) << " instruction: " << DumpInstruction(outer_method, dex_pc); ArtMethod* caller = outer_method; - if (stack_map.HasInlineInfo(encoding.stack_map.encoding)) { - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); - const InlineInfoEncoding& inline_info_encoding = encoding.inline_info.encoding; - size_t depth = inline_info.GetDepth(inline_info_encoding); - for (size_t d = 0; d < depth; ++d) { - const char* tag = ""; - dex_pc = inline_info.GetDexPcAtDepth(inline_info_encoding, d); - if (inline_info.EncodesArtMethodAtDepth(inline_info_encoding, d)) { - tag = "encoded "; - caller = inline_info.GetArtMethodAtDepth(inline_info_encoding, d); + BitTableRange inline_infos = code_info.GetInlineInfosOf(stack_map); + for (InlineInfo inline_info : inline_infos) { + const char* tag = ""; + dex_pc = inline_info.GetDexPc(); + if (inline_info.EncodesArtMethod()) { + tag = "encoded "; + caller = inline_info.GetArtMethod(); + } else { + uint32_t method_index = inline_info.GetMethodIndex(method_info); + if (dex_pc == static_cast(-1)) { + tag = "special "; + CHECK(inline_info.Equals(inline_infos.back())); + caller = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt); + CHECK_EQ(caller->GetDexMethodIndex(), method_index); } else { - uint32_t method_index = inline_info.GetMethodIndexAtDepth(inline_info_encoding, - method_info, - d); - if (dex_pc == static_cast(-1)) { - tag = "special "; - CHECK_EQ(d + 1u, depth); - caller = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt); - CHECK_EQ(caller->GetDexMethodIndex(), method_index); - } else { - ObjPtr dex_cache = caller->GetDexCache(); - ObjPtr class_loader = caller->GetClassLoader(); - caller = class_linker->LookupResolvedMethod(method_index, dex_cache, class_loader); - CHECK(caller != nullptr); - } + ObjPtr dex_cache = caller->GetDexCache(); + ObjPtr class_loader = caller->GetClassLoader(); + caller = class_linker->LookupResolvedMethod(method_index, dex_cache, class_loader); + CHECK(caller != nullptr); } - LOG(FATAL_WITHOUT_ABORT) << "Inlined method #" << d << ": " << tag << caller->PrettyMethod() - << " dex pc: " << dex_pc - << " dex file: " << caller->GetDexFile()->GetLocation() - << " class table: " - << class_linker->ClassTableForClassLoader(caller->GetClassLoader()); - DumpB74410240ClassData(caller->GetDeclaringClass()); - LOG(FATAL_WITHOUT_ABORT) << " instruction: " << DumpInstruction(caller, dex_pc); } + LOG(FATAL_WITHOUT_ABORT) << "InlineInfo #" << inline_info.Row() + << ": " << tag << caller->PrettyMethod() + << " dex pc: " << dex_pc + << " dex file: " << caller->GetDexFile()->GetLocation() + << " class table: " + << class_linker->ClassTableForClassLoader(caller->GetClassLoader()); + DumpB74410240ClassData(caller->GetDeclaringClass()); + LOG(FATAL_WITHOUT_ABORT) << " instruction: " << DumpInstruction(caller, dex_pc); } } @@ -1308,14 +1309,7 @@ extern "C" const void* artQuickResolutionTrampoline( caller = QuickArgumentVisitor::GetCallingMethod(sp); called_method.dex_file = caller->GetDexFile(); - InvokeType stack_map_invoke_type; - uint32_t stack_map_dex_method_idx; - const bool found_stack_map = QuickArgumentVisitor::GetInvokeType(sp, - &stack_map_invoke_type, - &stack_map_dex_method_idx); - // For debug builds, we make sure both of the paths are consistent by also looking at the dex - // code. - if (!found_stack_map || kIsDebugBuild) { + { uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp); CodeItemInstructionAccessor accessor(caller->DexInstructions()); CHECK_LT(dex_pc, accessor.InsnsSizeInCodeUnits()); @@ -1369,23 +1363,8 @@ extern "C" const void* artQuickResolutionTrampoline( UNREACHABLE(); } called_method.index = (is_range) ? instr.VRegB_3rc() : instr.VRegB_35c(); - // Check that the invoke matches what we expected, note that this path only happens for debug - // builds. - if (found_stack_map) { - DCHECK_EQ(stack_map_invoke_type, invoke_type); - if (invoke_type != kSuper) { - // Super may be sharpened. - DCHECK_EQ(stack_map_dex_method_idx, called_method.index) - << called_method.dex_file->PrettyMethod(stack_map_dex_method_idx) << " " - << called_method.PrettyMethod(); - } - } else { - VLOG(dex) << "Accessed dex file for invoke " << invoke_type << " " - << called_method.index; - } - } else { - invoke_type = stack_map_invoke_type; - called_method.index = stack_map_dex_method_idx; + VLOG(dex) << "Accessed dex file for invoke " << invoke_type << " " + << called_method.index; } } else { invoke_type = kStatic; @@ -2356,10 +2335,6 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod** ArtMethod* called = *sp; DCHECK(called->IsNative()) << called->PrettyMethod(true); Runtime* runtime = Runtime::Current(); - jit::Jit* jit = runtime->GetJit(); - if (jit != nullptr) { - jit->AddSamples(self, called, 1u, /*with_backedges*/ false); - } uint32_t shorty_len = 0; const char* shorty = called->GetShorty(&shorty_len); bool critical_native = called->IsCriticalNative(); @@ -2385,6 +2360,12 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod** self->VerifyStack(); + // We can now walk the stack if needed by JIT GC from MethodEntered() for JIT-on-first-use. + jit::Jit* jit = runtime->GetJit(); + if (jit != nullptr) { + jit->MethodEntered(self, called); + } + uint32_t cookie; uint32_t* sp32; // Skip calling JniMethodStart for @CriticalNative. @@ -2738,18 +2719,11 @@ extern "C" TwoWordReturn artInvokeInterfaceTrampoline(ArtMethod* interface_metho reinterpret_cast(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 @@ -2764,7 +2738,7 @@ extern "C" uintptr_t artInvokePolymorphic( const Instruction& inst = caller_method->DexInstructions().InstructionAt(dex_pc); DCHECK(inst.Opcode() == Instruction::INVOKE_POLYMORPHIC || inst.Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE); - const uint32_t proto_idx = inst.VRegH(); + const dex::ProtoIndex proto_idx(inst.VRegH()); const char* shorty = caller_method->GetDexFile()->GetShorty(proto_idx); const size_t shorty_length = strlen(shorty); static const bool kMethodIsStatic = false; // invoke() and invokeExact() are not static. @@ -2782,25 +2756,12 @@ extern "C" uintptr_t artInvokePolymorphic( ArtMethod* resolved_method = linker->ResolveMethod( self, inst.VRegB(), caller_method, kVirtual); - if (UNLIKELY(receiver_handle.IsNull())) { - ThrowNullPointerExceptionForMethodAccess(resolved_method, InvokeType::kVirtual); - return static_cast('V'); - } - - // TODO(oth): Ensure this path isn't taken for VarHandle accessors (b/65872996). - DCHECK_EQ(resolved_method->GetDeclaringClass(), - WellKnownClasses::ToClass(WellKnownClasses::java_lang_invoke_MethodHandle)); - - Handle method_handle(hs.NewHandle( - ObjPtr::DownCast(MakeObjPtr(receiver_handle.Get())))); - Handle 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('V'); + return 0UL; } DCHECK_EQ(ArtMethod::NumArgRegisters(shorty) + 1u, (uint32_t)inst.VRegA()); @@ -2833,30 +2794,108 @@ extern "C" uintptr_t artInvokePolymorphic( // Call DoInvokePolymorphic with |is_range| = true, as shadow frame has argument registers in // consecutive order. RangeInstructionOperands operands(first_arg + 1, num_vregs - 1); - bool isExact = (jni::EncodeArtMethod(resolved_method) == - WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact); + Intrinsics intrinsic = static_cast(resolved_method->GetIntrinsic()); + JValue result; bool success = false; - if (isExact) { - success = MethodHandleInvokeExact(self, + if (resolved_method->GetDeclaringClass() == GetClassRoot(linker)) { + Handle method_handle(hs.NewHandle( + ObjPtr::DownCast(MakeObjPtr(receiver_handle.Get())))); + if (intrinsic == Intrinsics::kMethodHandleInvokeExact) { + success = MethodHandleInvokeExact(self, + *shadow_frame, + method_handle, + method_type, + &operands, + &result); + } else { + DCHECK_EQ(static_cast(intrinsic), + static_cast(Intrinsics::kMethodHandleInvoke)); + success = MethodHandleInvoke(self, + *shadow_frame, + method_handle, + method_type, + &operands, + &result); + } + } else { + DCHECK_EQ(GetClassRoot(linker), resolved_method->GetDeclaringClass()); + Handle var_handle(hs.NewHandle( + ObjPtr::DownCast(MakeObjPtr(receiver_handle.Get())))); + mirror::VarHandle::AccessMode access_mode = + mirror::VarHandle::GetAccessModeByIntrinsic(intrinsic); + success = VarHandleInvokeAccessor(self, *shadow_frame, - method_handle, + var_handle, method_type, + access_mode, &operands, - result); - } else { - success = MethodHandleInvoke(self, - *shadow_frame, - method_handle, - method_type, - &operands, - result); + &result); } + DCHECK(success || self->IsExceptionPending()); // Pop transition record. self->PopManagedStackFragment(fragment); - return static_cast(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/quick/quick_trampoline_entrypoints_test.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc index 77b3132bdc295649d14daf038b08bd28f2e6ffa0..89694e351a33888ca3df82a3aab008f54c29151a 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc @@ -54,15 +54,6 @@ class QuickTrampolineEntrypointsTest : public CommonRuntimeTest { return save_method; } - static void CheckFrameSize(InstructionSet isa, CalleeSaveType type, uint32_t save_size) - NO_THREAD_SAFETY_ANALYSIS { - ArtMethod* save_method = CreateCalleeSaveMethod(isa, type); - QuickMethodFrameInfo frame_info = Runtime::Current()->GetRuntimeMethodFrameInfo(save_method); - EXPECT_EQ(frame_info.FrameSizeInBytes(), save_size) << "Expected and real size differs for " - << type << " core spills=" << std::hex << frame_info.CoreSpillMask() << " fp spills=" - << frame_info.FpSpillMask() << std::dec << " ISA " << isa; - } - static void CheckPCOffset(InstructionSet isa, CalleeSaveType type, size_t pc_offset) NO_THREAD_SAFETY_ANALYSIS { ArtMethod* save_method = CreateCalleeSaveMethod(isa, type); @@ -74,79 +65,36 @@ class QuickTrampolineEntrypointsTest : public CommonRuntimeTest { } }; -// Note: these tests are all runtime tests. They let the Runtime create the corresponding ArtMethod -// and check against it. Technically we know and expect certain values, but the Runtime code is -// not constexpr, so we cannot make this compile-time checks (and I want the Runtime code tested). - -// This test ensures that kQuickCalleeSaveFrame_RefAndArgs_FrameSize is correct. -TEST_F(QuickTrampolineEntrypointsTest, FrameSize) { - // We have to use a define here as the callee_save_frame.h functions are constexpr. -#define CHECK_FRAME_SIZE(isa) \ - CheckFrameSize(isa, \ - CalleeSaveType::kSaveRefsAndArgs, \ - GetCalleeSaveFrameSize(isa, CalleeSaveType::kSaveRefsAndArgs)); \ - CheckFrameSize(isa, \ - CalleeSaveType::kSaveRefsOnly, \ - GetCalleeSaveFrameSize(isa, CalleeSaveType::kSaveRefsOnly)); \ - CheckFrameSize(isa, \ - CalleeSaveType::kSaveAllCalleeSaves, \ - GetCalleeSaveFrameSize(isa, CalleeSaveType::kSaveAllCalleeSaves)); \ - CheckFrameSize(isa, \ - CalleeSaveType::kSaveEverything, \ - GetCalleeSaveFrameSize(isa, CalleeSaveType::kSaveEverything)); \ - CheckFrameSize(isa, \ - CalleeSaveType::kSaveEverythingForClinit, \ - GetCalleeSaveFrameSize(isa, \ - CalleeSaveType::kSaveEverythingForClinit)); \ - CheckFrameSize(isa, \ - CalleeSaveType::kSaveEverythingForSuspendCheck, \ - GetCalleeSaveFrameSize( \ - isa, CalleeSaveType::kSaveEverythingForSuspendCheck)) - - CHECK_FRAME_SIZE(InstructionSet::kArm); - CHECK_FRAME_SIZE(InstructionSet::kArm64); - CHECK_FRAME_SIZE(InstructionSet::kMips); - CHECK_FRAME_SIZE(InstructionSet::kMips64); - CHECK_FRAME_SIZE(InstructionSet::kX86); - CHECK_FRAME_SIZE(InstructionSet::kX86_64); -} - -// This test ensures that GetConstExprPointerSize is correct with respect to -// GetInstructionSetPointerSize. -TEST_F(QuickTrampolineEntrypointsTest, PointerSize) { - EXPECT_EQ(GetInstructionSetPointerSize(InstructionSet::kArm), - GetConstExprPointerSize(InstructionSet::kArm)); - EXPECT_EQ(GetInstructionSetPointerSize(InstructionSet::kArm64), - GetConstExprPointerSize(InstructionSet::kArm64)); - EXPECT_EQ(GetInstructionSetPointerSize(InstructionSet::kMips), - GetConstExprPointerSize(InstructionSet::kMips)); - EXPECT_EQ(GetInstructionSetPointerSize(InstructionSet::kMips64), - GetConstExprPointerSize(InstructionSet::kMips64)); - EXPECT_EQ(GetInstructionSetPointerSize(InstructionSet::kX86), - GetConstExprPointerSize(InstructionSet::kX86)); - EXPECT_EQ(GetInstructionSetPointerSize(InstructionSet::kX86_64), - GetConstExprPointerSize(InstructionSet::kX86_64)); -} - // This test ensures that the constexpr specialization of the return PC offset computation in // GetCalleeSavePCOffset is correct. TEST_F(QuickTrampolineEntrypointsTest, ReturnPC) { // Ensure that the computation in callee_save_frame.h correct. // Note: we can only check against the kRuntimeISA, because the ArtMethod computation uses // sizeof(void*), which is wrong when the target bitwidth is not the same as the host's. - CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveRefsAndArgs, - GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveRefsAndArgs)); - CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveRefsOnly, - GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveRefsOnly)); - CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveAllCalleeSaves, - GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveAllCalleeSaves)); - CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveEverything, - GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveEverything)); - CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveEverythingForClinit, - GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveEverythingForClinit)); - CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveEverythingForSuspendCheck, - GetCalleeSaveReturnPcOffset(kRuntimeISA, - CalleeSaveType::kSaveEverythingForSuspendCheck)); + CheckPCOffset( + kRuntimeISA, + CalleeSaveType::kSaveRefsAndArgs, + RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveRefsAndArgs)); + CheckPCOffset( + kRuntimeISA, + CalleeSaveType::kSaveRefsOnly, + RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveRefsOnly)); + CheckPCOffset( + kRuntimeISA, + CalleeSaveType::kSaveAllCalleeSaves, + RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveAllCalleeSaves)); + CheckPCOffset( + kRuntimeISA, + CalleeSaveType::kSaveEverything, + RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveEverything)); + CheckPCOffset( + kRuntimeISA, + CalleeSaveType::kSaveEverythingForClinit, + RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveEverythingForClinit)); + CheckPCOffset( + kRuntimeISA, + CalleeSaveType::kSaveEverythingForSuspendCheck, + RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveEverythingForSuspendCheck)); } } // namespace art diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index 1fdf439d3f090d616e2b8ead960401b437afd86b..c3cd793780e2ae5bda5bc03ba180fd23a38412c3 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -168,7 +168,9 @@ class EntrypointsOrderTest : public CommonRuntimeTest { sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectInitialized, pAllocObjectWithChecks, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectWithChecks, pAllocStringFromBytes, + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectWithChecks, pAllocStringObject, + sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocStringObject, pAllocStringFromBytes, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocStringFromBytes, pAllocStringFromChars, sizeof(void*)); @@ -183,7 +185,9 @@ class EntrypointsOrderTest : public CommonRuntimeTest { sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeTypeAndVerifyAccess, pInitializeType, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeType, pResolveString, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeType, pResolveMethodHandle, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveMethodHandle, pResolveMethodType, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveMethodType, pResolveString, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveString, pSet8Instance, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet8Instance, pSet8Static, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet8Static, pSet16Instance, sizeof(void*)); @@ -285,8 +289,8 @@ class EntrypointsOrderTest : public CommonRuntimeTest { 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 68edfa8b7296843a20975bbfb1de51bb8f74145e..c138ce3f9e68e125fecb4a8397697939f0f60fc0 100644 --- a/runtime/exec_utils_test.cc +++ b/runtime/exec_utils_test.cc @@ -36,10 +36,9 @@ TEST_F(ExecUtilsTest, ExecSuccess) { 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. - EXPECT_TRUE(Exec(command, &error_msg)); - } + // Historical note: Running on Valgrind failed due to some memory + // that leaks in thread alternate signal stacks. + EXPECT_TRUE(Exec(command, &error_msg)); EXPECT_EQ(0U, error_msg.size()) << error_msg; } @@ -50,11 +49,10 @@ TEST_F(ExecUtilsTest, ExecError) { std::vector 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. - EXPECT_FALSE(Exec(command, &error_msg)); - EXPECT_FALSE(error_msg.empty()); - } + // Historical note: Running on Valgrind failed due to some memory + // that leaks in thread alternate signal stacks. + EXPECT_FALSE(Exec(command, &error_msg)); + EXPECT_FALSE(error_msg.empty()); } TEST_F(ExecUtilsTest, EnvSnapshotAdditionsAreNotVisible) { @@ -72,11 +70,10 @@ TEST_F(ExecUtilsTest, EnvSnapshotAdditionsAreNotVisible) { } 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. - EXPECT_FALSE(Exec(command, &error_msg)); - EXPECT_NE(0U, error_msg.size()) << error_msg; - } + // Historical note: Running on Valgrind failed due to some memory + // that leaks in thread alternate signal stacks. + EXPECT_FALSE(Exec(command, &error_msg)); + EXPECT_NE(0U, error_msg.size()) << error_msg; } TEST_F(ExecUtilsTest, EnvSnapshotDeletionsAreNotVisible) { @@ -97,11 +94,10 @@ TEST_F(ExecUtilsTest, EnvSnapshotDeletionsAreNotVisible) { } 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. - EXPECT_TRUE(Exec(command, &error_msg)); - EXPECT_EQ(0U, error_msg.size()) << error_msg; - } + // Historical note: Running on Valgrind failed due to some memory + // that leaks in thread alternate signal stacks. + EXPECT_TRUE(Exec(command, &error_msg)); + EXPECT_EQ(0U, error_msg.size()) << error_msg; // Restore the variable's value. EXPECT_EQ(setenv(kDeletedVariable, save_value, kOverwrite), 0); } diff --git a/runtime/gc/accounting/atomic_stack.h b/runtime/gc/accounting/atomic_stack.h index 6b103bfe1b7d4ea167fbb0b4eaea4af331599f30..e30fef4fc2566d2a9cd77a0049d774c3edb427c0 100644 --- a/runtime/gc/accounting/atomic_stack.h +++ b/runtime/gc/accounting/atomic_stack.h @@ -27,7 +27,7 @@ #include "base/atomic.h" #include "base/macros.h" -#include "mem_map.h" +#include "base/mem_map.h" #include "stack_reference.h" // This implements a double-ended queue (deque) with various flavors of PushBack operations, @@ -74,8 +74,8 @@ class AtomicStack { void Reset() { DCHECK(mem_map_.get() != nullptr); DCHECK(begin_ != nullptr); - front_index_.StoreRelaxed(0); - back_index_.StoreRelaxed(0); + front_index_.store(0, std::memory_order_relaxed); + back_index_.store(0, std::memory_order_relaxed); debug_is_sorted_ = true; mem_map_->MadviseDontNeedAndZero(); } @@ -103,7 +103,7 @@ class AtomicStack { int32_t index; int32_t new_index; do { - index = back_index_.LoadRelaxed(); + index = back_index_.load(std::memory_order_relaxed); new_index = index + num_slots; if (UNLIKELY(static_cast(new_index) >= growth_limit_)) { // Stack overflow. @@ -134,31 +134,32 @@ class AtomicStack { if (kIsDebugBuild) { debug_is_sorted_ = false; } - const int32_t index = back_index_.LoadRelaxed(); + const int32_t index = back_index_.load(std::memory_order_relaxed); DCHECK_LT(static_cast(index), growth_limit_); - back_index_.StoreRelaxed(index + 1); + back_index_.store(index + 1, std::memory_order_relaxed); begin_[index].Assign(value); } T* PopBack() REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK_GT(back_index_.LoadRelaxed(), front_index_.LoadRelaxed()); + DCHECK_GT(back_index_.load(std::memory_order_relaxed), + front_index_.load(std::memory_order_relaxed)); // Decrement the back index non atomically. - back_index_.StoreRelaxed(back_index_.LoadRelaxed() - 1); - return begin_[back_index_.LoadRelaxed()].AsMirrorPtr(); + back_index_.store(back_index_.load(std::memory_order_relaxed) - 1, std::memory_order_relaxed); + return begin_[back_index_.load(std::memory_order_relaxed)].AsMirrorPtr(); } // Take an item from the front of the stack. T PopFront() { - int32_t index = front_index_.LoadRelaxed(); - DCHECK_LT(index, back_index_.LoadRelaxed()); - front_index_.StoreRelaxed(index + 1); + int32_t index = front_index_.load(std::memory_order_relaxed); + DCHECK_LT(index, back_index_.load(std::memory_order_relaxed)); + front_index_.store(index + 1, std::memory_order_relaxed); return begin_[index]; } // Pop a number of elements. void PopBackCount(int32_t n) { DCHECK_GE(Size(), static_cast(n)); - back_index_.StoreRelaxed(back_index_.LoadRelaxed() - n); + back_index_.store(back_index_.load(std::memory_order_relaxed) - n, std::memory_order_relaxed); } bool IsEmpty() const { @@ -170,15 +171,17 @@ class AtomicStack { } size_t Size() const { - DCHECK_LE(front_index_.LoadRelaxed(), back_index_.LoadRelaxed()); - return back_index_.LoadRelaxed() - front_index_.LoadRelaxed(); + DCHECK_LE(front_index_.load(std::memory_order_relaxed), + back_index_.load(std::memory_order_relaxed)); + return + back_index_.load(std::memory_order_relaxed) - front_index_.load(std::memory_order_relaxed); } StackReference* Begin() const { - return begin_ + front_index_.LoadRelaxed(); + return begin_ + front_index_.load(std::memory_order_relaxed); } StackReference* End() const { - return begin_ + back_index_.LoadRelaxed(); + return begin_ + back_index_.load(std::memory_order_relaxed); } size_t Capacity() const { @@ -193,11 +196,11 @@ class AtomicStack { } void Sort() { - int32_t start_back_index = back_index_.LoadRelaxed(); - int32_t start_front_index = front_index_.LoadRelaxed(); + int32_t start_back_index = back_index_.load(std::memory_order_relaxed); + int32_t start_front_index = front_index_.load(std::memory_order_relaxed); std::sort(Begin(), End(), ObjectComparator()); - CHECK_EQ(start_back_index, back_index_.LoadRelaxed()); - CHECK_EQ(start_front_index, front_index_.LoadRelaxed()); + CHECK_EQ(start_back_index, back_index_.load(std::memory_order_relaxed)); + CHECK_EQ(start_front_index, front_index_.load(std::memory_order_relaxed)); if (kIsDebugBuild) { debug_is_sorted_ = true; } @@ -236,7 +239,7 @@ class AtomicStack { } int32_t index; do { - index = back_index_.LoadRelaxed(); + index = back_index_.load(std::memory_order_relaxed); if (UNLIKELY(static_cast(index) >= limit)) { // Stack overflow. return false; diff --git a/runtime/gc/accounting/bitmap-inl.h b/runtime/gc/accounting/bitmap-inl.h index a71b212af3180a522b300b339090258c11eacb94..a4273e5ff6a59977b09ee2b90d7df476f1b26099 100644 --- a/runtime/gc/accounting/bitmap-inl.h +++ b/runtime/gc/accounting/bitmap-inl.h @@ -37,7 +37,7 @@ inline bool Bitmap::AtomicTestAndSetBit(uintptr_t bit_index) { auto* atomic_entry = reinterpret_cast*>(&bitmap_begin_[word_index]); uintptr_t old_word; do { - old_word = atomic_entry->LoadRelaxed(); + old_word = atomic_entry->load(std::memory_order_relaxed); // Fast path: The bit is already set. if ((old_word & word_mask) != 0) { DCHECK(TestBit(bit_index)); diff --git a/runtime/gc/accounting/bitmap.cc b/runtime/gc/accounting/bitmap.cc index e5353807a9cb2b4cb77723c8403b40a9dbe678f5..d45a0cc018e6a48ca3f0ebff675d71a30987fca7 100644 --- a/runtime/gc/accounting/bitmap.cc +++ b/runtime/gc/accounting/bitmap.cc @@ -19,9 +19,9 @@ #include // For the PROT_* and MAP_* constants. #include "base/bit_utils.h" +#include "base/mem_map.h" #include "card_table.h" #include "jit/jit_code_cache.h" -#include "mem_map.h" namespace art { namespace gc { diff --git a/runtime/gc/accounting/bitmap.h b/runtime/gc/accounting/bitmap.h index d039d88770486de8a01574a495c684a901d948c8..2d83a8ad2e82b31c4edc777dc4a56a751c9846c9 100644 --- a/runtime/gc/accounting/bitmap.h +++ b/runtime/gc/accounting/bitmap.h @@ -23,8 +23,8 @@ #include #include +#include "base/globals.h" #include "base/mutex.h" -#include "globals.h" namespace art { diff --git a/runtime/gc/accounting/card_table-inl.h b/runtime/gc/accounting/card_table-inl.h index 14f5d0e1c601cfa64a8ae6eee28afc2791365182..357a4985b688072d4be4e6eb606045bb27f92d59 100644 --- a/runtime/gc/accounting/card_table-inl.h +++ b/runtime/gc/accounting/card_table-inl.h @@ -23,7 +23,7 @@ #include "base/atomic.h" #include "base/bit_utils.h" -#include "mem_map.h" +#include "base/mem_map.h" #include "space_bitmap.h" namespace art { @@ -43,7 +43,7 @@ static inline bool byte_cas(uint8_t old_value, uint8_t new_value, uint8_t* addre Atomic* word_atomic = reinterpret_cast*>(address); // Word with the byte we are trying to cas cleared. - const uintptr_t cur_word = word_atomic->LoadRelaxed() & + const uintptr_t cur_word = word_atomic->load(std::memory_order_relaxed) & ~(static_cast(0xFF) << shift_in_bits); const uintptr_t old_word = cur_word | (static_cast(old_value) << shift_in_bits); const uintptr_t new_word = cur_word | (static_cast(new_value) << shift_in_bits); diff --git a/runtime/gc/accounting/card_table.cc b/runtime/gc/accounting/card_table.cc index 4eea607c393b2b1262073cfbe2e6d386012a9007..c7f936fb116ad2bca3d54fe2940405139356a661 100644 --- a/runtime/gc/accounting/card_table.cc +++ b/runtime/gc/accounting/card_table.cc @@ -18,13 +18,13 @@ #include +#include "base/mem_map.h" #include "base/systrace.h" #include "base/utils.h" #include "card_table-inl.h" #include "gc/heap.h" #include "gc/space/space.h" #include "heap_bitmap.h" -#include "mem_map.h" #include "runtime.h" namespace art { diff --git a/runtime/gc/accounting/card_table.h b/runtime/gc/accounting/card_table.h index 766c976c989bb380a4110308519375a16de34af4..f3548f7ce5f6f0358ad6c52d619bd8bd52f5268d 100644 --- a/runtime/gc/accounting/card_table.h +++ b/runtime/gc/accounting/card_table.h @@ -19,8 +19,8 @@ #include +#include "base/globals.h" #include "base/mutex.h" -#include "globals.h" namespace art { diff --git a/runtime/gc/accounting/mod_union_table.h b/runtime/gc/accounting/mod_union_table.h index 766e0f5d333af56ca8359371f89b9f0df95b0332..7a3c06a28104249ec61852bc1f18ab8b06c1e72a 100644 --- a/runtime/gc/accounting/mod_union_table.h +++ b/runtime/gc/accounting/mod_union_table.h @@ -18,11 +18,11 @@ #define ART_RUNTIME_GC_ACCOUNTING_MOD_UNION_TABLE_H_ #include "base/allocator.h" +#include "base/globals.h" #include "base/safe_map.h" #include "base/tracking_safe_map.h" #include "bitmap.h" #include "card_table.h" -#include "globals.h" #include "mirror/object_reference.h" #include diff --git a/runtime/gc/accounting/mod_union_table_test.cc b/runtime/gc/accounting/mod_union_table_test.cc index e5b8ea5609042e6237aa09021f8c358999eb9d48..d59ff71676d54657a81b02362fec33e217761fd1 100644 --- a/runtime/gc/accounting/mod_union_table_test.cc +++ b/runtime/gc/accounting/mod_union_table_test.cc @@ -17,6 +17,7 @@ #include "mod_union_table-inl.h" #include "class_linker-inl.h" +#include "class_root.h" #include "common_runtime_test.h" #include "gc/space/space-inl.h" #include "mirror/array-inl.h" @@ -70,8 +71,7 @@ class ModUnionTableTest : public CommonRuntimeTest { mirror::Class* GetObjectArrayClass(Thread* self, space::ContinuousMemMapAllocSpace* space) REQUIRES_SHARED(Locks::mutator_lock_) { if (java_lang_object_array_ == nullptr) { - java_lang_object_array_ = - Runtime::Current()->GetClassLinker()->GetClassRoot(ClassLinker::kObjectArrayClass); + java_lang_object_array_ = GetClassRoot>().Ptr(); // Since the test doesn't have an image, the class of the object array keeps cards live // inside the card cache mod-union table and causes the check // ASSERT_FALSE(table->ContainsCardFor(reinterpret_cast(obj3))); diff --git a/runtime/gc/accounting/read_barrier_table.h b/runtime/gc/accounting/read_barrier_table.h index 775746f68dda7f0f052a5c0454f6fd0b0c430fa9..4b5a8c61c172e5a5668c450f649016f1549a5344 100644 --- a/runtime/gc/accounting/read_barrier_table.h +++ b/runtime/gc/accounting/read_barrier_table.h @@ -20,10 +20,10 @@ #include // For the PROT_* and MAP_* constants. #include "base/bit_utils.h" +#include "base/globals.h" +#include "base/mem_map.h" #include "base/mutex.h" #include "gc/space/space.h" -#include "globals.h" -#include "mem_map.h" namespace art { namespace gc { diff --git a/runtime/gc/accounting/space_bitmap-inl.h b/runtime/gc/accounting/space_bitmap-inl.h index 384e3c2f4c263a542ceaf32b49d4b5d9b42f8209..d460e000752e4e7d7d5a117e0c0efaae1c2dc3ce 100644 --- a/runtime/gc/accounting/space_bitmap-inl.h +++ b/runtime/gc/accounting/space_bitmap-inl.h @@ -41,7 +41,7 @@ inline bool SpaceBitmap::AtomicTestAndSet(const mirror::Object* obj) DCHECK_LT(index, bitmap_size_ / sizeof(intptr_t)) << " bitmap_size_ = " << bitmap_size_; uintptr_t old_word; do { - old_word = atomic_entry->LoadRelaxed(); + old_word = atomic_entry->load(std::memory_order_relaxed); // Fast path: The bit is already set. if ((old_word & mask) != 0) { DCHECK(Test(obj)); @@ -59,7 +59,8 @@ inline bool SpaceBitmap::Test(const mirror::Object* obj) const { DCHECK(bitmap_begin_ != nullptr); DCHECK_GE(addr, heap_begin_); const uintptr_t offset = addr - heap_begin_; - return (bitmap_begin_[OffsetToIndex(offset)].LoadRelaxed() & OffsetToMask(offset)) != 0; + size_t index = OffsetToIndex(offset); + return (bitmap_begin_[index].load(std::memory_order_relaxed) & OffsetToMask(offset)) != 0; } template @@ -119,7 +120,7 @@ inline void SpaceBitmap::VisitMarkedRange(uintptr_t visit_begin, // Traverse the middle, full part. for (size_t i = index_start + 1; i < index_end; ++i) { - uintptr_t w = bitmap_begin_[i].LoadRelaxed(); + uintptr_t w = bitmap_begin_[i].load(std::memory_order_relaxed); if (w != 0) { const uintptr_t ptr_base = IndexToOffset(i) + heap_begin_; // Iterate on the bits set in word `w`, from the least to the most significant bit. @@ -168,7 +169,7 @@ void SpaceBitmap::Walk(Visitor&& visitor) { uintptr_t end = OffsetToIndex(HeapLimit() - heap_begin_ - 1); Atomic* bitmap_begin = bitmap_begin_; for (uintptr_t i = 0; i <= end; ++i) { - uintptr_t w = bitmap_begin[i].LoadRelaxed(); + uintptr_t w = bitmap_begin[i].load(std::memory_order_relaxed); if (w != 0) { uintptr_t ptr_base = IndexToOffset(i) + heap_begin_; do { @@ -192,7 +193,7 @@ inline bool SpaceBitmap::Modify(const mirror::Object* obj) { const uintptr_t mask = OffsetToMask(offset); DCHECK_LT(index, bitmap_size_ / sizeof(intptr_t)) << " bitmap_size_ = " << bitmap_size_; Atomic* atomic_entry = &bitmap_begin_[index]; - uintptr_t old_word = atomic_entry->LoadRelaxed(); + uintptr_t old_word = atomic_entry->load(std::memory_order_relaxed); if (kSetBit) { // Check the bit before setting the word incase we are trying to mark a read only bitmap // like an image space bitmap. This bitmap is mapped as read only and will fault if we @@ -200,10 +201,10 @@ inline bool SpaceBitmap::Modify(const mirror::Object* obj) { // occur if we check before setting the bit. This also prevents dirty pages that would // occur if the bitmap was read write and we did not check the bit. if ((old_word & mask) == 0) { - atomic_entry->StoreRelaxed(old_word | mask); + atomic_entry->store(old_word | mask, std::memory_order_relaxed); } } else { - atomic_entry->StoreRelaxed(old_word & ~mask); + atomic_entry->store(old_word & ~mask, std::memory_order_relaxed); } DCHECK_EQ(Test(obj), kSetBit); return (old_word & mask) != 0; diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc index 0247564a8cd0457b6be19e7b1ef74301a60085ed..ced62cd249847b32aa3dc95cf3a018009fd9973d 100644 --- a/runtime/gc/accounting/space_bitmap.cc +++ b/runtime/gc/accounting/space_bitmap.cc @@ -19,8 +19,8 @@ #include "android-base/stringprintf.h" #include "art_field-inl.h" +#include "base/mem_map.h" #include "dex/dex_file-inl.h" -#include "mem_map.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array.h" @@ -145,7 +145,7 @@ void SpaceBitmap::CopyFrom(SpaceBitmap* source_bitmap) { Atomic* const src = source_bitmap->Begin(); Atomic* const dest = Begin(); for (size_t i = 0; i < count; ++i) { - dest[i].StoreRelaxed(src[i].LoadRelaxed()); + dest[i].store(src[i].load(std::memory_order_relaxed), std::memory_order_relaxed); } } @@ -184,7 +184,8 @@ void SpaceBitmap::SweepWalk(const SpaceBitmap& live_bitm Atomic* live = live_bitmap.bitmap_begin_; Atomic* mark = mark_bitmap.bitmap_begin_; for (size_t i = start; i <= end; i++) { - uintptr_t garbage = live[i].LoadRelaxed() & ~mark[i].LoadRelaxed(); + uintptr_t garbage = + live[i].load(std::memory_order_relaxed) & ~mark[i].load(std::memory_order_relaxed); if (UNLIKELY(garbage != 0)) { uintptr_t ptr_base = IndexToOffset(i) + live_bitmap.heap_begin_; do { diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h index 437aecc2b16ea2b90bf64a6bf49ce7004334bc6a..1237f6e8b5305993e22150c1d911d5ecd0adc478 100644 --- a/runtime/gc/accounting/space_bitmap.h +++ b/runtime/gc/accounting/space_bitmap.h @@ -23,8 +23,8 @@ #include #include +#include "base/globals.h" #include "base/mutex.h" -#include "globals.h" namespace art { diff --git a/runtime/gc/accounting/space_bitmap_test.cc b/runtime/gc/accounting/space_bitmap_test.cc index 1ca3fd6d12115144b0a8f8c3073fee39868a6118..22529b83c21ab63212835f784df4da652ca4db74 100644 --- a/runtime/gc/accounting/space_bitmap_test.cc +++ b/runtime/gc/accounting/space_bitmap_test.cc @@ -19,9 +19,9 @@ #include #include +#include "base/globals.h" #include "base/mutex.h" #include "common_runtime_test.h" -#include "globals.h" #include "space_bitmap-inl.h" namespace art { diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc index 928abe873ebc20bf608316373386a7d6600dd684..a4095d815fde9ae67c68230f1637543274b3b138 100644 --- a/runtime/gc/allocator/rosalloc.cc +++ b/runtime/gc/allocator/rosalloc.cc @@ -25,9 +25,9 @@ #include "base/logging.h" // For VLOG #include "base/memory_tool.h" +#include "base/mem_map.h" #include "base/mutex-inl.h" #include "gc/space/memory_tool_settings.h" -#include "mem_map.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object.h" diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h index 6e5cf0ede8c3c82cb37516c67605ce7b8a70ed13..30213d55c55adf003209c6cc71e499afc51ecc9d 100644 --- a/runtime/gc/allocator/rosalloc.h +++ b/runtime/gc/allocator/rosalloc.h @@ -30,8 +30,8 @@ #include "base/allocator.h" #include "base/bit_utils.h" +#include "base/globals.h" #include "base/mutex.h" -#include "globals.h" #include "thread.h" namespace art { @@ -625,7 +625,7 @@ class RosAlloc { // 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 @@ class RosAlloc { // greater than or equal to this value, release pages. const size_t page_release_size_threshold_; - // Whether this allocator is running under Valgrind. + // Whether this allocator is running on a memory tool. bool is_running_on_memory_tool_; // The base address of the memory region that's managed by this allocator. diff --git a/runtime/gc/collector/concurrent_copying-inl.h b/runtime/gc/collector/concurrent_copying-inl.h index 8ba2ac1233b3b51b4816f27d864733867d3f82a0..36fefbdbc3c692ae34f5032249b7d86578c99e8a 100644 --- a/runtime/gc/collector/concurrent_copying-inl.h +++ b/runtime/gc/collector/concurrent_copying-inl.h @@ -32,7 +32,9 @@ namespace gc { namespace collector { inline mirror::Object* ConcurrentCopying::MarkUnevacFromSpaceRegion( - mirror::Object* ref, accounting::ContinuousSpaceBitmap* bitmap) { + Thread* const self, + mirror::Object* ref, + accounting::ContinuousSpaceBitmap* bitmap) { // For the Baker-style RB, in a rare case, we could incorrectly change the object from white // to gray even though the object has already been marked through. This happens if a mutator // thread gets preempted before the AtomicSetReadBarrierState below, GC marks through the @@ -63,35 +65,36 @@ inline mirror::Object* ConcurrentCopying::MarkUnevacFromSpaceRegion( if (kUseBakerReadBarrier) { DCHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::GrayState()); } - PushOntoMarkStack(ref); + PushOntoMarkStack(self, ref); } return ref; } template -inline mirror::Object* ConcurrentCopying::MarkImmuneSpace(mirror::Object* ref) { +inline mirror::Object* ConcurrentCopying::MarkImmuneSpace(Thread* const self, + mirror::Object* ref) { if (kUseBakerReadBarrier) { // The GC-running thread doesn't (need to) gray immune objects except when updating thread roots // in the thread flip on behalf of suspended threads (when gc_grays_immune_objects_ is // true). Also, a mutator doesn't (need to) gray an immune object after GC has updated all // immune space objects (when updated_all_immune_objects_ is true). if (kIsDebugBuild) { - if (Thread::Current() == thread_running_gc_) { + if (self == thread_running_gc_) { DCHECK(!kGrayImmuneObject || - updated_all_immune_objects_.LoadRelaxed() || + updated_all_immune_objects_.load(std::memory_order_relaxed) || gc_grays_immune_objects_); } else { DCHECK(kGrayImmuneObject); } } - if (!kGrayImmuneObject || updated_all_immune_objects_.LoadRelaxed()) { + if (!kGrayImmuneObject || updated_all_immune_objects_.load(std::memory_order_relaxed)) { return ref; } // This may or may not succeed, which is ok because the object may already be gray. bool success = ref->AtomicSetReadBarrierState(/* expected_rb_state */ ReadBarrier::WhiteState(), /* rb_state */ ReadBarrier::GrayState()); if (success) { - MutexLock mu(Thread::Current(), immune_gray_stack_lock_); + MutexLock mu(self, immune_gray_stack_lock_); immune_gray_stack_.push_back(ref); } } @@ -99,7 +102,8 @@ inline mirror::Object* ConcurrentCopying::MarkImmuneSpace(mirror::Object* ref) { } template -inline mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref, +inline mirror::Object* ConcurrentCopying::Mark(Thread* const self, + mirror::Object* from_ref, mirror::Object* holder, MemberOffset offset) { if (from_ref == nullptr) { @@ -108,7 +112,7 @@ inline mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref, DCHECK(heap_->collector_type_ == kCollectorTypeCC); if (kFromGCThread) { DCHECK(is_active_); - DCHECK_EQ(Thread::Current(), thread_running_gc_); + DCHECK_EQ(self, thread_running_gc_); } else if (UNLIKELY(kUseBakerReadBarrier && !is_active_)) { // In the lock word forward address state, the read barrier bits // in the lock word are part of the stored forwarding address and @@ -134,7 +138,7 @@ inline mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref, mirror::Object* to_ref = GetFwdPtr(from_ref); if (to_ref == nullptr) { // It isn't marked yet. Mark it by copying it to the to-space. - to_ref = Copy(from_ref, holder, offset); + to_ref = Copy(self, from_ref, holder, offset); } // The copy should either be in a to-space region, or in the // non-moving space, if it could not fit in a to-space region. @@ -143,7 +147,7 @@ inline mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref, return to_ref; } case space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace: - return MarkUnevacFromSpaceRegion(from_ref, region_space_bitmap_); + return MarkUnevacFromSpaceRegion(self, from_ref, region_space_bitmap_); default: // The reference is in an unused region. LOG(FATAL_WITHOUT_ABORT) << DumpHeapReference(holder, offset, from_ref); @@ -153,24 +157,25 @@ inline mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref, } } else { if (immune_spaces_.ContainsObject(from_ref)) { - return MarkImmuneSpace(from_ref); + return MarkImmuneSpace(self, from_ref); } else { - return MarkNonMoving(from_ref, holder, offset); + return MarkNonMoving(self, from_ref, holder, offset); } } } inline mirror::Object* ConcurrentCopying::MarkFromReadBarrier(mirror::Object* from_ref) { mirror::Object* ret; + Thread* const self = Thread::Current(); // We can get here before marking starts since we gray immune objects before the marking phase. - if (from_ref == nullptr || !Thread::Current()->GetIsGcMarking()) { + if (from_ref == nullptr || !self->GetIsGcMarking()) { return from_ref; } // TODO: Consider removing this check when we are done investigating slow paths. b/30162165 if (UNLIKELY(mark_from_read_barrier_measurements_)) { - ret = MarkFromReadBarrierWithMeasurements(from_ref); + ret = MarkFromReadBarrierWithMeasurements(self, from_ref); } else { - ret = Mark(from_ref); + ret = Mark(self, from_ref); } // Only set the mark bit for baker barrier. if (kUseBakerReadBarrier && LIKELY(!rb_mark_bit_stack_full_ && ret->AtomicSetMarkBit(0, 1))) { diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index 81e86d4b8df96b4589d5875b1958f31461e2dd8f..edaa043ce6425bf2150cd3663221949144cad2d3 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -23,6 +23,7 @@ #include "base/quasi_atomic.h" #include "base/stl_util.h" #include "base/systrace.h" +#include "class_root.h" #include "debugger.h" #include "gc/accounting/atomic_stack.h" #include "gc/accounting/heap_bitmap-inl.h" @@ -128,13 +129,14 @@ ConcurrentCopying::ConcurrentCopying(Heap* heap, void ConcurrentCopying::MarkHeapReference(mirror::HeapReference* field, bool do_atomic_update) { + Thread* const self = Thread::Current(); if (UNLIKELY(do_atomic_update)) { // Used to mark the referent in DelayReferenceReferent in transaction mode. mirror::Object* from_ref = field->AsMirrorPtr(); if (from_ref == nullptr) { return; } - mirror::Object* to_ref = Mark(from_ref); + mirror::Object* to_ref = Mark(self, from_ref); if (from_ref != to_ref) { do { if (field->AsMirrorPtr() != from_ref) { @@ -147,7 +149,7 @@ void ConcurrentCopying::MarkHeapReference(mirror::HeapReference* // Used for preserving soft references, should be OK to not have a CAS here since there should be // no other threads which can trigger read barriers on the same referent during reference // processing. - field->Assign(Mark(field->AsMirrorPtr())); + field->Assign(Mark(self, field->AsMirrorPtr())); } } @@ -291,14 +293,16 @@ void ConcurrentCopying::InitializePhase() { rb_mark_bit_stack_full_ = false; mark_from_read_barrier_measurements_ = measure_read_barrier_slow_path_; if (measure_read_barrier_slow_path_) { - rb_slow_path_ns_.StoreRelaxed(0); - rb_slow_path_count_.StoreRelaxed(0); - rb_slow_path_count_gc_.StoreRelaxed(0); + rb_slow_path_ns_.store(0, std::memory_order_relaxed); + rb_slow_path_count_.store(0, std::memory_order_relaxed); + rb_slow_path_count_gc_.store(0, std::memory_order_relaxed); } immune_spaces_.Reset(); - bytes_moved_.StoreRelaxed(0); - objects_moved_.StoreRelaxed(0); + bytes_moved_.store(0, std::memory_order_relaxed); + objects_moved_.store(0, std::memory_order_relaxed); + bytes_moved_gc_thread_ = 0; + objects_moved_gc_thread_ = 0; GcCause gc_cause = GetCurrentIteration()->GetGcCause(); if (gc_cause == kGcCauseExplicit || gc_cause == kGcCauseCollectorTransition || @@ -308,7 +312,7 @@ void ConcurrentCopying::InitializePhase() { force_evacuate_all_ = false; } if (kUseBakerReadBarrier) { - updated_all_immune_objects_.StoreRelaxed(false); + updated_all_immune_objects_.store(false, std::memory_order_relaxed); // GC may gray immune objects in the thread flip. gc_grays_immune_objects_ = true; if (kIsDebugBuild) { @@ -350,7 +354,7 @@ class ConcurrentCopying::ThreadFlipVisitor : public Closure, public RootVisitor concurrent_copying_->region_space_->RevokeThreadLocalBuffers(thread); reinterpret_cast*>( &concurrent_copying_->from_space_num_objects_at_first_pause_)-> - FetchAndAddSequentiallyConsistent(thread_local_objects); + fetch_add(thread_local_objects, std::memory_order_seq_cst); } else { concurrent_copying_->region_space_->RevokeThreadLocalBuffers(thread); } @@ -369,11 +373,12 @@ class ConcurrentCopying::ThreadFlipVisitor : public Closure, public RootVisitor size_t count, const RootInfo& info ATTRIBUTE_UNUSED) REQUIRES_SHARED(Locks::mutator_lock_) { + Thread* self = Thread::Current(); for (size_t i = 0; i < count; ++i) { mirror::Object** root = roots[i]; mirror::Object* ref = *root; if (ref != nullptr) { - mirror::Object* to_ref = concurrent_copying_->Mark(ref); + mirror::Object* to_ref = concurrent_copying_->Mark(self, ref); if (to_ref != ref) { *root = to_ref; } @@ -385,11 +390,12 @@ class ConcurrentCopying::ThreadFlipVisitor : public Closure, public RootVisitor size_t count, const RootInfo& info ATTRIBUTE_UNUSED) REQUIRES_SHARED(Locks::mutator_lock_) { + Thread* self = Thread::Current(); for (size_t i = 0; i < count; ++i) { mirror::CompressedReference* const root = roots[i]; if (!root->IsNull()) { mirror::Object* ref = root->AsMirrorPtr(); - mirror::Object* to_ref = concurrent_copying_->Mark(ref); + mirror::Object* to_ref = concurrent_copying_->Mark(self, ref); if (to_ref != ref) { root->Assign(to_ref); } @@ -430,7 +436,8 @@ class ConcurrentCopying::FlipCallback : public Closure { cc->from_space_num_bytes_at_first_pause_ = cc->region_space_->GetBytesAllocated(); } cc->is_marking_ = true; - cc->mark_stack_mode_.StoreRelaxed(ConcurrentCopying::kMarkStackModeThreadLocal); + cc->mark_stack_mode_.store(ConcurrentCopying::kMarkStackModeThreadLocal, + std::memory_order_relaxed); if (kIsDebugBuild) { cc->region_space_->AssertAllRegionLiveBytesZeroOrCleared(); } @@ -450,7 +457,7 @@ class ConcurrentCopying::FlipCallback : public Closure { // This is safe since single threaded behavior should mean FillDummyObject does not // happen when java_lang_Object_ is null. if (WellKnownClasses::java_lang_Object != nullptr) { - cc->java_lang_Object_ = down_cast(cc->Mark( + cc->java_lang_Object_ = down_cast(cc->Mark(thread, WellKnownClasses::ToClass(WellKnownClasses::java_lang_Object).Ptr())); } else { cc->java_lang_Object_ = nullptr; @@ -728,7 +735,7 @@ void ConcurrentCopying::GrayAllNewlyDirtyImmuneObjects() { } // Since all of the objects that may point to other spaces are gray, we can avoid all the read // barriers in the immune spaces. - updated_all_immune_objects_.StoreRelaxed(true); + updated_all_immune_objects_.store(true, std::memory_order_relaxed); } void ConcurrentCopying::SwapStacks() { @@ -816,7 +823,7 @@ void ConcurrentCopying::MarkingPhase() { if (kUseBakerReadBarrier) { // This release fence makes the field updates in the above loop visible before allowing mutator // getting access to immune objects without graying it first. - updated_all_immune_objects_.StoreRelease(true); + updated_all_immune_objects_.store(true, std::memory_order_release); // Now whiten immune objects concurrently accessed and grayed by mutators. We can't do this in // the above loop because we would incorrectly disable the read barrier by whitening an object // which may point to an unscanned, white object, breaking the to-space invariant. @@ -1018,14 +1025,14 @@ void ConcurrentCopying::DisableMarking() { heap_->rb_table_->ClearAll(); DCHECK(heap_->rb_table_->IsAllCleared()); } - is_mark_stack_push_disallowed_.StoreSequentiallyConsistent(1); - mark_stack_mode_.StoreSequentiallyConsistent(kMarkStackModeOff); + is_mark_stack_push_disallowed_.store(1, std::memory_order_seq_cst); + mark_stack_mode_.store(kMarkStackModeOff, std::memory_order_seq_cst); } -void ConcurrentCopying::PushOntoFalseGrayStack(mirror::Object* ref) { +void ConcurrentCopying::PushOntoFalseGrayStack(Thread* const self, mirror::Object* ref) { CHECK(kUseBakerReadBarrier); DCHECK(ref != nullptr); - MutexLock mu(Thread::Current(), mark_stack_lock_); + MutexLock mu(self, mark_stack_lock_); false_gray_stack_.push_back(ref); } @@ -1068,12 +1075,11 @@ void ConcurrentCopying::ExpandGcMarkStack() { DCHECK(!gc_mark_stack_->IsFull()); } -void ConcurrentCopying::PushOntoMarkStack(mirror::Object* to_ref) { - CHECK_EQ(is_mark_stack_push_disallowed_.LoadRelaxed(), 0) +void ConcurrentCopying::PushOntoMarkStack(Thread* const self, mirror::Object* to_ref) { + CHECK_EQ(is_mark_stack_push_disallowed_.load(std::memory_order_relaxed), 0) << " " << to_ref << " " << mirror::Object::PrettyTypeOf(to_ref); - Thread* self = Thread::Current(); // TODO: pass self as an argument from call sites? CHECK(thread_running_gc_ != nullptr); - MarkStackMode mark_stack_mode = mark_stack_mode_.LoadRelaxed(); + MarkStackMode mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed); if (LIKELY(mark_stack_mode == kMarkStackModeThreadLocal)) { if (LIKELY(self == thread_running_gc_)) { // If GC-running thread, use the GC mark stack instead of a thread-local mark stack. @@ -1407,12 +1413,12 @@ void ConcurrentCopying::ProcessMarkStack() { } bool ConcurrentCopying::ProcessMarkStackOnce() { - Thread* self = Thread::Current(); - CHECK(thread_running_gc_ != nullptr); - CHECK(self == thread_running_gc_); - CHECK(self->GetThreadLocalMarkStack() == nullptr); + DCHECK(thread_running_gc_ != nullptr); + Thread* const self = Thread::Current(); + DCHECK(self == thread_running_gc_); + DCHECK(thread_running_gc_->GetThreadLocalMarkStack() == nullptr); size_t count = 0; - MarkStackMode mark_stack_mode = mark_stack_mode_.LoadRelaxed(); + MarkStackMode mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed); if (mark_stack_mode == kMarkStackModeThreadLocal) { // Process the thread-local mark stacks and the GC mark stack. count += ProcessThreadLocalMarkStacks(/* disable_weak_ref_access */ false, @@ -1430,14 +1436,14 @@ bool ConcurrentCopying::ProcessMarkStackOnce() { IssueEmptyCheckpoint(); // Process the shared GC mark stack with a lock. { - MutexLock mu(self, mark_stack_lock_); + MutexLock mu(thread_running_gc_, mark_stack_lock_); CHECK(revoked_mark_stacks_.empty()); } while (true) { std::vector refs; { // Copy refs with lock. Note the number of refs should be small. - MutexLock mu(self, mark_stack_lock_); + MutexLock mu(thread_running_gc_, mark_stack_lock_); if (gc_mark_stack_->IsEmpty()) { break; } @@ -1456,7 +1462,7 @@ bool ConcurrentCopying::ProcessMarkStackOnce() { CHECK_EQ(static_cast(mark_stack_mode), static_cast(kMarkStackModeGcExclusive)); { - MutexLock mu(self, mark_stack_lock_); + MutexLock mu(thread_running_gc_, mark_stack_lock_); CHECK(revoked_mark_stacks_.empty()); } // Process the GC mark stack in the exclusive mode. No need to take the lock. @@ -1479,7 +1485,7 @@ size_t ConcurrentCopying::ProcessThreadLocalMarkStacks(bool disable_weak_ref_acc size_t count = 0; std::vector*> mark_stacks; { - MutexLock mu(Thread::Current(), mark_stack_lock_); + MutexLock mu(thread_running_gc_, mark_stack_lock_); // Make a copy of the mark stack vector. mark_stacks = revoked_mark_stacks_; revoked_mark_stacks_.clear(); @@ -1491,7 +1497,7 @@ size_t ConcurrentCopying::ProcessThreadLocalMarkStacks(bool disable_weak_ref_acc ++count; } { - MutexLock mu(Thread::Current(), mark_stack_lock_); + MutexLock mu(thread_running_gc_, mark_stack_lock_); if (pooled_mark_stacks_.size() >= kMarkStackPoolSize) { // The pool has enough. Delete it. delete mark_stack; @@ -1545,7 +1551,7 @@ inline void ConcurrentCopying::ProcessMarkStackRef(mirror::Object* to_ref) { // above IsInToSpace() evaluates to true and we change the color from gray to white here in this // else block. if (kUseBakerReadBarrier) { - bool success = to_ref->AtomicSetReadBarrierState( + bool success = to_ref->AtomicSetReadBarrierState( ReadBarrier::GrayState(), ReadBarrier::WhiteState()); DCHECK(success) << "Must succeed as we won the race."; @@ -1594,13 +1600,13 @@ class ConcurrentCopying::DisableWeakRefAccessCallback : public Closure { void ConcurrentCopying::SwitchToSharedMarkStackMode() { Thread* self = Thread::Current(); - CHECK(thread_running_gc_ != nullptr); - CHECK_EQ(self, thread_running_gc_); - CHECK(self->GetThreadLocalMarkStack() == nullptr); - MarkStackMode before_mark_stack_mode = mark_stack_mode_.LoadRelaxed(); + DCHECK(thread_running_gc_ != nullptr); + DCHECK(self == thread_running_gc_); + DCHECK(thread_running_gc_->GetThreadLocalMarkStack() == nullptr); + MarkStackMode before_mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed); CHECK_EQ(static_cast(before_mark_stack_mode), static_cast(kMarkStackModeThreadLocal)); - mark_stack_mode_.StoreRelaxed(kMarkStackModeShared); + mark_stack_mode_.store(kMarkStackModeShared, std::memory_order_relaxed); DisableWeakRefAccessCallback dwrac(this); // Process the thread local mark stacks one last time after switching to the shared mark stack // mode and disable weak ref accesses. @@ -1612,13 +1618,13 @@ void ConcurrentCopying::SwitchToSharedMarkStackMode() { void ConcurrentCopying::SwitchToGcExclusiveMarkStackMode() { Thread* self = Thread::Current(); - CHECK(thread_running_gc_ != nullptr); - CHECK_EQ(self, thread_running_gc_); - CHECK(self->GetThreadLocalMarkStack() == nullptr); - MarkStackMode before_mark_stack_mode = mark_stack_mode_.LoadRelaxed(); + DCHECK(thread_running_gc_ != nullptr); + DCHECK(self == thread_running_gc_); + DCHECK(thread_running_gc_->GetThreadLocalMarkStack() == nullptr); + MarkStackMode before_mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed); CHECK_EQ(static_cast(before_mark_stack_mode), static_cast(kMarkStackModeShared)); - mark_stack_mode_.StoreRelaxed(kMarkStackModeGcExclusive); + mark_stack_mode_.store(kMarkStackModeGcExclusive, std::memory_order_relaxed); QuasiAtomic::ThreadFenceForConstructor(); if (kVerboseMode) { LOG(INFO) << "Switched to GC exclusive mark stack mode"; @@ -1627,14 +1633,14 @@ void ConcurrentCopying::SwitchToGcExclusiveMarkStackMode() { void ConcurrentCopying::CheckEmptyMarkStack() { Thread* self = Thread::Current(); - CHECK(thread_running_gc_ != nullptr); - CHECK_EQ(self, thread_running_gc_); - CHECK(self->GetThreadLocalMarkStack() == nullptr); - MarkStackMode mark_stack_mode = mark_stack_mode_.LoadRelaxed(); + DCHECK(thread_running_gc_ != nullptr); + DCHECK(self == thread_running_gc_); + DCHECK(thread_running_gc_->GetThreadLocalMarkStack() == nullptr); + MarkStackMode mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed); if (mark_stack_mode == kMarkStackModeThreadLocal) { // Thread-local mark stack mode. RevokeThreadLocalMarkStacks(false, nullptr); - MutexLock mu(Thread::Current(), mark_stack_lock_); + MutexLock mu(thread_running_gc_, mark_stack_lock_); if (!revoked_mark_stacks_.empty()) { for (accounting::AtomicStack* mark_stack : revoked_mark_stacks_) { while (!mark_stack->IsEmpty()) { @@ -1653,7 +1659,7 @@ void ConcurrentCopying::CheckEmptyMarkStack() { } } else { // Shared, GC-exclusive, or off. - MutexLock mu(Thread::Current(), mark_stack_lock_); + MutexLock mu(thread_running_gc_, mark_stack_lock_); CHECK(gc_mark_stack_->IsEmpty()); CHECK(revoked_mark_stacks_.empty()); } @@ -1738,9 +1744,9 @@ void ConcurrentCopying::ReclaimPhase() { } IssueEmptyCheckpoint(); // Disable the check. - is_mark_stack_push_disallowed_.StoreSequentiallyConsistent(0); + is_mark_stack_push_disallowed_.store(0, std::memory_order_seq_cst); if (kUseBakerReadBarrier) { - updated_all_immune_objects_.StoreSequentiallyConsistent(false); + updated_all_immune_objects_.store(false, std::memory_order_seq_cst); } CheckEmptyMarkStack(); } @@ -1753,10 +1759,10 @@ void ConcurrentCopying::ReclaimPhase() { const uint64_t from_objects = region_space_->GetObjectsAllocatedInFromSpace(); const uint64_t unevac_from_bytes = region_space_->GetBytesAllocatedInUnevacFromSpace(); const uint64_t unevac_from_objects = region_space_->GetObjectsAllocatedInUnevacFromSpace(); - uint64_t to_bytes = bytes_moved_.LoadSequentiallyConsistent(); - cumulative_bytes_moved_.FetchAndAddRelaxed(to_bytes); - uint64_t to_objects = objects_moved_.LoadSequentiallyConsistent(); - cumulative_objects_moved_.FetchAndAddRelaxed(to_objects); + uint64_t to_bytes = bytes_moved_.load(std::memory_order_seq_cst) + bytes_moved_gc_thread_; + cumulative_bytes_moved_.fetch_add(to_bytes, std::memory_order_relaxed); + uint64_t to_objects = objects_moved_.load(std::memory_order_seq_cst) + objects_moved_gc_thread_; + cumulative_objects_moved_.fetch_add(to_objects, std::memory_order_relaxed); if (kEnableFromSpaceAccountingCheck) { CHECK_EQ(from_space_num_objects_at_first_pause_, from_objects + unevac_from_objects); CHECK_EQ(from_space_num_bytes_at_first_pause_, from_bytes + unevac_from_bytes); @@ -1787,12 +1793,12 @@ void ConcurrentCopying::ReclaimPhase() { << " unevac_from_space size=" << region_space_->UnevacFromSpaceSize() << " to_space size=" << region_space_->ToSpaceSize(); LOG(INFO) << "(before) num_bytes_allocated=" - << heap_->num_bytes_allocated_.LoadSequentiallyConsistent(); + << heap_->num_bytes_allocated_.load(std::memory_order_seq_cst); } RecordFree(ObjectBytePair(freed_objects, freed_bytes)); if (kVerboseMode) { LOG(INFO) << "(after) num_bytes_allocated=" - << heap_->num_bytes_allocated_.LoadSequentiallyConsistent(); + << heap_->num_bytes_allocated_.load(std::memory_order_seq_cst); } } @@ -1816,7 +1822,7 @@ void ConcurrentCopying::ReclaimPhase() { std::string ConcurrentCopying::DumpReferenceInfo(mirror::Object* ref, const char* ref_name, - std::string indent) { + const char* indent) { std::ostringstream oss; oss << indent << heap_->GetVerification()->DumpObjectInfo(ref, ref_name) << '\n'; if (ref != nullptr) { @@ -1840,13 +1846,13 @@ std::string ConcurrentCopying::DumpHeapReference(mirror::Object* obj, MemberOffset offset, mirror::Object* ref) { std::ostringstream oss; - std::string indent = " "; - oss << indent << "Invalid reference: ref=" << ref + constexpr const char* kIndent = " "; + oss << kIndent << "Invalid reference: ref=" << ref << " referenced from: object=" << obj << " offset= " << offset << '\n'; // Information about `obj`. - oss << DumpReferenceInfo(obj, "obj", indent) << '\n'; + oss << DumpReferenceInfo(obj, "obj", kIndent) << '\n'; // Information about `ref`. - oss << DumpReferenceInfo(ref, "ref", indent); + oss << DumpReferenceInfo(ref, "ref", kIndent); return oss.str(); } @@ -1922,10 +1928,10 @@ class RootPrinter { std::string ConcurrentCopying::DumpGcRoot(mirror::Object* ref) { std::ostringstream oss; - std::string indent = " "; - oss << indent << "Invalid GC root: ref=" << ref << '\n'; + constexpr const char* kIndent = " "; + oss << kIndent << "Invalid GC root: ref=" << ref << '\n'; // Information about `ref`. - oss << DumpReferenceInfo(ref, "ref", indent); + oss << DumpReferenceInfo(ref, "ref", kIndent); return oss.str(); } @@ -2042,7 +2048,7 @@ void ConcurrentCopying::AssertToSpaceInvariantInNonMovingSpace(mirror::Object* o if (Thread::Current() == thread_running_gc_ && !gc_grays_immune_objects_) { return; } - bool updated_all_immune_objects = updated_all_immune_objects_.LoadSequentiallyConsistent(); + bool updated_all_immune_objects = updated_all_immune_objects_.load(std::memory_order_seq_cst); CHECK(updated_all_immune_objects || ref->GetReadBarrierState() == ReadBarrier::GrayState()) << "Unmarked immune space ref. obj=" << obj << " rb_state=" << (obj != nullptr ? obj->GetReadBarrierState() : 0U) @@ -2073,8 +2079,8 @@ void ConcurrentCopying::AssertToSpaceInvariantInNonMovingSpace(mirror::Object* o // Used to scan ref fields of an object. class ConcurrentCopying::RefFieldsVisitor { public: - explicit RefFieldsVisitor(ConcurrentCopying* collector) - : collector_(collector) {} + explicit RefFieldsVisitor(ConcurrentCopying* collector, Thread* const thread) + : collector_(collector), thread_(thread) {} void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */) const ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) @@ -2099,11 +2105,12 @@ class ConcurrentCopying::RefFieldsVisitor { void VisitRoot(mirror::CompressedReference* root) const ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) { - collector_->MarkRoot(root); + collector_->MarkRoot(thread_, root); } private: ConcurrentCopying* const collector_; + Thread* const thread_; }; inline void ConcurrentCopying::Scan(mirror::Object* to_ref) { @@ -2115,12 +2122,12 @@ inline void ConcurrentCopying::Scan(mirror::Object* to_ref) { } DCHECK(!region_space_->IsInFromSpace(to_ref)); DCHECK_EQ(Thread::Current(), thread_running_gc_); - RefFieldsVisitor visitor(this); + RefFieldsVisitor visitor(this, thread_running_gc_); // Disable the read barrier for a performance reason. to_ref->VisitReferences( visitor, visitor); if (kDisallowReadBarrierDuringScan && !Runtime::Current()->IsActiveTransaction()) { - Thread::Current()->ModifyDebugDisallowReadBarrier(-1); + thread_running_gc_->ModifyDebugDisallowReadBarrier(-1); } } @@ -2129,6 +2136,7 @@ inline void ConcurrentCopying::Process(mirror::Object* obj, MemberOffset offset) mirror::Object* ref = obj->GetFieldObject< mirror::Object, kVerifyNone, kWithoutReadBarrier, false>(offset); mirror::Object* to_ref = Mark( + thread_running_gc_, ref, /*holder*/ obj, offset); @@ -2145,19 +2153,22 @@ inline void ConcurrentCopying::Process(mirror::Object* obj, MemberOffset offset) break; } // Use release CAS to make sure threads reading the reference see contents of copied objects. - } while (!obj->CasFieldWeakReleaseObjectWithoutWriteBarrier( + } while (!obj->CasFieldObjectWithoutWriteBarrier( offset, expected_ref, - new_ref)); + new_ref, + CASMode::kWeak, + std::memory_order_release)); } // Process some roots. inline void ConcurrentCopying::VisitRoots( mirror::Object*** roots, size_t count, const RootInfo& info ATTRIBUTE_UNUSED) { + Thread* const self = Thread::Current(); for (size_t i = 0; i < count; ++i) { mirror::Object** root = roots[i]; mirror::Object* ref = *root; - mirror::Object* to_ref = Mark(ref); + mirror::Object* to_ref = Mark(self, ref); if (to_ref == ref) { continue; } @@ -2165,7 +2176,7 @@ inline void ConcurrentCopying::VisitRoots( mirror::Object* expected_ref = ref; mirror::Object* new_ref = to_ref; do { - if (expected_ref != addr->LoadRelaxed()) { + if (expected_ref != addr->load(std::memory_order_relaxed)) { // It was updated by the mutator. break; } @@ -2174,17 +2185,18 @@ inline void ConcurrentCopying::VisitRoots( } template -inline void ConcurrentCopying::MarkRoot(mirror::CompressedReference* root) { +inline void ConcurrentCopying::MarkRoot(Thread* const self, + mirror::CompressedReference* root) { DCHECK(!root->IsNull()); mirror::Object* const ref = root->AsMirrorPtr(); - mirror::Object* to_ref = Mark(ref); + mirror::Object* to_ref = Mark(self, ref); if (to_ref != ref) { auto* addr = reinterpret_cast>*>(root); auto expected_ref = mirror::CompressedReference::FromMirrorPtr(ref); auto new_ref = mirror::CompressedReference::FromMirrorPtr(to_ref); // If the cas fails, then it was updated by the mutator. do { - if (ref != addr->LoadRelaxed().AsMirrorPtr()) { + if (ref != addr->load(std::memory_order_relaxed).AsMirrorPtr()) { // It was updated by the mutator. break; } @@ -2195,11 +2207,12 @@ inline void ConcurrentCopying::MarkRoot(mirror::CompressedReference** roots, size_t count, const RootInfo& info ATTRIBUTE_UNUSED) { + Thread* const self = Thread::Current(); for (size_t i = 0; i < count; ++i) { mirror::CompressedReference* const root = roots[i]; if (!root->IsNull()) { // kGrayImmuneObject is true because this is used for the thread flip. - MarkRoot(root); + MarkRoot(self, root); } } } @@ -2233,7 +2246,9 @@ class ConcurrentCopying::ScopedGcGraysImmuneObjects { // Fill the given memory block with a dummy object. Used to fill in a // copy of objects that was lost in race. -void ConcurrentCopying::FillWithDummyObject(mirror::Object* dummy_obj, size_t byte_size) { +void ConcurrentCopying::FillWithDummyObject(Thread* const self, + mirror::Object* dummy_obj, + size_t byte_size) { // GC doesn't gray immune objects while scanning immune objects. But we need to trigger the read // barriers here because we need the updated reference to the int array class, etc. Temporary set // gc_grays_immune_objects_ to true so that we won't cause a DCHECK failure in MarkImmuneSpace(). @@ -2243,7 +2258,7 @@ void ConcurrentCopying::FillWithDummyObject(mirror::Object* dummy_obj, size_t by // Avoid going through read barrier for since kDisallowReadBarrierDuringScan may be enabled. // Explicitly mark to make sure to get an object in the to-space. mirror::Class* int_array_class = down_cast( - Mark(mirror::IntArray::GetArrayClass())); + Mark(self, GetClassRoot().Ptr())); CHECK(int_array_class != nullptr); if (ReadBarrier::kEnableToSpaceInvariantChecks) { AssertToSpaceInvariant(nullptr, MemberOffset(0), int_array_class); @@ -2277,10 +2292,9 @@ void ConcurrentCopying::FillWithDummyObject(mirror::Object* dummy_obj, size_t by } // Reuse the memory blocks that were copy of objects that were lost in race. -mirror::Object* ConcurrentCopying::AllocateInSkippedBlock(size_t alloc_size) { +mirror::Object* ConcurrentCopying::AllocateInSkippedBlock(Thread* const self, size_t alloc_size) { // Try to reuse the blocks that were unused due to CAS failures. CHECK_ALIGNED(alloc_size, space::RegionSpace::kAlignment); - Thread* self = Thread::Current(); size_t min_object_size = RoundUp(sizeof(mirror::Object), space::RegionSpace::kAlignment); size_t byte_size; uint8_t* addr; @@ -2323,8 +2337,9 @@ mirror::Object* ConcurrentCopying::AllocateInSkippedBlock(size_t alloc_size) { CHECK_GE(byte_size - alloc_size, min_object_size); // FillWithDummyObject may mark an object, avoid holding skipped_blocks_lock_ to prevent lock // violation and possible deadlock. The deadlock case is a recursive case: - // FillWithDummyObject -> IntArray::GetArrayClass -> Mark -> Copy -> AllocateInSkippedBlock. - FillWithDummyObject(reinterpret_cast(addr + alloc_size), + // FillWithDummyObject -> Mark(IntArray.class) -> Copy -> AllocateInSkippedBlock. + FillWithDummyObject(self, + reinterpret_cast(addr + alloc_size), byte_size - alloc_size); CHECK(region_space_->IsInToSpace(reinterpret_cast(addr + alloc_size))); { @@ -2335,7 +2350,8 @@ mirror::Object* ConcurrentCopying::AllocateInSkippedBlock(size_t alloc_size) { return reinterpret_cast(addr); } -mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, +mirror::Object* ConcurrentCopying::Copy(Thread* const self, + mirror::Object* from_ref, mirror::Object* holder, MemberOffset offset) { DCHECK(region_space_->IsInFromSpace(from_ref)); @@ -2364,7 +2380,7 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, DCHECK_EQ(region_space_alloc_size, region_space_bytes_allocated); } else { // Failed to allocate in the region space. Try the skipped blocks. - to_ref = AllocateInSkippedBlock(region_space_alloc_size); + to_ref = AllocateInSkippedBlock(self, region_space_alloc_size); if (to_ref != nullptr) { // Succeeded to allocate in a skipped block. if (heap_->use_tlab_) { @@ -2380,10 +2396,11 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, fall_back_to_non_moving = true; if (kVerboseMode) { LOG(INFO) << "Out of memory in the to-space. Fall back to non-moving. skipped_bytes=" - << to_space_bytes_skipped_.LoadSequentiallyConsistent() - << " skipped_objects=" << to_space_objects_skipped_.LoadSequentiallyConsistent(); + << to_space_bytes_skipped_.load(std::memory_order_seq_cst) + << " skipped_objects=" + << to_space_objects_skipped_.load(std::memory_order_seq_cst); } - to_ref = heap_->non_moving_space_->Alloc(Thread::Current(), obj_size, + to_ref = heap_->non_moving_space_->Alloc(self, obj_size, &non_moving_space_bytes_allocated, nullptr, &dummy); if (UNLIKELY(to_ref == nullptr)) { LOG(FATAL_WITHOUT_ABORT) << "Fall-back non-moving space allocation failed for a " @@ -2424,7 +2441,7 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, // the forwarding pointer first. Make the lost copy (to_ref) // look like a valid but dead (dummy) object and keep it for // future reuse. - FillWithDummyObject(to_ref, bytes_allocated); + FillWithDummyObject(self, to_ref, bytes_allocated); if (!fall_back_to_non_moving) { DCHECK(region_space_->IsInToSpace(to_ref)); if (bytes_allocated > space::RegionSpace::kRegionSize) { @@ -2432,10 +2449,10 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, region_space_->FreeLarge(to_ref, bytes_allocated); } else { // Record the lost copy for later reuse. - heap_->num_bytes_allocated_.FetchAndAddSequentiallyConsistent(bytes_allocated); - to_space_bytes_skipped_.FetchAndAddSequentiallyConsistent(bytes_allocated); - to_space_objects_skipped_.FetchAndAddSequentiallyConsistent(1); - MutexLock mu(Thread::Current(), skipped_blocks_lock_); + heap_->num_bytes_allocated_.fetch_add(bytes_allocated, std::memory_order_seq_cst); + to_space_bytes_skipped_.fetch_add(bytes_allocated, std::memory_order_seq_cst); + to_space_objects_skipped_.fetch_add(1, std::memory_order_seq_cst); + MutexLock mu(self, skipped_blocks_lock_); skipped_blocks_map_.insert(std::make_pair(bytes_allocated, reinterpret_cast(to_ref))); } @@ -2447,7 +2464,7 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, heap_mark_bitmap_->GetContinuousSpaceBitmap(to_ref); CHECK(mark_bitmap != nullptr); CHECK(mark_bitmap->Clear(to_ref)); - heap_->non_moving_space_->Free(Thread::Current(), to_ref); + heap_->non_moving_space_->Free(self, to_ref); } // Get the winner's forward ptr. @@ -2470,16 +2487,26 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, // Do a fence to prevent the field CAS in ConcurrentCopying::Process from possibly reordering // before the object copy. - QuasiAtomic::ThreadFenceRelease(); + std::atomic_thread_fence(std::memory_order_release); LockWord new_lock_word = LockWord::FromForwardingAddress(reinterpret_cast(to_ref)); // Try to atomically write the fwd ptr. - bool success = from_ref->CasLockWordWeakRelaxed(old_lock_word, new_lock_word); + bool success = from_ref->CasLockWord(old_lock_word, + new_lock_word, + CASMode::kWeak, + std::memory_order_relaxed); if (LIKELY(success)) { // The CAS succeeded. - objects_moved_.FetchAndAddRelaxed(1); - bytes_moved_.FetchAndAddRelaxed(region_space_alloc_size); + DCHECK(thread_running_gc_ != nullptr); + if (LIKELY(self == thread_running_gc_)) { + objects_moved_gc_thread_ += 1; + bytes_moved_gc_thread_ += region_space_alloc_size; + } else { + objects_moved_.fetch_add(1, std::memory_order_relaxed); + bytes_moved_.fetch_add(region_space_alloc_size, std::memory_order_relaxed); + } + if (LIKELY(!fall_back_to_non_moving)) { DCHECK(region_space_->IsInToSpace(to_ref)); } else { @@ -2491,7 +2518,7 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, } DCHECK(GetFwdPtr(from_ref) == to_ref); CHECK_NE(to_ref->GetLockWord(false).GetState(), LockWord::kForwardingAddress); - PushOntoMarkStack(to_ref); + PushOntoMarkStack(self, to_ref); return to_ref; } else { // The CAS failed. It may have lost the race or may have failed @@ -2565,12 +2592,13 @@ mirror::Object* ConcurrentCopying::IsMarked(mirror::Object* from_ref) { bool ConcurrentCopying::IsOnAllocStack(mirror::Object* ref) { // TODO: Explain why this is here. What release operation does it pair with? - QuasiAtomic::ThreadFenceAcquire(); + std::atomic_thread_fence(std::memory_order_acquire); accounting::ObjectStack* alloc_stack = GetAllocationStack(); return alloc_stack->Contains(ref); } -mirror::Object* ConcurrentCopying::MarkNonMoving(mirror::Object* ref, +mirror::Object* ConcurrentCopying::MarkNonMoving(Thread* const self, + mirror::Object* ref, mirror::Object* holder, MemberOffset offset) { // ref is in a non-moving space (from_ref == to_ref). @@ -2584,16 +2612,8 @@ mirror::Object* ConcurrentCopying::MarkNonMoving(mirror::Object* ref, bool is_los = mark_bitmap == nullptr; if (!is_los && mark_bitmap->Test(ref)) { // Already marked. - if (kUseBakerReadBarrier) { - DCHECK(ref->GetReadBarrierState() == ReadBarrier::GrayState() || - ref->GetReadBarrierState() == ReadBarrier::WhiteState()); - } } else if (is_los && los_bitmap->Test(ref)) { // Already marked in LOS. - if (kUseBakerReadBarrier) { - DCHECK(ref->GetReadBarrierState() == ReadBarrier::GrayState() || - ref->GetReadBarrierState() == ReadBarrier::WhiteState()); - } } else { // Not marked. if (IsOnAllocStack(ref)) { @@ -2633,20 +2653,20 @@ mirror::Object* ConcurrentCopying::MarkNonMoving(mirror::Object* ref, // Already marked. if (kUseBakerReadBarrier && cas_success && ref->GetReadBarrierState() == ReadBarrier::GrayState()) { - PushOntoFalseGrayStack(ref); + PushOntoFalseGrayStack(self, ref); } } else if (is_los && los_bitmap->AtomicTestAndSet(ref)) { // Already marked in LOS. if (kUseBakerReadBarrier && cas_success && ref->GetReadBarrierState() == ReadBarrier::GrayState()) { - PushOntoFalseGrayStack(ref); + PushOntoFalseGrayStack(self, ref); } } else { // Newly marked. if (kUseBakerReadBarrier) { DCHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::GrayState()); } - PushOntoMarkStack(ref); + PushOntoMarkStack(self, ref); } } } @@ -2705,9 +2725,10 @@ void ConcurrentCopying::FinishPhase() { } if (measure_read_barrier_slow_path_) { MutexLock mu(self, rb_slow_path_histogram_lock_); - rb_slow_path_time_histogram_.AdjustAndAddValue(rb_slow_path_ns_.LoadRelaxed()); - rb_slow_path_count_total_ += rb_slow_path_count_.LoadRelaxed(); - rb_slow_path_count_gc_total_ += rb_slow_path_count_gc_.LoadRelaxed(); + rb_slow_path_time_histogram_.AdjustAndAddValue( + rb_slow_path_ns_.load(std::memory_order_relaxed)); + rb_slow_path_count_total_ += rb_slow_path_count_.load(std::memory_order_relaxed); + rb_slow_path_count_gc_total_ += rb_slow_path_count_gc_.load(std::memory_order_relaxed); } } @@ -2738,7 +2759,7 @@ bool ConcurrentCopying::IsNullOrMarkedHeapReference(mirror::HeapReference klass, @@ -2759,17 +2780,18 @@ void ConcurrentCopying::RevokeAllThreadLocalBuffers() { region_space_->RevokeAllThreadLocalBuffers(); } -mirror::Object* ConcurrentCopying::MarkFromReadBarrierWithMeasurements(mirror::Object* from_ref) { - if (Thread::Current() != thread_running_gc_) { - rb_slow_path_count_.FetchAndAddRelaxed(1u); +mirror::Object* ConcurrentCopying::MarkFromReadBarrierWithMeasurements(Thread* const self, + mirror::Object* from_ref) { + if (self != thread_running_gc_) { + rb_slow_path_count_.fetch_add(1u, std::memory_order_relaxed); } else { - rb_slow_path_count_gc_.FetchAndAddRelaxed(1u); + rb_slow_path_count_gc_.fetch_add(1u, std::memory_order_relaxed); } ScopedTrace tr(__FUNCTION__); const uint64_t start_time = measure_read_barrier_slow_path_ ? NanoTime() : 0u; - mirror::Object* ret = Mark(from_ref); + mirror::Object* ret = Mark(self, from_ref); if (measure_read_barrier_slow_path_) { - rb_slow_path_ns_.FetchAndAddRelaxed(NanoTime() - start_time); + rb_slow_path_ns_.fetch_add(NanoTime() - start_time, std::memory_order_relaxed); } return ret; } @@ -2788,8 +2810,10 @@ void ConcurrentCopying::DumpPerformanceInfo(std::ostream& os) { if (rb_slow_path_count_gc_total_ > 0) { os << "GC slow path count " << rb_slow_path_count_gc_total_ << "\n"; } - os << "Cumulative bytes moved " << cumulative_bytes_moved_.LoadRelaxed() << "\n"; - os << "Cumulative objects moved " << cumulative_objects_moved_.LoadRelaxed() << "\n"; + os << "Cumulative bytes moved " + << cumulative_bytes_moved_.load(std::memory_order_relaxed) << "\n"; + os << "Cumulative objects moved " + << cumulative_objects_moved_.load(std::memory_order_relaxed) << "\n"; os << "Peak regions allocated " << region_space_->GetMaxPeakNumNonFreeRegions() << " (" diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h index a00dbb58d0d580d0615cf08ffb792c4bdb315d58..448525d013ff15d4a75f30762659ac242d5de124 100644 --- a/runtime/gc/collector/concurrent_copying.h +++ b/runtime/gc/collector/concurrent_copying.h @@ -112,7 +112,8 @@ class ConcurrentCopying : public GarbageCollector { } template // Mark object `from_ref`, copying it to the to-space if needed. - ALWAYS_INLINE mirror::Object* Mark(mirror::Object* from_ref, + ALWAYS_INLINE mirror::Object* Mark(Thread* const self, + mirror::Object* from_ref, mirror::Object* holder = nullptr, MemberOffset offset = MemberOffset(0)) REQUIRES_SHARED(Locks::mutator_lock_) @@ -144,9 +145,11 @@ class ConcurrentCopying : public GarbageCollector { REQUIRES_SHARED(Locks::mutator_lock_); private: - void PushOntoMarkStack(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) + void PushOntoMarkStack(Thread* const self, mirror::Object* obj) + REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_); - mirror::Object* Copy(mirror::Object* from_ref, + mirror::Object* Copy(Thread* const self, + mirror::Object* from_ref, mirror::Object* holder, MemberOffset offset) REQUIRES_SHARED(Locks::mutator_lock_) @@ -162,7 +165,7 @@ class ConcurrentCopying : public GarbageCollector { OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_); template - void MarkRoot(mirror::CompressedReference* root) + void MarkRoot(Thread* const self, mirror::CompressedReference* root) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_); virtual void VisitRoots(mirror::CompressedReference** roots, size_t count, @@ -220,10 +223,10 @@ class ConcurrentCopying : public GarbageCollector { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_); void MarkZygoteLargeObjects() REQUIRES_SHARED(Locks::mutator_lock_); - void FillWithDummyObject(mirror::Object* dummy_obj, size_t byte_size) + void FillWithDummyObject(Thread* const self, mirror::Object* dummy_obj, size_t byte_size) REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - mirror::Object* AllocateInSkippedBlock(size_t alloc_size) + mirror::Object* AllocateInSkippedBlock(Thread* const self, size_t alloc_size) REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_) REQUIRES_SHARED(Locks::mutator_lock_); void CheckEmptyMarkStack() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_); @@ -239,7 +242,7 @@ class ConcurrentCopying : public GarbageCollector { REQUIRES_SHARED(Locks::mutator_lock_); // Dump information about reference `ref` and return it as a string. // Use `ref_name` to name the reference in messages. Each message is prefixed with `indent`. - std::string DumpReferenceInfo(mirror::Object* ref, const char* ref_name, std::string indent = "") + std::string DumpReferenceInfo(mirror::Object* ref, const char* ref_name, const char* indent = "") REQUIRES_SHARED(Locks::mutator_lock_); // Dump information about heap reference `ref`, referenced from object `obj` at offset `offset`, // and return it as a string. @@ -253,25 +256,30 @@ class ConcurrentCopying : public GarbageCollector { void DisableMarking() REQUIRES_SHARED(Locks::mutator_lock_); void IssueDisableMarkingCheckpoint() REQUIRES_SHARED(Locks::mutator_lock_); void ExpandGcMarkStack() REQUIRES_SHARED(Locks::mutator_lock_); - mirror::Object* MarkNonMoving(mirror::Object* from_ref, + mirror::Object* MarkNonMoving(Thread* const self, + mirror::Object* from_ref, mirror::Object* holder = nullptr, MemberOffset offset = MemberOffset(0)) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_); - ALWAYS_INLINE mirror::Object* MarkUnevacFromSpaceRegion(mirror::Object* from_ref, + ALWAYS_INLINE mirror::Object* MarkUnevacFromSpaceRegion(Thread* const self, + mirror::Object* from_ref, accounting::SpaceBitmap* bitmap) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_); template - ALWAYS_INLINE mirror::Object* MarkImmuneSpace(mirror::Object* from_ref) + ALWAYS_INLINE mirror::Object* MarkImmuneSpace(Thread* const self, + mirror::Object* from_ref) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!immune_gray_stack_lock_); - void PushOntoFalseGrayStack(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) + void PushOntoFalseGrayStack(Thread* const self, mirror::Object* obj) + REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_); void ProcessFalseGrayStack() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_); void ScanImmuneObject(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_); - mirror::Object* MarkFromReadBarrierWithMeasurements(mirror::Object* from_ref) + mirror::Object* MarkFromReadBarrierWithMeasurements(Thread* const self, + mirror::Object* from_ref) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_); void DumpPerformanceInfo(std::ostream& os) OVERRIDE REQUIRES(!rb_slow_path_histogram_lock_); @@ -330,8 +338,12 @@ class ConcurrentCopying : public GarbageCollector { bool weak_ref_access_enabled_ GUARDED_BY(Locks::thread_list_lock_); // How many objects and bytes we moved. Used for accounting. - Atomic bytes_moved_; - Atomic objects_moved_; + // GC thread moves many more objects than mutators. + // Therefore, we separate the two to avoid CAS. + Atomic bytes_moved_; // Used by mutators + Atomic objects_moved_; // Used by mutators + size_t bytes_moved_gc_thread_; // Used by GC + size_t objects_moved_gc_thread_; // Used by GC Atomic cumulative_bytes_moved_; Atomic cumulative_objects_moved_; diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc deleted file mode 100644 index 34cc129ce8c0c9dff9d30d385c16fc7f24830455..0000000000000000000000000000000000000000 --- a/runtime/gc/collector/mark_compact.cc +++ /dev/null @@ -1,642 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mark_compact.h" - -#include - -#include "base/macros.h" -#include "base/mutex-inl.h" -#include "base/timing_logger.h" -#include "gc/accounting/heap_bitmap-inl.h" -#include "gc/accounting/mod_union_table.h" -#include "gc/accounting/space_bitmap-inl.h" -#include "gc/heap.h" -#include "gc/reference_processor.h" -#include "gc/space/bump_pointer_space-inl.h" -#include "gc/space/large_object_space.h" -#include "gc/space/space-inl.h" -#include "mirror/class-inl.h" -#include "mirror/object-inl.h" -#include "mirror/object-refvisitor-inl.h" -#include "runtime.h" -#include "stack.h" -#include "thread-current-inl.h" -#include "thread_list.h" - -namespace art { -namespace gc { -namespace collector { - -void MarkCompact::BindBitmaps() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - // Mark all of the spaces we never collect as immune. - for (const auto& space : GetHeap()->GetContinuousSpaces()) { - if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyNeverCollect || - space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect) { - immune_spaces_.AddSpace(space); - } - } -} - -MarkCompact::MarkCompact(Heap* heap, const std::string& name_prefix) - : GarbageCollector(heap, name_prefix + (name_prefix.empty() ? "" : " ") + "mark compact"), - mark_stack_(nullptr), - space_(nullptr), - mark_bitmap_(nullptr), - collector_name_(name_), - bump_pointer_(nullptr), - live_objects_in_space_(0), - updating_references_(false) {} - -void MarkCompact::RunPhases() { - Thread* self = Thread::Current(); - InitializePhase(); - CHECK(!Locks::mutator_lock_->IsExclusiveHeld(self)); - { - ScopedPause pause(this); - GetHeap()->PreGcVerificationPaused(this); - GetHeap()->PrePauseRosAllocVerification(this); - MarkingPhase(); - ReclaimPhase(); - } - GetHeap()->PostGcVerification(this); - FinishPhase(); -} - -void MarkCompact::ForwardObject(mirror::Object* obj) { - const size_t alloc_size = RoundUp(obj->SizeOf(), space::BumpPointerSpace::kAlignment); - LockWord lock_word = obj->GetLockWord(false); - // If we have a non empty lock word, store it and restore it later. - if (!LockWord::IsDefault(lock_word)) { - // Set the bit in the bitmap so that we know to restore it later. - objects_with_lockword_->Set(obj); - lock_words_to_restore_.push_back(lock_word); - } - obj->SetLockWord(LockWord::FromForwardingAddress(reinterpret_cast(bump_pointer_)), - false); - bump_pointer_ += alloc_size; - ++live_objects_in_space_; -} - - -void MarkCompact::CalculateObjectForwardingAddresses() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - // The bump pointer in the space where the next forwarding address will be. - bump_pointer_ = reinterpret_cast(space_->Begin()); - // Visit all the marked objects in the bitmap. - objects_before_forwarding_->VisitMarkedRange(reinterpret_cast(space_->Begin()), - reinterpret_cast(space_->End()), - [this](mirror::Object* obj) - REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { - DCHECK_ALIGNED(obj, space::BumpPointerSpace::kAlignment); - DCHECK(IsMarked(obj) != nullptr); - ForwardObject(obj); - }); -} - -void MarkCompact::InitializePhase() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - mark_stack_ = heap_->GetMarkStack(); - DCHECK(mark_stack_ != nullptr); - immune_spaces_.Reset(); - CHECK(space_->CanMoveObjects()) << "Attempting compact non-movable space from " << *space_; - // TODO: I don't think we should need heap bitmap lock to Get the mark bitmap. - ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - mark_bitmap_ = heap_->GetMarkBitmap(); - live_objects_in_space_ = 0; -} - -void MarkCompact::ProcessReferences(Thread* self) { - WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap_->GetReferenceProcessor()->ProcessReferences( - false, GetTimings(), GetCurrentIteration()->GetClearSoftReferences(), this); -} - -inline mirror::Object* MarkCompact::MarkObject(mirror::Object* obj) { - if (obj == nullptr) { - return nullptr; - } - if (kUseBakerReadBarrier) { - // Verify all the objects have the correct forward state installed. - obj->AssertReadBarrierState(); - } - if (!immune_spaces_.IsInImmuneRegion(obj)) { - if (objects_before_forwarding_->HasAddress(obj)) { - if (!objects_before_forwarding_->Set(obj)) { - MarkStackPush(obj); // This object was not previously marked. - } - } else { - DCHECK(!space_->HasAddress(obj)); - auto slow_path = [](const mirror::Object* ref) - REQUIRES_SHARED(Locks::mutator_lock_) { - // Marking a large object, make sure its aligned as a sanity check. - if (!IsAligned(ref)) { - Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(ERROR)); - LOG(FATAL) << ref; - } - }; - if (!mark_bitmap_->Set(obj, slow_path)) { - // This object was not previously marked. - MarkStackPush(obj); - } - } - } - return obj; -} - -void MarkCompact::MarkingPhase() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - Thread* self = Thread::Current(); - // Bitmap which describes which objects we have to move. - objects_before_forwarding_.reset(accounting::ContinuousSpaceBitmap::Create( - "objects before forwarding", space_->Begin(), space_->Size())); - // Bitmap which describes which lock words we need to restore. - objects_with_lockword_.reset(accounting::ContinuousSpaceBitmap::Create( - "objects with lock words", space_->Begin(), space_->Size())); - CHECK(Locks::mutator_lock_->IsExclusiveHeld(self)); - // Assume the cleared space is already empty. - BindBitmaps(); - t.NewTiming("ProcessCards"); - // Process dirty cards and add dirty cards to mod-union tables. - heap_->ProcessCards(GetTimings(), false, false, true); - // Clear the whole card table since we cannot get any additional dirty cards during the - // paused GC. This saves memory but only works for pause the world collectors. - t.NewTiming("ClearCardTable"); - heap_->GetCardTable()->ClearCardTable(); - // Need to do this before the checkpoint since we don't want any threads to add references to - // the live stack during the recursive mark. - if (kUseThreadLocalAllocationStack) { - t.NewTiming("RevokeAllThreadLocalAllocationStacks"); - heap_->RevokeAllThreadLocalAllocationStacks(self); - } - t.NewTiming("SwapStacks"); - heap_->SwapStacks(); - { - WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - MarkRoots(); - // Mark roots of immune spaces. - UpdateAndMarkModUnion(); - // Recursively mark remaining objects. - MarkReachableObjects(); - } - ProcessReferences(self); - { - ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); - SweepSystemWeaks(); - } - Runtime::Current()->GetClassLinker()->CleanupClassLoaders(); - // Revoke buffers before measuring how many objects were moved since the TLABs need to be revoked - // before they are properly counted. - RevokeAllThreadLocalBuffers(); - // Disabled due to an issue where we have objects in the bump pointer space which reference dead - // objects. - // heap_->PreSweepingGcVerification(this); -} - -void MarkCompact::UpdateAndMarkModUnion() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - for (auto& space : heap_->GetContinuousSpaces()) { - // If the space is immune then we need to mark the references to other spaces. - if (immune_spaces_.ContainsSpace(space)) { - accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space); - if (table != nullptr) { - // TODO: Improve naming. - TimingLogger::ScopedTiming t2( - space->IsZygoteSpace() ? "UpdateAndMarkZygoteModUnionTable" : - "UpdateAndMarkImageModUnionTable", GetTimings()); - table->UpdateAndMarkReferences(this); - } - } - } -} - -void MarkCompact::MarkReachableObjects() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - accounting::ObjectStack* live_stack = heap_->GetLiveStack(); - { - TimingLogger::ScopedTiming t2("MarkAllocStackAsLive", GetTimings()); - heap_->MarkAllocStackAsLive(live_stack); - } - live_stack->Reset(); - // Recursively process the mark stack. - ProcessMarkStack(); -} - -void MarkCompact::ReclaimPhase() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - // Reclaim unmarked objects. - Sweep(false); - // Swap the live and mark bitmaps for each space which we modified space. This is an - // optimization that enables us to not clear live bits inside of the sweep. Only swaps unbound - // bitmaps. - SwapBitmaps(); - GetHeap()->UnBindBitmaps(); // Unbind the live and mark bitmaps. - Compact(); -} - -void MarkCompact::ResizeMarkStack(size_t new_size) { - std::vector> temp(mark_stack_->Begin(), mark_stack_->End()); - CHECK_LE(mark_stack_->Size(), new_size); - mark_stack_->Resize(new_size); - for (auto& obj : temp) { - mark_stack_->PushBack(obj.AsMirrorPtr()); - } -} - -inline void MarkCompact::MarkStackPush(mirror::Object* obj) { - if (UNLIKELY(mark_stack_->Size() >= mark_stack_->Capacity())) { - ResizeMarkStack(mark_stack_->Capacity() * 2); - } - // The object must be pushed on to the mark stack. - mark_stack_->PushBack(obj); -} - -void MarkCompact::MarkHeapReference(mirror::HeapReference* obj_ptr, - bool do_atomic_update ATTRIBUTE_UNUSED) { - if (updating_references_) { - UpdateHeapReference(obj_ptr); - } else { - MarkObject(obj_ptr->AsMirrorPtr()); - } -} - -void MarkCompact::VisitRoots( - mirror::Object*** roots, size_t count, const RootInfo& info ATTRIBUTE_UNUSED) { - for (size_t i = 0; i < count; ++i) { - MarkObject(*roots[i]); - } -} - -void MarkCompact::VisitRoots( - mirror::CompressedReference** roots, size_t count, - const RootInfo& info ATTRIBUTE_UNUSED) { - for (size_t i = 0; i < count; ++i) { - MarkObject(roots[i]->AsMirrorPtr()); - } -} - -class MarkCompact::UpdateRootVisitor : public RootVisitor { - public: - explicit UpdateRootVisitor(MarkCompact* collector) : collector_(collector) {} - - void VisitRoots(mirror::Object*** roots, size_t count, const RootInfo& info ATTRIBUTE_UNUSED) - OVERRIDE REQUIRES(Locks::mutator_lock_) - REQUIRES_SHARED(Locks::heap_bitmap_lock_) { - for (size_t i = 0; i < count; ++i) { - mirror::Object* obj = *roots[i]; - mirror::Object* new_obj = collector_->GetMarkedForwardAddress(obj); - if (obj != new_obj) { - *roots[i] = new_obj; - DCHECK(new_obj != nullptr); - } - } - } - - void VisitRoots(mirror::CompressedReference** roots, size_t count, - const RootInfo& info ATTRIBUTE_UNUSED) - OVERRIDE REQUIRES(Locks::mutator_lock_) - REQUIRES_SHARED(Locks::heap_bitmap_lock_) { - for (size_t i = 0; i < count; ++i) { - mirror::Object* obj = roots[i]->AsMirrorPtr(); - mirror::Object* new_obj = collector_->GetMarkedForwardAddress(obj); - if (obj != new_obj) { - roots[i]->Assign(new_obj); - DCHECK(new_obj != nullptr); - } - } - } - - private: - MarkCompact* const collector_; -}; - -class MarkCompact::UpdateObjectReferencesVisitor { - public: - explicit UpdateObjectReferencesVisitor(MarkCompact* collector) : collector_(collector) {} - - void operator()(mirror::Object* obj) const REQUIRES_SHARED(Locks::heap_bitmap_lock_) - REQUIRES(Locks::mutator_lock_) ALWAYS_INLINE { - collector_->UpdateObjectReferences(obj); - } - - private: - MarkCompact* const collector_; -}; - -void MarkCompact::UpdateReferences() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - updating_references_ = true; - Runtime* runtime = Runtime::Current(); - // Update roots. - UpdateRootVisitor update_root_visitor(this); - runtime->VisitRoots(&update_root_visitor); - // Update object references in mod union tables and spaces. - for (const auto& space : heap_->GetContinuousSpaces()) { - // If the space is immune then we need to mark the references to other spaces. - accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space); - if (table != nullptr) { - // TODO: Improve naming. - TimingLogger::ScopedTiming t2( - space->IsZygoteSpace() ? "UpdateZygoteModUnionTableReferences" : - "UpdateImageModUnionTableReferences", - GetTimings()); - table->UpdateAndMarkReferences(this); - } else { - // No mod union table, so we need to scan the space using bitmap visit. - // Scan the space using bitmap visit. - accounting::ContinuousSpaceBitmap* bitmap = space->GetLiveBitmap(); - if (bitmap != nullptr) { - UpdateObjectReferencesVisitor visitor(this); - bitmap->VisitMarkedRange(reinterpret_cast(space->Begin()), - reinterpret_cast(space->End()), - visitor); - } - } - } - CHECK(!kMovingClasses) - << "Didn't update large object classes since they are assumed to not move."; - // Update the system weaks, these should already have been swept. - runtime->SweepSystemWeaks(this); - // Update the objects in the bump pointer space last, these objects don't have a bitmap. - UpdateObjectReferencesVisitor visitor(this); - objects_before_forwarding_->VisitMarkedRange(reinterpret_cast(space_->Begin()), - reinterpret_cast(space_->End()), - visitor); - // Update the reference processor cleared list. - heap_->GetReferenceProcessor()->UpdateRoots(this); - updating_references_ = false; -} - -void MarkCompact::Compact() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - CalculateObjectForwardingAddresses(); - UpdateReferences(); - MoveObjects(); - // Space - int64_t objects_freed = space_->GetObjectsAllocated() - live_objects_in_space_; - int64_t bytes_freed = reinterpret_cast(space_->End()) - - reinterpret_cast(bump_pointer_); - t.NewTiming("RecordFree"); - space_->RecordFree(objects_freed, bytes_freed); - RecordFree(ObjectBytePair(objects_freed, bytes_freed)); - space_->SetEnd(bump_pointer_); - // Need to zero out the memory we freed. TODO: Use madvise for pages. - memset(bump_pointer_, 0, bytes_freed); -} - -// Marks all objects in the root set. -void MarkCompact::MarkRoots() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - Runtime::Current()->VisitRoots(this); -} - -inline void MarkCompact::UpdateHeapReference(mirror::HeapReference* reference) { - mirror::Object* obj = reference->AsMirrorPtr(); - if (obj != nullptr) { - mirror::Object* new_obj = GetMarkedForwardAddress(obj); - if (obj != new_obj) { - DCHECK(new_obj != nullptr); - reference->Assign(new_obj); - } - } -} - -class MarkCompact::UpdateReferenceVisitor { - public: - explicit UpdateReferenceVisitor(MarkCompact* collector) : collector_(collector) {} - - void operator()(mirror::Object* obj, MemberOffset offset, bool /*is_static*/) const - ALWAYS_INLINE REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { - collector_->UpdateHeapReference(obj->GetFieldObjectReferenceAddr(offset)); - } - - void operator()(ObjPtr /*klass*/, mirror::Reference* ref) const - REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { - collector_->UpdateHeapReference( - ref->GetFieldObjectReferenceAddr(mirror::Reference::ReferentOffset())); - } - - // TODO: Remove NO_THREAD_SAFETY_ANALYSIS when clang better understands visitors. - void VisitRootIfNonNull(mirror::CompressedReference* root) const - NO_THREAD_SAFETY_ANALYSIS { - if (!root->IsNull()) { - VisitRoot(root); - } - } - - void VisitRoot(mirror::CompressedReference* root) const - NO_THREAD_SAFETY_ANALYSIS { - root->Assign(collector_->GetMarkedForwardAddress(root->AsMirrorPtr())); - } - - private: - MarkCompact* const collector_; -}; - -void MarkCompact::UpdateObjectReferences(mirror::Object* obj) { - UpdateReferenceVisitor visitor(this); - obj->VisitReferences(visitor, visitor); -} - -inline mirror::Object* MarkCompact::GetMarkedForwardAddress(mirror::Object* obj) { - DCHECK(obj != nullptr); - if (objects_before_forwarding_->HasAddress(obj)) { - DCHECK(objects_before_forwarding_->Test(obj)); - mirror::Object* ret = - reinterpret_cast(obj->GetLockWord(false).ForwardingAddress()); - DCHECK(ret != nullptr); - return ret; - } - DCHECK(!space_->HasAddress(obj)); - return obj; -} - -mirror::Object* MarkCompact::IsMarked(mirror::Object* object) { - if (immune_spaces_.IsInImmuneRegion(object)) { - return object; - } - if (updating_references_) { - return GetMarkedForwardAddress(object); - } - if (objects_before_forwarding_->HasAddress(object)) { - return objects_before_forwarding_->Test(object) ? object : nullptr; - } - return mark_bitmap_->Test(object) ? object : nullptr; -} - -bool MarkCompact::IsNullOrMarkedHeapReference(mirror::HeapReference* ref_ptr, - // MarkCompact does the GC in a pause. No CAS needed. - bool do_atomic_update ATTRIBUTE_UNUSED) { - // Side effect free since we call this before ever moving objects. - mirror::Object* obj = ref_ptr->AsMirrorPtr(); - if (obj == nullptr) { - return true; - } - return IsMarked(obj) != nullptr; -} - -void MarkCompact::SweepSystemWeaks() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - Runtime::Current()->SweepSystemWeaks(this); -} - -bool MarkCompact::ShouldSweepSpace(space::ContinuousSpace* space) const { - return space != space_ && !immune_spaces_.ContainsSpace(space); -} - -void MarkCompact::MoveObject(mirror::Object* obj, size_t len) { - // Look at the forwarding address stored in the lock word to know where to copy. - DCHECK(space_->HasAddress(obj)) << obj; - uintptr_t dest_addr = obj->GetLockWord(false).ForwardingAddress(); - mirror::Object* dest_obj = reinterpret_cast(dest_addr); - DCHECK(space_->HasAddress(dest_obj)) << dest_obj; - // Use memmove since there may be overlap. - memmove(reinterpret_cast(dest_addr), reinterpret_cast(obj), len); - // Restore the saved lock word if needed. - LockWord lock_word = LockWord::Default(); - if (UNLIKELY(objects_with_lockword_->Test(obj))) { - lock_word = lock_words_to_restore_.front(); - lock_words_to_restore_.pop_front(); - } - dest_obj->SetLockWord(lock_word, false); -} - -void MarkCompact::MoveObjects() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - // Move the objects in the before forwarding bitmap. - objects_before_forwarding_->VisitMarkedRange(reinterpret_cast(space_->Begin()), - reinterpret_cast(space_->End()), - [this](mirror::Object* obj) - REQUIRES_SHARED(Locks::heap_bitmap_lock_) - REQUIRES(Locks::mutator_lock_) ALWAYS_INLINE { - MoveObject(obj, obj->SizeOf()); - }); - CHECK(lock_words_to_restore_.empty()); -} - -void MarkCompact::Sweep(bool swap_bitmaps) { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - DCHECK(mark_stack_->IsEmpty()); - for (const auto& space : GetHeap()->GetContinuousSpaces()) { - if (space->IsContinuousMemMapAllocSpace()) { - space::ContinuousMemMapAllocSpace* alloc_space = space->AsContinuousMemMapAllocSpace(); - if (!ShouldSweepSpace(alloc_space)) { - continue; - } - TimingLogger::ScopedTiming t2( - alloc_space->IsZygoteSpace() ? "SweepZygoteSpace" : "SweepAllocSpace", GetTimings()); - RecordFree(alloc_space->Sweep(swap_bitmaps)); - } - } - SweepLargeObjects(swap_bitmaps); -} - -void MarkCompact::SweepLargeObjects(bool swap_bitmaps) { - space::LargeObjectSpace* los = heap_->GetLargeObjectsSpace(); - if (los != nullptr) { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());\ - RecordFreeLOS(los->Sweep(swap_bitmaps)); - } -} - -// Process the "referent" field in a java.lang.ref.Reference. If the referent has not yet been -// marked, put it on the appropriate list in the heap for later processing. -void MarkCompact::DelayReferenceReferent(ObjPtr klass, - ObjPtr reference) { - heap_->GetReferenceProcessor()->DelayReferenceReferent(klass, reference, this); -} - -class MarkCompact::MarkObjectVisitor { - public: - explicit MarkObjectVisitor(MarkCompact* collector) : collector_(collector) {} - - void operator()(ObjPtr obj, - MemberOffset offset, - bool /*is_static*/) const ALWAYS_INLINE - REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { - // Object was already verified when we scanned it. - collector_->MarkObject(obj->GetFieldObject(offset)); - } - - void operator()(ObjPtr klass, - ObjPtr ref) const - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(Locks::heap_bitmap_lock_) { - collector_->DelayReferenceReferent(klass, ref); - } - - // TODO: Remove NO_THREAD_SAFETY_ANALYSIS when clang better understands visitors. - void VisitRootIfNonNull(mirror::CompressedReference* root) const - NO_THREAD_SAFETY_ANALYSIS { - if (!root->IsNull()) { - VisitRoot(root); - } - } - - void VisitRoot(mirror::CompressedReference* root) const - NO_THREAD_SAFETY_ANALYSIS { - collector_->MarkObject(root->AsMirrorPtr()); - } - - private: - MarkCompact* const collector_; -}; - -// Visit all of the references of an object and update. -void MarkCompact::ScanObject(mirror::Object* obj) { - MarkObjectVisitor visitor(this); - obj->VisitReferences(visitor, visitor); -} - -// Scan anything that's on the mark stack. -void MarkCompact::ProcessMarkStack() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - while (!mark_stack_->IsEmpty()) { - mirror::Object* obj = mark_stack_->PopBack(); - DCHECK(obj != nullptr); - ScanObject(obj); - } -} - -void MarkCompact::SetSpace(space::BumpPointerSpace* space) { - DCHECK(space != nullptr); - space_ = space; -} - -void MarkCompact::FinishPhase() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - space_ = nullptr; - CHECK(mark_stack_->IsEmpty()); - mark_stack_->Reset(); - // Clear all of the spaces' mark bitmaps. - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - heap_->ClearMarkedObjects(); - // Release our bitmaps. - objects_before_forwarding_.reset(nullptr); - objects_with_lockword_.reset(nullptr); -} - -void MarkCompact::RevokeAllThreadLocalBuffers() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - GetHeap()->RevokeAllThreadLocalBuffers(); -} - -} // namespace collector -} // namespace gc -} // namespace art diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h deleted file mode 100644 index e7749597cd446cc352ce05b6943e1d36cc230918..0000000000000000000000000000000000000000 --- a/runtime/gc/collector/mark_compact.h +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_GC_COLLECTOR_MARK_COMPACT_H_ -#define ART_RUNTIME_GC_COLLECTOR_MARK_COMPACT_H_ - -#include -#include // For unique_ptr. - -#include "base/atomic.h" -#include "base/macros.h" -#include "base/mutex.h" -#include "garbage_collector.h" -#include "gc/accounting/heap_bitmap.h" -#include "gc_root.h" -#include "immune_spaces.h" -#include "lock_word.h" -#include "offsets.h" - -namespace art { - -class Thread; - -namespace mirror { -class Class; -class Object; -} // namespace mirror - -namespace gc { - -class Heap; - -namespace accounting { -template class AtomicStack; -typedef AtomicStack ObjectStack; -} // namespace accounting - -namespace space { -class BumpPointerSpace; -class ContinuousMemMapAllocSpace; -class ContinuousSpace; -} // namespace space - -namespace collector { - -class MarkCompact : public GarbageCollector { - public: - explicit MarkCompact(Heap* heap, const std::string& name_prefix = ""); - ~MarkCompact() {} - - virtual void RunPhases() OVERRIDE NO_THREAD_SAFETY_ANALYSIS; - void InitializePhase(); - void MarkingPhase() REQUIRES(Locks::mutator_lock_) - REQUIRES(!Locks::heap_bitmap_lock_); - void ReclaimPhase() REQUIRES(Locks::mutator_lock_) - REQUIRES(!Locks::heap_bitmap_lock_); - void FinishPhase() REQUIRES(Locks::mutator_lock_); - void MarkReachableObjects() - REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_); - virtual GcType GetGcType() const OVERRIDE { - return kGcTypePartial; - } - virtual CollectorType GetCollectorType() const OVERRIDE { - return kCollectorTypeMC; - } - - // Sets which space we will be copying objects in. - void SetSpace(space::BumpPointerSpace* space); - - // Initializes internal structures. - void Init(); - - // Find the default mark bitmap. - void FindDefaultMarkBitmap(); - - void ScanObject(mirror::Object* obj) - REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - - // Marks the root set at the start of a garbage collection. - void MarkRoots() - REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - - // Bind the live bits to the mark bits of bitmaps for spaces that are never collected, ie - // the image. Mark that portion of the heap as immune. - void BindBitmaps() REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!Locks::heap_bitmap_lock_); - - void UnBindBitmaps() - REQUIRES(Locks::heap_bitmap_lock_); - - void ProcessReferences(Thread* self) REQUIRES(Locks::mutator_lock_) - REQUIRES(Locks::mutator_lock_); - - // Sweeps unmarked objects to complete the garbage collection. - void Sweep(bool swap_bitmaps) REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - - // Sweeps unmarked objects to complete the garbage collection. - void SweepLargeObjects(bool swap_bitmaps) REQUIRES(Locks::heap_bitmap_lock_); - - void SweepSystemWeaks() - REQUIRES_SHARED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - - virtual void VisitRoots(mirror::Object*** roots, size_t count, const RootInfo& info) - OVERRIDE REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_); - - virtual void VisitRoots(mirror::CompressedReference** roots, size_t count, - const RootInfo& info) - OVERRIDE REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_); - - // Schedules an unmarked object for reference processing. - void DelayReferenceReferent(ObjPtr klass, ObjPtr reference) - REQUIRES_SHARED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - - protected: - // Returns null if the object is not marked, otherwise returns the forwarding address (same as - // object for non movable things). - mirror::Object* GetMarkedForwardAddress(mirror::Object* object) - REQUIRES(Locks::mutator_lock_) - REQUIRES_SHARED(Locks::heap_bitmap_lock_); - - // Marks or unmarks a large object based on whether or not set is true. If set is true, then we - // mark, otherwise we unmark. - bool MarkLargeObject(const mirror::Object* obj) - REQUIRES(Locks::heap_bitmap_lock_) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Expand mark stack to 2x its current size. - void ResizeMarkStack(size_t new_size) REQUIRES_SHARED(Locks::mutator_lock_); - - // Returns true if we should sweep the space. - bool ShouldSweepSpace(space::ContinuousSpace* space) const; - - // Push an object onto the mark stack. - void MarkStackPush(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_); - - void UpdateAndMarkModUnion() - REQUIRES(Locks::heap_bitmap_lock_) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Recursively blackens objects on the mark stack. - void ProcessMarkStack() - REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_); - - // 3 pass mark compact approach. - void Compact() REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_); - // Calculate the forwarding address of objects marked as "live" in the objects_before_forwarding - // bitmap. - void CalculateObjectForwardingAddresses() - REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_); - // Update the references of objects by using the forwarding addresses. - void UpdateReferences() REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_); - // Move objects and restore lock words. - void MoveObjects() REQUIRES(Locks::mutator_lock_); - // Move a single object to its forward address. - void MoveObject(mirror::Object* obj, size_t len) REQUIRES(Locks::mutator_lock_); - // Mark a single object. - virtual mirror::Object* MarkObject(mirror::Object* obj) OVERRIDE - REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - virtual void MarkHeapReference(mirror::HeapReference* obj_ptr, - bool do_atomic_update) OVERRIDE - REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - virtual mirror::Object* IsMarked(mirror::Object* obj) OVERRIDE - REQUIRES_SHARED(Locks::heap_bitmap_lock_) - REQUIRES(Locks::mutator_lock_); - virtual bool IsNullOrMarkedHeapReference(mirror::HeapReference* obj, - bool do_atomic_update) OVERRIDE - REQUIRES_SHARED(Locks::heap_bitmap_lock_) - REQUIRES(Locks::mutator_lock_); - void ForwardObject(mirror::Object* obj) REQUIRES(Locks::heap_bitmap_lock_, - Locks::mutator_lock_); - // Update a single heap reference. - void UpdateHeapReference(mirror::HeapReference* reference) - REQUIRES_SHARED(Locks::heap_bitmap_lock_) - REQUIRES(Locks::mutator_lock_); - // Update all of the references of a single object. - void UpdateObjectReferences(mirror::Object* obj) - REQUIRES_SHARED(Locks::heap_bitmap_lock_) - REQUIRES(Locks::mutator_lock_); - - // Revoke all the thread-local buffers. - void RevokeAllThreadLocalBuffers(); - - accounting::ObjectStack* mark_stack_; - - // Every object inside the immune spaces is assumed to be marked. - ImmuneSpaces immune_spaces_; - - // Bump pointer space which we are collecting. - space::BumpPointerSpace* space_; - // Cached mark bitmap as an optimization. - accounting::HeapBitmap* mark_bitmap_; - - // The name of the collector. - std::string collector_name_; - - // The bump pointer in the space where the next forwarding address will be. - uint8_t* bump_pointer_; - // How many live objects we have in the space. - size_t live_objects_in_space_; - - // Bitmap which describes which objects we have to move, need to do / 2 so that we can handle - // objects which are only 8 bytes. - std::unique_ptr objects_before_forwarding_; - // Bitmap which describes which lock words we need to restore. - std::unique_ptr objects_with_lockword_; - // Which lock words we need to restore as we are moving objects. - std::deque lock_words_to_restore_; - - // State whether or not we are updating references. - bool updating_references_; - - private: - class MarkObjectVisitor; - class UpdateObjectReferencesVisitor; - class UpdateReferenceVisitor; - class UpdateRootVisitor; - - DISALLOW_IMPLICIT_CONSTRUCTORS(MarkCompact); -}; - -} // namespace collector -} // namespace gc -} // namespace art - -#endif // ART_RUNTIME_GC_COLLECTOR_MARK_COMPACT_H_ diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 9ab965ec78c26c65bc46a315065c7c61fd4916fa..23359640fe3bca73d3596dd4086c309f61e418ee 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -116,21 +116,21 @@ void MarkSweep::InitializePhase() { mark_stack_ = heap_->GetMarkStack(); DCHECK(mark_stack_ != nullptr); immune_spaces_.Reset(); - no_reference_class_count_.StoreRelaxed(0); - normal_count_.StoreRelaxed(0); - class_count_.StoreRelaxed(0); - object_array_count_.StoreRelaxed(0); - other_count_.StoreRelaxed(0); - reference_count_.StoreRelaxed(0); - large_object_test_.StoreRelaxed(0); - large_object_mark_.StoreRelaxed(0); - overhead_time_ .StoreRelaxed(0); - work_chunks_created_.StoreRelaxed(0); - work_chunks_deleted_.StoreRelaxed(0); - mark_null_count_.StoreRelaxed(0); - mark_immune_count_.StoreRelaxed(0); - mark_fastpath_count_.StoreRelaxed(0); - mark_slowpath_count_.StoreRelaxed(0); + no_reference_class_count_.store(0, std::memory_order_relaxed); + normal_count_.store(0, std::memory_order_relaxed); + class_count_.store(0, std::memory_order_relaxed); + object_array_count_.store(0, std::memory_order_relaxed); + other_count_.store(0, std::memory_order_relaxed); + reference_count_.store(0, std::memory_order_relaxed); + large_object_test_.store(0, std::memory_order_relaxed); + large_object_mark_.store(0, std::memory_order_relaxed); + overhead_time_ .store(0, std::memory_order_relaxed); + work_chunks_created_.store(0, std::memory_order_relaxed); + work_chunks_deleted_.store(0, std::memory_order_relaxed); + mark_null_count_.store(0, std::memory_order_relaxed); + mark_immune_count_.store(0, std::memory_order_relaxed); + mark_fastpath_count_.store(0, std::memory_order_relaxed); + mark_slowpath_count_.store(0, std::memory_order_relaxed); { // TODO: I don't think we should need heap bitmap lock to Get the mark bitmap. ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); @@ -724,7 +724,7 @@ class MarkSweep::MarkStackTask : public Task { if (kUseFinger) { std::atomic_thread_fence(std::memory_order_seq_cst); if (reinterpret_cast(ref) >= - static_cast(mark_sweep_->atomic_finger_.LoadRelaxed())) { + static_cast(mark_sweep_->atomic_finger_.load(std::memory_order_relaxed))) { return; } } @@ -1046,7 +1046,7 @@ void MarkSweep::RecursiveMark() { // This function does not handle heap end increasing, so we must use the space end. uintptr_t begin = reinterpret_cast(space->Begin()); uintptr_t end = reinterpret_cast(space->End()); - atomic_finger_.StoreRelaxed(AtomicInteger::MaxValue()); + atomic_finger_.store(AtomicInteger::MaxValue(), std::memory_order_relaxed); // Create a few worker tasks. const size_t n = thread_count * 2; @@ -1405,8 +1405,8 @@ void MarkSweep::ProcessMarkStackParallel(size_t thread_count) { thread_pool->Wait(self, true, true); thread_pool->StopWorkers(self); mark_stack_->Reset(); - CHECK_EQ(work_chunks_created_.LoadSequentiallyConsistent(), - work_chunks_deleted_.LoadSequentiallyConsistent()) + CHECK_EQ(work_chunks_created_.load(std::memory_order_seq_cst), + work_chunks_deleted_.load(std::memory_order_seq_cst)) << " some of the work chunks were leaked"; } @@ -1462,28 +1462,32 @@ void MarkSweep::FinishPhase() { if (kCountScannedTypes) { VLOG(gc) << "MarkSweep scanned" - << " no reference objects=" << no_reference_class_count_.LoadRelaxed() - << " normal objects=" << normal_count_.LoadRelaxed() - << " classes=" << class_count_.LoadRelaxed() - << " object arrays=" << object_array_count_.LoadRelaxed() - << " references=" << reference_count_.LoadRelaxed() - << " other=" << other_count_.LoadRelaxed(); + << " no reference objects=" << no_reference_class_count_.load(std::memory_order_relaxed) + << " normal objects=" << normal_count_.load(std::memory_order_relaxed) + << " classes=" << class_count_.load(std::memory_order_relaxed) + << " object arrays=" << object_array_count_.load(std::memory_order_relaxed) + << " references=" << reference_count_.load(std::memory_order_relaxed) + << " other=" << other_count_.load(std::memory_order_relaxed); } if (kCountTasks) { - VLOG(gc) << "Total number of work chunks allocated: " << work_chunks_created_.LoadRelaxed(); + VLOG(gc) + << "Total number of work chunks allocated: " + << work_chunks_created_.load(std::memory_order_relaxed); } if (kMeasureOverhead) { - VLOG(gc) << "Overhead time " << PrettyDuration(overhead_time_.LoadRelaxed()); + VLOG(gc) << "Overhead time " << PrettyDuration(overhead_time_.load(std::memory_order_relaxed)); } if (kProfileLargeObjects) { - VLOG(gc) << "Large objects tested " << large_object_test_.LoadRelaxed() - << " marked " << large_object_mark_.LoadRelaxed(); + VLOG(gc) + << "Large objects tested " << large_object_test_.load(std::memory_order_relaxed) + << " marked " << large_object_mark_.load(std::memory_order_relaxed); } if (kCountMarkedObjects) { - VLOG(gc) << "Marked: null=" << mark_null_count_.LoadRelaxed() - << " immune=" << mark_immune_count_.LoadRelaxed() - << " fastpath=" << mark_fastpath_count_.LoadRelaxed() - << " slowpath=" << mark_slowpath_count_.LoadRelaxed(); + VLOG(gc) + << "Marked: null=" << mark_null_count_.load(std::memory_order_relaxed) + << " immune=" << mark_immune_count_.load(std::memory_order_relaxed) + << " fastpath=" << mark_fastpath_count_.load(std::memory_order_relaxed) + << " slowpath=" << mark_slowpath_count_.load(std::memory_order_relaxed); } CHECK(mark_stack_->IsEmpty()); // Ensure that the mark stack is empty. mark_stack_->Reset(); diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index 1e136bca2efba883e3ccaa59f16a2821b92de6bf..8cd484fc4800da56b942407b62a018c4f36bc970 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -39,7 +39,7 @@ #include "gc/space/space-inl.h" #include "indirect_reference_table.h" #include "intern_table.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mark_sweep-inl.h" #include "mirror/object-inl.h" #include "mirror/object-refvisitor-inl.h" @@ -48,6 +48,7 @@ #include "runtime.h" #include "thread-inl.h" #include "thread_list.h" +#include "write_barrier-inl.h" using ::art::mirror::Object; @@ -531,7 +532,7 @@ mirror::Object* SemiSpace::MarkNonForwardedObject(mirror::Object* obj) { // Dirty the card at the destionation as it may contain // references (including the class pointer) to the bump pointer // space. - GetHeap()->WriteBarrierEveryFieldOf(forward_address); + WriteBarrier::ForEveryFieldWrite(forward_address); // Handle the bitmaps marking. accounting::ContinuousSpaceBitmap* live_bitmap = promo_dest_space_->GetLiveBitmap(); DCHECK(live_bitmap != nullptr); diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h index 8979e742c87b24c8415e616d4d45a393dd62773c..4759fca46c52a7fdd232de222d943f7118f0d498 100644 --- a/runtime/gc/collector_type.h +++ b/runtime/gc/collector_type.h @@ -34,8 +34,6 @@ enum CollectorType { kCollectorTypeSS, // A generational variant of kCollectorTypeSS. kCollectorTypeGSS, - // Mark compact collector. - kCollectorTypeMC, // Heap trimming collector, doesn't do any actual collecting. kCollectorTypeHeapTrim, // A (mostly) concurrent copying collector. diff --git a/runtime/gc/gc_cause.cc b/runtime/gc/gc_cause.cc index 508d76535e3857c55f29f09c1e44d3ab3c8f93c0..ee7ac7dae0f764dfee55ff726868fc7f1402f743 100644 --- a/runtime/gc/gc_cause.cc +++ b/runtime/gc/gc_cause.cc @@ -18,8 +18,8 @@ #include +#include "base/globals.h" #include "base/macros.h" -#include "globals.h" #include diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 41ee18350dbcf4c9a8c46e54fe8a353b9b3f5c8a..791d0378d13e402417067892df381db74123f725 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -37,6 +37,7 @@ #include "runtime.h" #include "thread-inl.h" #include "verify_object.h" +#include "write_barrier-inl.h" namespace art { namespace gc { @@ -151,12 +152,12 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, // enabled. We don't need this for kAllocatorTypeRosAlloc/DlMalloc // cases because we don't directly allocate into the main alloc // space (besides promotions) under the SS/GSS collector. - WriteBarrierField(obj, mirror::Object::ClassOffset(), klass); + WriteBarrier::ForFieldWrite(obj, mirror::Object::ClassOffset(), klass); } pre_fence_visitor(obj, usable_size); QuasiAtomic::ThreadFenceForConstructor(); size_t num_bytes_allocated_before = - num_bytes_allocated_.FetchAndAddRelaxed(bytes_tl_bulk_allocated); + num_bytes_allocated_.fetch_add(bytes_tl_bulk_allocated, std::memory_order_relaxed); new_num_bytes_allocated = num_bytes_allocated_before + bytes_tl_bulk_allocated; if (bytes_tl_bulk_allocated > 0) { // Only trace when we get an increase in the number of bytes allocated. This happens when @@ -187,7 +188,7 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, DCHECK(allocation_records_ != nullptr); allocation_records_->RecordAllocation(self, &obj, bytes_allocated); } - AllocationListener* l = alloc_listener_.LoadSequentiallyConsistent(); + AllocationListener* l = alloc_listener_.load(std::memory_order_seq_cst); if (l != nullptr) { // Same as above. We assume that a listener that was once stored will never be deleted. // Otherwise we'd have to perform this under a lock. @@ -272,7 +273,7 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, } 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 +304,7 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, } 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, @@ -393,7 +394,7 @@ inline bool Heap::ShouldAllocLargeObject(ObjPtr c, size_t byte_co inline bool Heap::IsOutOfMemoryOnAllocation(AllocatorType allocator_type, size_t alloc_size, bool grow) { - size_t new_footprint = num_bytes_allocated_.LoadSequentiallyConsistent() + alloc_size; + size_t new_footprint = num_bytes_allocated_.load(std::memory_order_seq_cst) + alloc_size; if (UNLIKELY(new_footprint > max_allowed_footprint_)) { if (UNLIKELY(new_footprint > growth_limit_)) { return true; @@ -418,22 +419,6 @@ inline void Heap::CheckConcurrentGC(Thread* self, } } -inline void Heap::WriteBarrierField(ObjPtr dst, - MemberOffset offset ATTRIBUTE_UNUSED, - ObjPtr new_value ATTRIBUTE_UNUSED) { - card_table_->MarkCard(dst.Ptr()); -} - -inline void Heap::WriteBarrierArray(ObjPtr dst, - int start_offset ATTRIBUTE_UNUSED, - size_t length ATTRIBUTE_UNUSED) { - card_table_->MarkCard(dst.Ptr()); -} - -inline void Heap::WriteBarrierEveryFieldOf(ObjPtr obj) { - card_table_->MarkCard(obj.Ptr()); -} - } // namespace gc } // namespace art diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 3011c37f3af02cd3b972c7773e7526c7856b2375..58becb1d09fe4c97eae1d7df04ed60857bcdee31 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -37,7 +37,6 @@ #include "base/systrace.h" #include "base/time_utils.h" #include "common_throws.h" -#include "cutils/sched_policy.h" #include "debugger.h" #include "dex/dex_file-inl.h" #include "entrypoints/quick/quick_alloc_entrypoints.h" @@ -48,7 +47,6 @@ #include "gc/accounting/remembered_set.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/collector/concurrent_copying.h" -#include "gc/collector/mark_compact.h" #include "gc/collector/mark_sweep.h" #include "gc/collector/partial_mark_sweep.h" #include "gc/collector/semi_space.h" @@ -72,9 +70,9 @@ #include "heap-visit-objects-inl.h" #include "image.h" #include "intern_table.h" -#include "java_vm_ext.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" +#include "jni/java_vm_ext.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object-refvisitor-inl.h" @@ -143,6 +141,10 @@ static constexpr bool kLogAllGCs = false; static constexpr size_t kPartialTlabSize = 16 * KB; static constexpr bool kUsePartialTlabs = true; +// Use Max heap for 2 seconds, this is smaller than the usual 5s window since we don't want to leave +// allocate with relaxed ergonomics for that long. +static constexpr size_t kPostForkMaxHeapDurationMS = 2000; + #if defined(__LP64__) || !defined(ADDRESS_SANITIZER) // 300 MB (0x12c00000) - (default non-moving space capacity). uint8_t* const Heap::kPreferredAllocSpaceBegin = @@ -258,7 +260,6 @@ Heap::Heap(size_t initial_size, verify_object_mode_(kVerifyObjectModeDisabled), disable_moving_gc_count_(0), semi_space_collector_(nullptr), - mark_compact_collector_(nullptr), concurrent_copying_collector_(nullptr), is_running_on_memory_tool_(Runtime::Current()->IsRunningOnMemoryTool()), use_tlab_(use_tlab), @@ -545,7 +546,7 @@ Heap::Heap(size_t initial_size, AddRememberedSet(non_moving_space_rem_set); } // TODO: Count objects in the image space here? - num_bytes_allocated_.StoreRelaxed(0); + num_bytes_allocated_.store(0, std::memory_order_relaxed); mark_stack_.reset(accounting::ObjectStack::Create("mark stack", kDefaultMarkStackSize, kDefaultMarkStackSize)); const size_t alloc_stack_capacity = max_allocation_stack_size_ + kAllocationStackReserveSize; @@ -599,10 +600,6 @@ Heap::Heap(size_t initial_size, concurrent_copying_collector_->SetRegionSpace(region_space_); garbage_collectors_.push_back(concurrent_copying_collector_); } - if (MayUseCollector(kCollectorTypeMC)) { - mark_compact_collector_ = new collector::MarkCompact(this); - garbage_collectors_.push_back(mark_compact_collector_); - } } if (!GetBootImageSpaces().empty() && non_moving_space_ != nullptr && (is_zygote || separate_non_moving_space || foreground_collector_type_ == kCollectorTypeGSS)) { @@ -1049,7 +1046,8 @@ void Heap::DumpGcPerformanceInfo(std::ostream& os) { } os << "Registered native bytes allocated: " - << old_native_bytes_allocated_.LoadRelaxed() + new_native_bytes_allocated_.LoadRelaxed() + << (old_native_bytes_allocated_.load(std::memory_order_relaxed) + + new_native_bytes_allocated_.load(std::memory_order_relaxed)) << "\n"; BaseMutex::DumpAll(os); @@ -1116,11 +1114,7 @@ void Heap::DumpBlockingGcCountRateHistogram(std::ostream& os) const { ALWAYS_INLINE static inline AllocationListener* GetAndOverwriteAllocationListener( Atomic* storage, AllocationListener* new_value) { - AllocationListener* old; - do { - old = storage->LoadSequentiallyConsistent(); - } while (!storage->CompareAndSetStrongSequentiallyConsistent(old, new_value)); - return old; + return storage->exchange(new_value); } Heap::~Heap() { @@ -1138,12 +1132,11 @@ Heap::~Heap() { delete thread_flip_lock_; delete pending_task_lock_; delete backtrace_lock_; - if (unique_backtrace_count_.LoadRelaxed() != 0 || seen_backtrace_count_.LoadRelaxed() != 0) { - LOG(INFO) << "gc stress unique=" << unique_backtrace_count_.LoadRelaxed() - << " total=" << seen_backtrace_count_.LoadRelaxed() + - unique_backtrace_count_.LoadRelaxed(); + uint64_t unique_count = unique_backtrace_count_.load(std::memory_order_relaxed); + uint64_t seen_count = seen_backtrace_count_.load(std::memory_order_relaxed); + if (unique_count != 0 || seen_count != 0) { + LOG(INFO) << "gc stress unique=" << unique_count << " total=" << (unique_count + seen_count); } - VLOG(heap) << "Finished ~Heap()"; } @@ -1209,7 +1202,8 @@ void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, AllocatorType // If we're in a stack overflow, do not create a new exception. It would require running the // constructor, which will of course still be in a stack overflow. if (self->IsHandlingStackOverflow()) { - self->SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryError()); + self->SetException( + Runtime::Current()->GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow()); return; } @@ -1489,7 +1483,7 @@ void Heap::VerifyObjectBody(ObjPtr obj) { } // Ignore early dawn of the universe verifications. - if (UNLIKELY(static_cast(num_bytes_allocated_.LoadRelaxed()) < 10 * KB)) { + if (UNLIKELY(num_bytes_allocated_.load(std::memory_order_relaxed) < 10 * KB)) { return; } CHECK_ALIGNED(obj.Ptr(), kObjectAlignment) << "Object isn't aligned"; @@ -1521,9 +1515,10 @@ void Heap::RecordFree(uint64_t freed_objects, int64_t freed_bytes) { // Use signed comparison since freed bytes can be negative when background compaction foreground // transitions occurs. This is caused by the moving objects from a bump pointer space to a // free list backed space typically increasing memory footprint due to padding and binning. - DCHECK_LE(freed_bytes, static_cast(num_bytes_allocated_.LoadRelaxed())); + DCHECK_LE(freed_bytes, + static_cast(num_bytes_allocated_.load(std::memory_order_relaxed))); // Note: This relies on 2s complement for handling negative freed_bytes. - num_bytes_allocated_.FetchAndSubSequentiallyConsistent(static_cast(freed_bytes)); + num_bytes_allocated_.fetch_sub(static_cast(freed_bytes)); if (Runtime::Current()->HasStatsEnabled()) { RuntimeStats* thread_stats = Thread::Current()->GetStats(); thread_stats->freed_objects += freed_objects; @@ -1540,10 +1535,10 @@ void Heap::RecordFreeRevoke() { // ahead-of-time, bulk counting of bytes allocated in rosalloc thread-local buffers. // If there's a concurrent revoke, ok to not necessarily reset num_bytes_freed_revoke_ // all the way to zero exactly as the remainder will be subtracted at the next GC. - size_t bytes_freed = num_bytes_freed_revoke_.LoadSequentiallyConsistent(); - CHECK_GE(num_bytes_freed_revoke_.FetchAndSubSequentiallyConsistent(bytes_freed), + size_t bytes_freed = num_bytes_freed_revoke_.load(); + CHECK_GE(num_bytes_freed_revoke_.fetch_sub(bytes_freed), bytes_freed) << "num_bytes_freed_revoke_ underflow"; - CHECK_GE(num_bytes_allocated_.FetchAndSubSequentiallyConsistent(bytes_freed), + CHECK_GE(num_bytes_allocated_.fetch_sub(bytes_freed), bytes_freed) << "num_bytes_allocated_ underflow"; GetCurrentGcIteration()->SetFreedRevoke(bytes_freed); } @@ -1699,13 +1694,13 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, // Always print that we ran homogeneous space compation since this can cause jank. VLOG(heap) << "Ran heap homogeneous space compaction, " << " requested defragmentation " - << count_requested_homogeneous_space_compaction_.LoadSequentiallyConsistent() + << count_requested_homogeneous_space_compaction_.load() << " performed defragmentation " - << count_performed_homogeneous_space_compaction_.LoadSequentiallyConsistent() + << count_performed_homogeneous_space_compaction_.load() << " ignored homogeneous space compaction " - << count_ignored_homogeneous_space_compaction_.LoadSequentiallyConsistent() + << count_ignored_homogeneous_space_compaction_.load() << " delayed count = " - << count_delayed_oom_.LoadSequentiallyConsistent(); + << count_delayed_oom_.load(); } break; } @@ -1968,7 +1963,7 @@ void Heap::TransitionCollector(CollectorType collector_type) { VLOG(heap) << "TransitionCollector: " << static_cast(collector_type_) << " -> " << static_cast(collector_type); uint64_t start_time = NanoTime(); - uint32_t before_allocated = num_bytes_allocated_.LoadSequentiallyConsistent(); + uint32_t before_allocated = num_bytes_allocated_.load(); Runtime* const runtime = Runtime::Current(); Thread* const self = Thread::Current(); ScopedThreadStateChange tsc(self, kWaitingPerformingGc); @@ -2106,7 +2101,7 @@ void Heap::TransitionCollector(CollectorType collector_type) { ScopedObjectAccess soa(self); soa.Vm()->UnloadNativeLibraries(); } - int32_t after_allocated = num_bytes_allocated_.LoadSequentiallyConsistent(); + int32_t after_allocated = num_bytes_allocated_.load(std::memory_order_seq_cst); int32_t delta_allocated = before_allocated - after_allocated; std::string saved_str; if (delta_allocated >= 0) { @@ -2121,10 +2116,6 @@ void Heap::TransitionCollector(CollectorType collector_type) { void Heap::ChangeCollector(CollectorType collector_type) { // TODO: Only do this with all mutators suspended to avoid races. if (collector_type != collector_type_) { - if (collector_type == kCollectorTypeMC) { - // Don't allow mark compact unless support is compiled in. - CHECK(kMarkCompactSupport); - } collector_type_ = collector_type; gc_plan_.clear(); switch (collector_type_) { @@ -2137,7 +2128,6 @@ void Heap::ChangeCollector(CollectorType collector_type) { } break; } - case kCollectorTypeMC: // Fall-through. case kCollectorTypeSS: // Fall-through. case kCollectorTypeGSS: { gc_plan_.push_back(collector::kGcTypeFull); @@ -2257,7 +2247,8 @@ class ZygoteCompactingCollector FINAL : public collector::SemiSpace { // 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(forward_address), obj, obj_size); if (kUseBakerReadBarrier) { obj->AssertReadBarrierState(); @@ -2486,13 +2477,9 @@ collector::GarbageCollector* Heap::Compact(space::ContinuousMemMapAllocSpace* ta semi_space_collector_->SetToSpace(target_space); semi_space_collector_->Run(gc_cause, false); return semi_space_collector_; - } else { - CHECK(target_space->IsBumpPointerSpace()) - << "In-place compaction is only supported for bump pointer spaces"; - mark_compact_collector_->SetSpace(target_space->AsBumpPointerSpace()); - mark_compact_collector_->Run(kGcCauseCollectorTransition, false); - return mark_compact_collector_; } + LOG(FATAL) << "Unsupported"; + UNREACHABLE(); } void Heap::TraceHeapSize(size_t heap_size) { @@ -2555,7 +2542,9 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, // Move all bytes from new_native_bytes_allocated_ to // old_native_bytes_allocated_ now that GC has been triggered, resetting // new_native_bytes_allocated_ to zero in the process. - old_native_bytes_allocated_.FetchAndAddRelaxed(new_native_bytes_allocated_.ExchangeRelaxed(0)); + old_native_bytes_allocated_.fetch_add( + new_native_bytes_allocated_.exchange(0, std::memory_order_relaxed), + std::memory_order_relaxed); } DCHECK_LT(gc_type, collector::kGcTypeMax); @@ -2580,14 +2569,10 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, case kCollectorTypeCC: collector = concurrent_copying_collector_; break; - case kCollectorTypeMC: - mark_compact_collector_->SetSpace(bump_pointer_space_); - collector = mark_compact_collector_; - break; default: LOG(FATAL) << "Invalid collector type " << static_cast(collector_type_); } - if (collector != mark_compact_collector_ && collector != concurrent_copying_collector_) { + if (collector != concurrent_copying_collector_) { temp_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); if (kIsDebugBuild) { // Try to read each page of the memory map in case mprotect didn't work properly b/19894268. @@ -2750,12 +2735,10 @@ class ScanVisitor { // Verify a reference from an object. class VerifyReferenceVisitor : public SingleRootVisitor { public: - VerifyReferenceVisitor(Heap* heap, Atomic* fail_count, bool verify_referent) + VerifyReferenceVisitor(Thread* self, Heap* heap, size_t* fail_count, bool verify_referent) REQUIRES_SHARED(Locks::mutator_lock_) - : heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) {} - - size_t GetFailureCount() const { - return fail_count_->LoadSequentiallyConsistent(); + : self_(self), heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) { + CHECK_EQ(self_, Thread::Current()); } void operator()(ObjPtr klass ATTRIBUTE_UNUSED, ObjPtr ref) const @@ -2807,8 +2790,10 @@ class VerifyReferenceVisitor : public SingleRootVisitor { // Verify that the reference is live. return true; } - if (fail_count_->FetchAndAddSequentiallyConsistent(1) == 0) { - // Print message on only on first failure to prevent spam. + CHECK_EQ(self_, Thread::Current()); // fail_count_ is private to the calling thread. + *fail_count_ += 1; + if (*fail_count_ == 1) { + // Only print message for the first failure to prevent spam. LOG(ERROR) << "!!!!!!!!!!!!!!Heap corruption detected!!!!!!!!!!!!!!!!!!!"; } if (obj != nullptr) { @@ -2894,38 +2879,41 @@ class VerifyReferenceVisitor : public SingleRootVisitor { return false; } + Thread* const self_; Heap* const heap_; - Atomic* const fail_count_; + size_t* const fail_count_; const bool verify_referent_; }; // Verify all references within an object, for use with HeapBitmap::Visit. class VerifyObjectVisitor { public: - VerifyObjectVisitor(Heap* heap, Atomic* fail_count, bool verify_referent) - : heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) {} + VerifyObjectVisitor(Thread* self, Heap* heap, size_t* fail_count, bool verify_referent) + : self_(self), heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) {} void operator()(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { // Note: we are verifying the references in obj but not obj itself, this is because obj must // be live or else how did we find it in the live bitmap? - VerifyReferenceVisitor visitor(heap_, fail_count_, verify_referent_); + VerifyReferenceVisitor visitor(self_, heap_, fail_count_, verify_referent_); // The class doesn't count as a reference but we should verify it anyways. obj->VisitReferences(visitor, visitor); } void VerifyRoots() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::heap_bitmap_lock_) { ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - VerifyReferenceVisitor visitor(heap_, fail_count_, verify_referent_); + VerifyReferenceVisitor visitor(self_, heap_, fail_count_, verify_referent_); Runtime::Current()->VisitRoots(&visitor); } - size_t GetFailureCount() const { - return fail_count_->LoadSequentiallyConsistent(); + uint32_t GetFailureCount() const REQUIRES(Locks::mutator_lock_) { + CHECK_EQ(self_, Thread::Current()); + return *fail_count_; } private: + Thread* const self_; Heap* const heap_; - Atomic* const fail_count_; + size_t* const fail_count_; const bool verify_referent_; }; @@ -2977,8 +2965,8 @@ size_t Heap::VerifyHeapReferences(bool verify_referents) { // Since we sorted the allocation stack content, need to revoke all // thread-local allocation stacks. RevokeAllThreadLocalAllocationStacks(self); - Atomic fail_count_(0); - VerifyObjectVisitor visitor(this, &fail_count_, verify_referents); + size_t fail_count = 0; + VerifyObjectVisitor visitor(self, this, &fail_count, verify_referents); // Verify objects in the allocation stack since these will be objects which were: // 1. Allocated prior to the GC (pre GC verification). // 2. Allocated during the GC (pre sweep GC verification). @@ -3539,6 +3527,12 @@ void Heap::ClampGrowthLimit() { } void Heap::ClearGrowthLimit() { + if (max_allowed_footprint_ == growth_limit_ && growth_limit_ < capacity_) { + max_allowed_footprint_ = capacity_; + concurrent_start_bytes_ = + std::max(max_allowed_footprint_, kMinConcurrentRemainingBytes) - + kMinConcurrentRemainingBytes; + } growth_limit_ = capacity_; ScopedObjectAccess soa(Thread::Current()); for (const auto& space : continuous_spaces_) { @@ -3595,7 +3589,7 @@ static bool CanAddHeapTask(Thread* self) REQUIRES(!Locks::runtime_shutdown_lock_ } void Heap::ClearConcurrentGCRequest() { - concurrent_gc_pending_.StoreRelaxed(false); + concurrent_gc_pending_.store(false, std::memory_order_relaxed); } void Heap::RequestConcurrentGC(Thread* self, GcCause cause, bool force_full) { @@ -3718,12 +3712,21 @@ void Heap::RequestTrim(Thread* self) { task_processor_->AddTask(self, added_task); } +void Heap::IncrementNumberOfBytesFreedRevoke(size_t freed_bytes_revoke) { + size_t previous_num_bytes_freed_revoke = + num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst); + // Check the updated value is less than the number of bytes allocated. There is a risk of + // execution being suspended between the increment above and the CHECK below, leading to + // the use of previous_num_bytes_freed_revoke in the comparison. + CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed), + previous_num_bytes_freed_revoke + freed_bytes_revoke); +} + void Heap::RevokeThreadLocalBuffers(Thread* thread) { if (rosalloc_space_ != nullptr) { size_t freed_bytes_revoke = rosalloc_space_->RevokeThreadLocalBuffers(thread); if (freed_bytes_revoke > 0U) { - num_bytes_freed_revoke_.FetchAndAddSequentiallyConsistent(freed_bytes_revoke); - CHECK_GE(num_bytes_allocated_.LoadRelaxed(), num_bytes_freed_revoke_.LoadRelaxed()); + IncrementNumberOfBytesFreedRevoke(freed_bytes_revoke); } } if (bump_pointer_space_ != nullptr) { @@ -3738,8 +3741,7 @@ void Heap::RevokeRosAllocThreadLocalBuffers(Thread* thread) { if (rosalloc_space_ != nullptr) { size_t freed_bytes_revoke = rosalloc_space_->RevokeThreadLocalBuffers(thread); if (freed_bytes_revoke > 0U) { - num_bytes_freed_revoke_.FetchAndAddSequentiallyConsistent(freed_bytes_revoke); - CHECK_GE(num_bytes_allocated_.LoadRelaxed(), num_bytes_freed_revoke_.LoadRelaxed()); + IncrementNumberOfBytesFreedRevoke(freed_bytes_revoke); } } } @@ -3748,8 +3750,7 @@ void Heap::RevokeAllThreadLocalBuffers() { if (rosalloc_space_ != nullptr) { size_t freed_bytes_revoke = rosalloc_space_->RevokeAllThreadLocalBuffers(); if (freed_bytes_revoke > 0U) { - num_bytes_freed_revoke_.FetchAndAddSequentiallyConsistent(freed_bytes_revoke); - CHECK_GE(num_bytes_allocated_.LoadRelaxed(), num_bytes_freed_revoke_.LoadRelaxed()); + IncrementNumberOfBytesFreedRevoke(freed_bytes_revoke); } } if (bump_pointer_space_ != nullptr) { @@ -3761,7 +3762,7 @@ void Heap::RevokeAllThreadLocalBuffers() { } bool Heap::IsGCRequestPending() const { - return concurrent_gc_pending_.LoadRelaxed(); + return concurrent_gc_pending_.load(std::memory_order_relaxed); } void Heap::RunFinalization(JNIEnv* env, uint64_t timeout) { @@ -3771,7 +3772,7 @@ void Heap::RunFinalization(JNIEnv* env, uint64_t timeout) { } void Heap::RegisterNativeAllocation(JNIEnv* env, size_t bytes) { - size_t old_value = new_native_bytes_allocated_.FetchAndAddRelaxed(bytes); + size_t old_value = new_native_bytes_allocated_.fetch_add(bytes, std::memory_order_relaxed); if (old_value > NativeAllocationGcWatermark() * HeapGrowthMultiplier() && !IsGCRequestPending()) { @@ -3793,12 +3794,12 @@ void Heap::RegisterNativeFree(JNIEnv*, size_t bytes) { size_t allocated; size_t new_freed_bytes; do { - allocated = new_native_bytes_allocated_.LoadRelaxed(); + allocated = new_native_bytes_allocated_.load(std::memory_order_relaxed); new_freed_bytes = std::min(allocated, bytes); } while (!new_native_bytes_allocated_.CompareAndSetWeakRelaxed(allocated, allocated - new_freed_bytes)); if (new_freed_bytes < bytes) { - old_native_bytes_allocated_.FetchAndSubRelaxed(bytes - new_freed_bytes); + old_native_bytes_allocated_.fetch_sub(bytes - new_freed_bytes, std::memory_order_relaxed); } } @@ -3912,9 +3913,9 @@ void Heap::BroadcastForNewAllocationRecords() const { } void Heap::CheckGcStressMode(Thread* self, ObjPtr* obj) { + DCHECK(gc_stress_mode_); auto* const runtime = Runtime::Current(); - if (gc_stress_mode_ && runtime->GetClassLinker()->IsInitialized() && - !runtime->IsActiveTransaction() && mirror::Class::HasJavaLangClass()) { + if (runtime->GetClassLinker()->IsInitialized() && !runtime->IsActiveTransaction()) { // Check if we should GC. bool new_backtrace = false; { @@ -3932,9 +3933,9 @@ void Heap::CheckGcStressMode(Thread* self, ObjPtr* obj) { StackHandleScope<1> hs(self); auto h = hs.NewHandleWrapper(obj); CollectGarbage(/* clear_soft_references */ false); - unique_backtrace_count_.FetchAndAddSequentiallyConsistent(1); + unique_backtrace_count_.fetch_add(1, std::memory_order_seq_cst); } else { - seen_backtrace_count_.FetchAndAddSequentiallyConsistent(1); + seen_backtrace_count_.fetch_add(1, std::memory_order_seq_cst); } } } @@ -4010,11 +4011,11 @@ void Heap::RemoveAllocationListener() { } void Heap::SetGcPauseListener(GcPauseListener* l) { - gc_pause_listener_.StoreRelaxed(l); + gc_pause_listener_.store(l, std::memory_order_relaxed); } void Heap::RemoveGcPauseListener() { - gc_pause_listener_.StoreRelaxed(nullptr); + gc_pause_listener_.store(nullptr, std::memory_order_relaxed); } mirror::Object* Heap::AllocWithNewTLAB(Thread* self, @@ -4110,5 +4111,32 @@ void Heap::VlogHeapGrowth(size_t max_allowed_footprint, size_t new_footprint, si << PrettySize(new_footprint) << " for a " << PrettySize(alloc_size) << " allocation"; } +class Heap::TriggerPostForkCCGcTask : public HeapTask { + public: + explicit TriggerPostForkCCGcTask(uint64_t target_time) : HeapTask(target_time) {} + void Run(Thread* self) OVERRIDE { + gc::Heap* heap = Runtime::Current()->GetHeap(); + // Trigger a GC, if not already done. The first GC after fork, whenever + // takes place, will adjust the thresholds to normal levels. + if (heap->max_allowed_footprint_ == heap->growth_limit_) { + heap->RequestConcurrentGC(self, kGcCauseBackground, false); + } + } +}; + +void Heap::PostForkChildAction(Thread* self) { + // Temporarily increase max_allowed_footprint_ and concurrent_start_bytes_ to + // max values to avoid GC during app launch. + if (collector_type_ == kCollectorTypeCC && !IsLowMemoryMode()) { + // Set max_allowed_footprint_ to the largest allowed value. + SetIdealFootprint(growth_limit_); + // Set concurrent_start_bytes_ to half of the heap size. + concurrent_start_bytes_ = std::max(max_allowed_footprint_ / 2, GetBytesAllocated()); + + GetTaskProcessor()->AddTask( + self, new TriggerPostForkCCGcTask(NanoTime() + MsToNs(kPostForkMaxHeapDurationMS))); + } +} + } // namespace gc } // namespace art diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 5ce01bc9d2f14112f3958a4af129f320d2f713df..d01437299ffdb3bd038fdd741a560ad801480c79 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -27,6 +27,7 @@ #include "allocator_type.h" #include "arch/instruction_set.h" #include "base/atomic.h" +#include "base/globals.h" #include "base/macros.h" #include "base/mutex.h" #include "base/runtime_debug.h" @@ -37,7 +38,6 @@ #include "gc/collector_type.h" #include "gc/gc_cause.h" #include "gc/space/large_object_space.h" -#include "globals.h" #include "handle.h" #include "obj_ptr.h" #include "offsets.h" @@ -84,7 +84,6 @@ class RememberedSet; namespace collector { class ConcurrentCopying; class GarbageCollector; -class MarkCompact; class MarkSweep; class SemiSpace; } // namespace collector @@ -467,23 +466,6 @@ class Heap { // Record the bytes freed by thread-local buffer revoke. void RecordFreeRevoke(); - // Must be called if a field of an Object in the heap changes, and before any GC safe-point. - // The call is not needed if null is stored in the field. - ALWAYS_INLINE void WriteBarrierField(ObjPtr dst, - MemberOffset offset, - ObjPtr new_value) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Write barrier for array operations that update many field positions - ALWAYS_INLINE void WriteBarrierArray(ObjPtr dst, - int start_offset, - // TODO: element_count or byte_count? - size_t length) - REQUIRES_SHARED(Locks::mutator_lock_); - - ALWAYS_INLINE void WriteBarrierEveryFieldOf(ObjPtr obj) - REQUIRES_SHARED(Locks::mutator_lock_); - accounting::CardTable* GetCardTable() const { return card_table_.get(); } @@ -496,7 +478,7 @@ class Heap { // Returns the number of bytes currently allocated. size_t GetBytesAllocated() const { - return num_bytes_allocated_.LoadSequentiallyConsistent(); + return num_bytes_allocated_.load(std::memory_order_seq_cst); } // Returns the number of objects currently allocated. @@ -546,7 +528,7 @@ class Heap { // Returns how much free memory we have until we need to grow the heap to perform an allocation. // Similar to GetFreeMemoryUntilGC. Implements java.lang.Runtime.freeMemory. size_t GetFreeMemory() const { - size_t byte_allocated = num_bytes_allocated_.LoadSequentiallyConsistent(); + size_t byte_allocated = num_bytes_allocated_.load(std::memory_order_seq_cst); size_t total_memory = GetTotalMemory(); // Make sure we don't get a negative number. return total_memory - std::min(total_memory, byte_allocated); @@ -775,11 +757,11 @@ class Heap { // Allocation tracking support // Callers to this function use double-checked locking to ensure safety on allocation_records_ bool IsAllocTrackingEnabled() const { - return alloc_tracking_enabled_.LoadRelaxed(); + return alloc_tracking_enabled_.load(std::memory_order_relaxed); } void SetAllocTrackingEnabled(bool enabled) REQUIRES(Locks::alloc_tracker_lock_) { - alloc_tracking_enabled_.StoreRelaxed(enabled); + alloc_tracking_enabled_.store(enabled, std::memory_order_relaxed); } AllocRecordObjectMap* GetAllocationRecords() const @@ -825,7 +807,7 @@ class Heap { void SetGcPauseListener(GcPauseListener* l); // Get the currently installed gc pause listener, or null. GcPauseListener* GetGcPauseListener() { - return gc_pause_listener_.LoadAcquire(); + return gc_pause_listener_.load(std::memory_order_acquire); } // Remove a gc pause listener. Note: the listener must not be deleted, as for performance // reasons, we assume it stays valid when we read it (so that we don't require a lock). @@ -833,10 +815,13 @@ class Heap { const Verification* GetVerification() const; + void PostForkChildAction(Thread* self); + private: class ConcurrentGCTask; class CollectorTransitionTask; class HeapTrimTask; + class TriggerPostForkCCGcTask; // Compact source space to target space. Returns the collector used. collector::GarbageCollector* Compact(space::ContinuousMemMapAllocSpace* target_space, @@ -880,7 +865,6 @@ class Heap { collector_type == kCollectorTypeGSS || collector_type == kCollectorTypeCC || collector_type == kCollectorTypeCCBackground || - collector_type == kCollectorTypeMC || collector_type == kCollectorTypeHomogeneousSpaceCompact; } bool ShouldAllocLargeObject(ObjPtr c, size_t byte_count) const @@ -1088,6 +1072,8 @@ class Heap { return max_free_; } + ALWAYS_INLINE void IncrementNumberOfBytesFreedRevoke(size_t freed_bytes_revoke); + void TraceHeapSize(size_t heap_size); // Remove a vlog code from heap-inl.h which is transitively included in half the world. @@ -1349,7 +1335,6 @@ class Heap { std::vector garbage_collectors_; collector::SemiSpace* semi_space_collector_; - collector::MarkCompact* mark_compact_collector_; collector::ConcurrentCopying* concurrent_copying_collector_; const bool is_running_on_memory_tool_; @@ -1437,7 +1422,6 @@ class Heap { friend class CollectorTransitionTask; friend class collector::GarbageCollector; - friend class collector::MarkCompact; friend class collector::ConcurrentCopying; friend class collector::MarkSweep; friend class collector::SemiSpace; diff --git a/runtime/gc/heap_verification_test.cc b/runtime/gc/heap_verification_test.cc index 40ee86ce79a10b8fb7d2a58e47fb31e1777bfd1f..6caca84854370cfc50b32d8f55372e72a95907ce 100644 --- a/runtime/gc/heap_verification_test.cc +++ b/runtime/gc/heap_verification_test.cc @@ -18,6 +18,7 @@ #include "base/memory_tool.h" #include "class_linker-inl.h" +#include "class_root.h" #include "handle_scope-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" @@ -34,12 +35,11 @@ class VerificationTest : public CommonRuntimeTest { VerificationTest() {} template - mirror::ObjectArray* AllocObjectArray(Thread* self, size_t length) + ObjPtr> AllocObjectArray(Thread* self, size_t length) REQUIRES_SHARED(Locks::mutator_lock_) { - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); return mirror::ObjectArray::Alloc( self, - class_linker->GetClassRoot(ClassLinker::ClassRoot::kObjectArrayClass), + GetClassRoot>(), length); } }; @@ -83,7 +83,7 @@ TEST_F(VerificationTest, IsValidClassOrNotInHeap) { } TEST_F(VerificationTest, IsValidClassInHeap) { - TEST_DISABLED_FOR_MEMORY_TOOL(); + TEST_DISABLED_FOR_MEMORY_TOOL_WITH_HEAP_POISONING(); ScopedObjectAccess soa(Thread::Current()); VariableSizedHandleScope hs(soa.Self()); Handle string( @@ -106,7 +106,7 @@ TEST_F(VerificationTest, DumpInvalidObjectInfo) { } TEST_F(VerificationTest, DumpValidObjectInfo) { - TEST_DISABLED_FOR_MEMORY_TOOL(); + TEST_DISABLED_FOR_MEMORY_TOOL_WITH_HEAP_POISONING(); ScopedLogSeverity sls(LogSeverity::INFO); ScopedObjectAccess soa(Thread::Current()); Runtime* const runtime = Runtime::Current(); @@ -126,7 +126,7 @@ TEST_F(VerificationTest, DumpValidObjectInfo) { } TEST_F(VerificationTest, LogHeapCorruption) { - TEST_DISABLED_FOR_MEMORY_TOOL(); + TEST_DISABLED_FOR_MEMORY_TOOL_WITH_HEAP_POISONING(); ScopedLogSeverity sls(LogSeverity::INFO); ScopedObjectAccess soa(Thread::Current()); Runtime* const runtime = Runtime::Current(); @@ -147,7 +147,6 @@ TEST_F(VerificationTest, LogHeapCorruption) { } TEST_F(VerificationTest, FindPathFromRootSet) { - TEST_DISABLED_FOR_MEMORY_TOOL(); ScopedLogSeverity sls(LogSeverity::INFO); ScopedObjectAccess soa(Thread::Current()); Runtime* const runtime = Runtime::Current(); diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc index 356f3ecaa8a5651cbdc5af67fc7991f25627f095..fe4124d7885a2f25b4a07a10fe6071bf2cf9881a 100644 --- a/runtime/gc/reference_processor.cc +++ b/runtime/gc/reference_processor.cc @@ -16,16 +16,17 @@ #include "reference_processor.h" +#include "art_field-inl.h" #include "base/time_utils.h" #include "base/utils.h" +#include "class_root.h" #include "collector/garbage_collector.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/reference-inl.h" #include "nativehelper/scoped_local_ref.h" #include "object_callbacks.h" -#include "reference_processor-inl.h" #include "reflection.h" #include "scoped_thread_state_change-inl.h" #include "task_processor.h" @@ -47,15 +48,37 @@ ReferenceProcessor::ReferenceProcessor() cleared_references_(Locks::reference_queue_cleared_references_lock_) { } +static inline MemberOffset GetSlowPathFlagOffset(ObjPtr reference_class) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(reference_class == GetClassRoot()); + // Second static field + ArtField* field = reference_class->GetStaticField(1); + DCHECK_STREQ(field->GetName(), "slowPathEnabled"); + return field->GetOffset(); +} + +static inline void SetSlowPathFlag(bool enabled) REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr reference_class = GetClassRoot(); + MemberOffset slow_path_offset = GetSlowPathFlagOffset(reference_class); + reference_class->SetFieldBoolean( + slow_path_offset, enabled ? 1 : 0); +} + void ReferenceProcessor::EnableSlowPath() { - mirror::Reference::GetJavaLangRefReference()->SetSlowPath(true); + SetSlowPathFlag(/* enabled */ true); } void ReferenceProcessor::DisableSlowPath(Thread* self) { - mirror::Reference::GetJavaLangRefReference()->SetSlowPath(false); + SetSlowPathFlag(/* enabled */ false); condition_.Broadcast(self); } +bool ReferenceProcessor::SlowPathEnabled() { + ObjPtr reference_class = GetClassRoot(); + MemberOffset slow_path_offset = GetSlowPathFlagOffset(reference_class); + return reference_class->GetFieldBoolean(slow_path_offset); +} + void ReferenceProcessor::BroadcastForSlowPath(Thread* self) { MutexLock mu(self, *Locks::reference_processor_lock_); condition_.Broadcast(self); diff --git a/runtime/gc/reference_processor.h b/runtime/gc/reference_processor.h index 1d984eb768afa462767336cdd44f425d7cae980b..c6c78367b0b58c1cf59cb8c5c147ce0422104b5d 100644 --- a/runtime/gc/reference_processor.h +++ b/runtime/gc/reference_processor.h @@ -17,8 +17,8 @@ #ifndef ART_RUNTIME_GC_REFERENCE_PROCESSOR_H_ #define ART_RUNTIME_GC_REFERENCE_PROCESSOR_H_ +#include "base/globals.h" #include "base/mutex.h" -#include "globals.h" #include "jni.h" #include "reference_queue.h" diff --git a/runtime/gc/reference_queue.h b/runtime/gc/reference_queue.h index af7788130ba069a734555c55100d1fc9c9463e9e..09ab51a0456beb90f8210d5c95bcbfe8fc9476b0 100644 --- a/runtime/gc/reference_queue.h +++ b/runtime/gc/reference_queue.h @@ -22,9 +22,9 @@ #include #include "base/atomic.h" +#include "base/globals.h" #include "base/mutex.h" #include "base/timing_logger.h" -#include "globals.h" #include "jni.h" #include "obj_ptr.h" #include "offsets.h" diff --git a/runtime/gc/space/bump_pointer_space-inl.h b/runtime/gc/space/bump_pointer_space-inl.h index 9ebb131ad109772b3ced077d349fde5d988cabb0..4c585497ec7e67c96403ef46ef02ab25b65590b4 100644 --- a/runtime/gc/space/bump_pointer_space-inl.h +++ b/runtime/gc/space/bump_pointer_space-inl.h @@ -46,16 +46,18 @@ inline mirror::Object* BumpPointerSpace::AllocThreadUnsafe(Thread* self, size_t size_t* bytes_tl_bulk_allocated) { Locks::mutator_lock_->AssertExclusiveHeld(self); num_bytes = RoundUp(num_bytes, kAlignment); - uint8_t* end = end_.LoadRelaxed(); + uint8_t* end = end_.load(std::memory_order_relaxed); if (end + num_bytes > growth_end_) { return nullptr; } mirror::Object* obj = reinterpret_cast(end); - end_.StoreRelaxed(end + num_bytes); + end_.store(end + num_bytes, std::memory_order_relaxed); *bytes_allocated = num_bytes; // Use the CAS free versions as an optimization. - objects_allocated_.StoreRelaxed(objects_allocated_.LoadRelaxed() + 1); - bytes_allocated_.StoreRelaxed(bytes_allocated_.LoadRelaxed() + num_bytes); + objects_allocated_.store(objects_allocated_.load(std::memory_order_relaxed) + 1, + std::memory_order_relaxed); + bytes_allocated_.store(bytes_allocated_.load(std::memory_order_relaxed) + num_bytes, + std::memory_order_relaxed); if (UNLIKELY(usable_size != nullptr)) { *usable_size = num_bytes; } @@ -68,7 +70,7 @@ inline mirror::Object* BumpPointerSpace::AllocNonvirtualWithoutAccounting(size_t uint8_t* old_end; uint8_t* new_end; do { - old_end = end_.LoadRelaxed(); + old_end = end_.load(std::memory_order_relaxed); new_end = old_end + num_bytes; // If there is no more room in the region, we are out of memory. if (UNLIKELY(new_end > growth_end_)) { @@ -81,8 +83,8 @@ inline mirror::Object* BumpPointerSpace::AllocNonvirtualWithoutAccounting(size_t inline mirror::Object* BumpPointerSpace::AllocNonvirtual(size_t num_bytes) { mirror::Object* ret = AllocNonvirtualWithoutAccounting(num_bytes); if (ret != nullptr) { - objects_allocated_.FetchAndAddSequentiallyConsistent(1); - bytes_allocated_.FetchAndAddSequentiallyConsistent(num_bytes); + objects_allocated_.fetch_add(1, std::memory_order_seq_cst); + bytes_allocated_.fetch_add(num_bytes, std::memory_order_seq_cst); } return ret; } diff --git a/runtime/gc/space/bump_pointer_space.cc b/runtime/gc/space/bump_pointer_space.cc index ce0e0f36308c89000d576bd154f6a8778bd485e4..e95da01d8c987434af25019411ad3d6e1b455dcc 100644 --- a/runtime/gc/space/bump_pointer_space.cc +++ b/runtime/gc/space/bump_pointer_space.cc @@ -72,8 +72,8 @@ void BumpPointerSpace::Clear() { // Reset the end of the space back to the beginning, we move the end forward as we allocate // objects. SetEnd(Begin()); - objects_allocated_.StoreRelaxed(0); - bytes_allocated_.StoreRelaxed(0); + objects_allocated_.store(0, std::memory_order_relaxed); + bytes_allocated_.store(0, std::memory_order_relaxed); growth_end_ = Limit(); { MutexLock mu(Thread::Current(), block_lock_); @@ -160,7 +160,7 @@ accounting::ContinuousSpaceBitmap::SweepCallback* BumpPointerSpace::GetSweepCall uint64_t BumpPointerSpace::GetBytesAllocated() { // Start out pre-determined amount (blocks which are not being allocated into). - uint64_t total = static_cast(bytes_allocated_.LoadRelaxed()); + uint64_t total = static_cast(bytes_allocated_.load(std::memory_order_relaxed)); Thread* self = Thread::Current(); MutexLock mu(self, *Locks::runtime_shutdown_lock_); MutexLock mu2(self, *Locks::thread_list_lock_); @@ -178,7 +178,7 @@ uint64_t BumpPointerSpace::GetBytesAllocated() { uint64_t BumpPointerSpace::GetObjectsAllocated() { // Start out pre-determined amount (blocks which are not being allocated into). - uint64_t total = static_cast(objects_allocated_.LoadRelaxed()); + uint64_t total = static_cast(objects_allocated_.load(std::memory_order_relaxed)); Thread* self = Thread::Current(); MutexLock mu(self, *Locks::runtime_shutdown_lock_); MutexLock mu2(self, *Locks::thread_list_lock_); @@ -195,8 +195,8 @@ uint64_t BumpPointerSpace::GetObjectsAllocated() { } void BumpPointerSpace::RevokeThreadLocalBuffersLocked(Thread* thread) { - objects_allocated_.FetchAndAddSequentiallyConsistent(thread->GetThreadLocalObjectsAllocated()); - bytes_allocated_.FetchAndAddSequentiallyConsistent(thread->GetThreadLocalBytesAllocated()); + objects_allocated_.fetch_add(thread->GetThreadLocalObjectsAllocated(), std::memory_order_seq_cst); + bytes_allocated_.fetch_add(thread->GetThreadLocalBytesAllocated(), std::memory_order_seq_cst); thread->SetTlab(nullptr, nullptr, nullptr); } diff --git a/runtime/gc/space/bump_pointer_space.h b/runtime/gc/space/bump_pointer_space.h index 7b43362c2d30b85c91a546e9a6a470b9e90245e4..5ba13ca3ff1653cb90be0d6c0fc3c7c61fe71f69 100644 --- a/runtime/gc/space/bump_pointer_space.h +++ b/runtime/gc/space/bump_pointer_space.h @@ -155,8 +155,8 @@ class BumpPointerSpace FINAL : public ContinuousMemMapAllocSpace { // Record objects / bytes freed. void RecordFree(int32_t objects, int32_t bytes) { - objects_allocated_.FetchAndSubSequentiallyConsistent(objects); - bytes_allocated_.FetchAndSubSequentiallyConsistent(bytes); + objects_allocated_.fetch_sub(objects, std::memory_order_seq_cst); + bytes_allocated_.fetch_sub(bytes, std::memory_order_seq_cst); } void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) OVERRIDE diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index fa684814215e97bc8eeb45b4484aca4663d4db8a..cbce94033798df1c5fdce7458e727b2c55dcba4f 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -672,7 +672,7 @@ class ImageSpaceLoader { // Loaded the map, use the image header from the file now in case we patch it with // RelocateInPlace. image_header = reinterpret_cast(map->Begin()); - const uint32_t bitmap_index = ImageSpace::bitmap_index_.FetchAndAddSequentiallyConsistent(1); + const uint32_t bitmap_index = ImageSpace::bitmap_index_.fetch_add(1, std::memory_order_seq_cst); std::string bitmap_name(StringPrintf("imagespace %s live-bitmap %u", image_filename, bitmap_index)); @@ -1287,7 +1287,7 @@ class ImageSpaceLoader { bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_object_visitor); // Fixup image roots. CHECK(app_image.InSource(reinterpret_cast( - image_header.GetImageRoots()))); + image_header.GetImageRoots().Ptr()))); image_header.RelocateImageObjects(app_image.Delta()); CHECK_EQ(image_header.GetImageBegin(), target_base); // Fix up dex cache DexFile pointers. @@ -1875,7 +1875,7 @@ std::string ImageSpace::GetMultiImageBootClassPath( bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg) { const ArtDexFileLoader dex_file_loader; - for (const OatFile::OatDexFile* oat_dex_file : oat_file.GetOatDexFiles()) { + for (const OatDexFile* oat_dex_file : oat_file.GetOatDexFiles()) { const std::string& dex_file_location = oat_dex_file->GetDexFileLocation(); // Skip multidex locations - These will be checked when we visit their @@ -1909,9 +1909,9 @@ bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg std::string multi_dex_location = DexFileLoader::GetMultiDexLocation( i, dex_file_location.c_str()); - const OatFile::OatDexFile* multi_dex = oat_file.GetOatDexFile(multi_dex_location.c_str(), - nullptr, - error_msg); + const OatDexFile* multi_dex = oat_file.GetOatDexFile(multi_dex_location.c_str(), + nullptr, + error_msg); if (multi_dex == nullptr) { *error_msg = StringPrintf("ValidateOatFile oat file '%s' is missing entry '%s'", oat_file.GetLocation().c_str(), diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc index 512cde484d229e1ae4fb316879cca3ea39fcef7b..a24ca323145e0e8827145598bf5708588d9f7d75 100644 --- a/runtime/gc/space/large_object_space.cc +++ b/runtime/gc/space/large_object_space.cc @@ -45,8 +45,9 @@ class MemoryToolLargeObjectMapSpace FINAL : public LargeObjectMapSpace { } ~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 8282f3dda7fe9280dcf067edb20d7248463e412c..c0221710824f8bf5e61a3f1a2311319b3204d6f9 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 space { namespace memory_tool_details { template -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 @@ template mirror::Object* MemoryToolMallocSpace::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( - obj_with_rdz, num_bytes, - bytes_allocated, usable_size, + return memory_tool_details::AdjustForMemoryTool( + obj_with_rdz, + num_bytes, + bytes_allocated, + usable_size, bytes_tl_bulk_allocated, bytes_allocated_out, usable_size_out, @@ -113,27 +123,35 @@ template mirror::Object* MemoryToolMallocSpace::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(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( + 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 mirror::Object* MemoryToolMallocSpace::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( - obj_with_rdz, num_bytes, - bytes_allocated, usable_size, + return memory_tool_details::AdjustForMemoryTool( + obj_with_rdz, + num_bytes, + bytes_allocated, + usable_size, bytes_tl_bulk_allocated, bytes_allocated_out, usable_size_out, @@ -170,12 +195,14 @@ template size_t MemoryToolMallocSpace::AllocationSize( + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::AllocationSize( mirror::Object* obj, size_t* usable_size) { - size_t result = S::AllocationSize(reinterpret_cast( - reinterpret_cast(obj) - (kAdjustForRedzoneInAllocSize ? kMemoryToolRedZoneBytes : 0)), + size_t result = S::AllocationSize( + reinterpret_cast( + reinterpret_cast(obj) + - (kAdjustForRedzoneInAllocSize ? kMemoryToolRedZoneBytes : 0)), usable_size); if (usable_size != nullptr) { if (kUseObjSizeForUsable) { @@ -192,10 +219,9 @@ template size_t MemoryToolMallocSpace::Free( - Thread* self, mirror::Object* ptr) { + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::Free(Thread* self, mirror::Object* ptr) { void* obj_after_rdz = reinterpret_cast(ptr); uint8_t* obj_with_rdz = reinterpret_cast(obj_after_rdz) - kMemoryToolRedZoneBytes; @@ -220,10 +246,10 @@ template size_t MemoryToolMallocSpace::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 @@ template template MemoryToolMallocSpace::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 @@ template size_t MemoryToolMallocSpace::MaxBytesBulkAllocatedFor(size_t num_bytes) { + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::MaxBytesBulkAllocatedFor(size_t num_bytes) { return S::MaxBytesBulkAllocatedFor(num_bytes + 2 * kMemoryToolRedZoneBytes); } diff --git a/runtime/gc/space/region_space-inl.h b/runtime/gc/space/region_space-inl.h index 410931cbe53c86c56821eb987739a3444d2b2449..c6ec174a13855319294bef4e429ef03b6643322b 100644 --- a/runtime/gc/space/region_space-inl.h +++ b/runtime/gc/space/region_space-inl.h @@ -100,13 +100,13 @@ inline mirror::Object* RegionSpace::Region::Alloc(size_t num_bytes, uint8_t* old_top; uint8_t* new_top; do { - old_top = top_.LoadRelaxed(); + old_top = top_.load(std::memory_order_relaxed); new_top = old_top + num_bytes; if (UNLIKELY(new_top > end_)) { return nullptr; } } while (!top_.CompareAndSetWeakRelaxed(old_top, new_top)); - objects_allocated_.FetchAndAddRelaxed(1); + objects_allocated_.fetch_add(1, std::memory_order_relaxed); DCHECK_LE(Top(), end_); DCHECK_LT(old_top, end_); DCHECK_LE(new_top, end_); @@ -258,16 +258,79 @@ inline mirror::Object* RegionSpace::AllocLarge(size_t num_bytes, return nullptr; } } + // Find a large enough set of contiguous free regions. - size_t left = 0; - while (left + num_regs - 1 < num_regions_) { + if (kCyclicRegionAllocation) { + // Try to find a range of free regions within [cyclic_alloc_region_index_, num_regions_). + size_t next_region1 = -1; + mirror::Object* region1 = AllocLargeInRange(cyclic_alloc_region_index_, + num_regions_, + num_regs, + bytes_allocated, + usable_size, + bytes_tl_bulk_allocated, + &next_region1); + if (region1 != nullptr) { + DCHECK_LT(0u, next_region1); + DCHECK_LE(next_region1, num_regions_); + // Move the cyclic allocation region marker to the region + // following the large region that was just allocated. + cyclic_alloc_region_index_ = next_region1 % num_regions_; + return region1; + } + + // If the previous attempt failed, try to find a range of free regions within + // [0, cyclic_alloc_region_index_ + num_regions_ - 1). + size_t next_region2 = -1; + mirror::Object* region2 = + AllocLargeInRange(0, + cyclic_alloc_region_index_ + num_regions_ - 1, + num_regs, + bytes_allocated, + usable_size, + bytes_tl_bulk_allocated, + &next_region2); + if (region2 != nullptr) { + DCHECK_LT(0u, next_region2); + DCHECK_LE(next_region2, num_regions_); + // Move the cyclic allocation region marker to the region + // following the large region that was just allocated. + cyclic_alloc_region_index_ = next_region2 % num_regions_; + return region2; + } + } else { + // Try to find a range of free regions within [0, num_regions_). + mirror::Object* region = AllocLargeInRange(0, + num_regions_, + num_regs, + bytes_allocated, + usable_size, + bytes_tl_bulk_allocated); + if (region != nullptr) { + return region; + } + } + return nullptr; +} + +template +inline mirror::Object* RegionSpace::AllocLargeInRange(size_t begin, + size_t end, + size_t num_regs, + /* out */ size_t* bytes_allocated, + /* out */ size_t* usable_size, + /* out */ size_t* bytes_tl_bulk_allocated, + /* out */ size_t* next_region) { + size_t left = begin; + while (left + num_regs - 1 < end) { bool found = true; size_t right = left; - DCHECK_LT(right, left + num_regs) - << "The inner loop Should iterate at least once"; + DCHECK_LT(right, left + num_regs) << "The inner loop should iterate at least once"; while (right < left + num_regs) { if (regions_[right].IsFree()) { ++right; + // Ensure `right` is not going beyond the past-the-end index of the region space. + DCHECK_LE(right, num_regions_); } else { found = false; break; @@ -303,9 +366,15 @@ inline mirror::Object* RegionSpace::AllocLarge(size_t num_bytes, *usable_size = allocated; } *bytes_tl_bulk_allocated = allocated; - return reinterpret_cast(first_reg->Begin()); + mirror::Object* large_region = reinterpret_cast(first_reg->Begin()); + DCHECK(large_region != nullptr); + if (next_region != nullptr) { + // Return the index to the region next to the allocated large region via `next_region`. + *next_region = right; + } + return large_region; } else { - // right points to the non-free region. Start with the one after it. + // `right` points to the non-free region. Start with the one after it. left = right + 1; } } @@ -365,11 +434,11 @@ inline size_t RegionSpace::Region::BytesAllocated() const { inline size_t RegionSpace::Region::ObjectsAllocated() const { if (IsLarge()) { DCHECK_LT(begin_ + kRegionSize, Top()); - DCHECK_EQ(objects_allocated_.LoadRelaxed(), 0U); + DCHECK_EQ(objects_allocated_.load(std::memory_order_relaxed), 0U); return 1; } else if (IsLargeTail()) { DCHECK_EQ(begin_, Top()); - DCHECK_EQ(objects_allocated_.LoadRelaxed(), 0U); + DCHECK_EQ(objects_allocated_.load(std::memory_order_relaxed), 0U); return 0; } else { DCHECK(IsAllocated()) << "state=" << state_; diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc index 8d94c8670198c7d34a915dbe04ffb760f4d9a669..0701330e81b46636569547fc72d76fa8d8914910 100644 --- a/runtime/gc/space/region_space.cc +++ b/runtime/gc/space/region_space.cc @@ -29,10 +29,19 @@ namespace space { // value of the region size, evaculate the region. static constexpr uint kEvacuateLivePercentThreshold = 75U; -// If we protect the cleared regions. +// Whether we protect the cleared regions. // Only protect for target builds to prevent flaky test failures (b/63131961). static constexpr bool kProtectClearedRegions = kIsTargetBuild; +// Wether we poison memory areas occupied by dead objects in unevacuated regions. +static constexpr bool kPoisonDeadObjectsInUnevacuatedRegions = true; + +// Special 32-bit value used to poison memory areas occupied by dead +// objects in unevacuated regions. Dereferencing this value is expected +// to trigger a memory protection fault, as it is unlikely that it +// points to a valid, non-protected memory area. +static constexpr uint32_t kPoisonDeadObject = 0xBADDB01D; // "BADDROID" + MemMap* RegionSpace::CreateMemMap(const std::string& name, size_t capacity, uint8_t* requested_begin) { CHECK_ALIGNED(capacity, kRegionSize); @@ -92,7 +101,8 @@ RegionSpace::RegionSpace(const std::string& name, MemMap* mem_map) max_peak_num_non_free_regions_(0U), non_free_region_index_limit_(0U), current_region_(&full_region_), - evac_region_(nullptr) { + evac_region_(nullptr), + cyclic_alloc_region_index_(0U) { CHECK_ALIGNED(mem_map->Size(), kRegionSize); CHECK_ALIGNED(mem_map->Begin(), kRegionSize); DCHECK_GT(num_regions_, 0U); @@ -369,6 +379,13 @@ void RegionSpace::ClearFromSpace(/* out */ uint64_t* cleared_bytes, // as they are unevac regions that are live. // Subtract one for the for-loop. i += regions_to_clear_bitmap - 1; + } else { + // Only some allocated bytes are live in this unevac region. + // This should only happen for an allocated non-large region. + DCHECK(r->IsAllocated()) << r->State(); + if (kPoisonDeadObjectsInUnevacuatedRegions) { + PoisonDeadObjectsInUnevacuatedRegion(r); + } } } // Note r != last_checked_region if r->IsInUnevacFromSpace() was true above. @@ -387,6 +404,55 @@ void RegionSpace::ClearFromSpace(/* out */ uint64_t* cleared_bytes, num_evac_regions_ = 0; } +// Poison the memory area in range [`begin`, `end`) with value `kPoisonDeadObject`. +static void PoisonUnevacuatedRange(uint8_t* begin, uint8_t* end) { + static constexpr size_t kPoisonDeadObjectSize = sizeof(kPoisonDeadObject); + static_assert(IsPowerOfTwo(kPoisonDeadObjectSize) && + IsPowerOfTwo(RegionSpace::kAlignment) && + (kPoisonDeadObjectSize < RegionSpace::kAlignment), + "RegionSpace::kAlignment should be a multiple of kPoisonDeadObjectSize" + " and both should be powers of 2"); + DCHECK_ALIGNED(begin, kPoisonDeadObjectSize); + DCHECK_ALIGNED(end, kPoisonDeadObjectSize); + uint32_t* begin_addr = reinterpret_cast(begin); + uint32_t* end_addr = reinterpret_cast(end); + std::fill(begin_addr, end_addr, kPoisonDeadObject); +} + +void RegionSpace::PoisonDeadObjectsInUnevacuatedRegion(Region* r) { + // The live byte count of `r` should be different from -1, as this + // region should neither be a newly allocated region nor an + // evacuated region. + DCHECK_NE(r->LiveBytes(), static_cast(-1)); + + // Past-the-end address of the previously visited (live) object (or + // the beginning of the region, if `maybe_poison` has not run yet). + uint8_t* prev_obj_end = reinterpret_cast(r->Begin()); + + // Functor poisoning the space between `obj` and the previously + // visited (live) object (or the beginng of the region), if any. + auto maybe_poison = [this, &prev_obj_end](mirror::Object* obj) REQUIRES(Locks::mutator_lock_) { + DCHECK_ALIGNED(obj, kAlignment); + uint8_t* cur_obj_begin = reinterpret_cast(obj); + if (cur_obj_begin != prev_obj_end) { + // There is a gap (dead object(s)) between the previously + // visited (live) object (or the beginning of the region) and + // `obj`; poison that space. + PoisonUnevacuatedRange(prev_obj_end, cur_obj_begin); + } + prev_obj_end = reinterpret_cast(GetNextObject(obj)); + }; + + // Visit live objects in `r` and poison gaps (dead objects) between them. + GetLiveBitmap()->VisitMarkedRange(reinterpret_cast(r->Begin()), + reinterpret_cast(r->Top()), + maybe_poison); + // Poison memory between the last live object and the end of the region, if any. + if (prev_obj_end < r->Top()) { + PoisonUnevacuatedRange(prev_obj_end, r->Top()); + } +} + void RegionSpace::LogFragmentationAllocFailure(std::ostream& os, size_t /* failed_alloc_bytes */) { size_t max_contiguous_allocation = 0; @@ -437,6 +503,7 @@ void RegionSpace::Clear() { r->Clear(/*zero_and_release_pages*/true); } SetNonFreeRegionLimit(0); + DCHECK_EQ(num_non_free_regions_, 0u); current_region_ = &full_region_; evac_region_ = &full_region_; } @@ -450,6 +517,9 @@ void RegionSpace::ClampGrowthLimit(size_t new_capacity) { return; } num_regions_ = new_num_regions; + if (kCyclicRegionAllocation && cyclic_alloc_region_index_ >= num_regions_) { + cyclic_alloc_region_index_ = 0u; + } SetLimit(Begin() + new_capacity); if (Size() > new_capacity) { SetEnd(Limit()); @@ -489,7 +559,7 @@ void RegionSpace::DumpNonFreeRegions(std::ostream& os) { void RegionSpace::RecordAlloc(mirror::Object* ref) { CHECK(ref != nullptr); Region* r = RefToRegion(ref); - r->objects_allocated_.FetchAndAddSequentiallyConsistent(1); + r->objects_allocated_.fetch_add(1, std::memory_order_seq_cst); } bool RegionSpace::AllocNewTlab(Thread* self, size_t min_bytes) { @@ -589,10 +659,10 @@ size_t RegionSpace::AllocationSizeNonvirtual(mirror::Object* obj, size_t* usable } void RegionSpace::Region::Clear(bool zero_and_release_pages) { - top_.StoreRelaxed(begin_); + top_.store(begin_, std::memory_order_relaxed); state_ = RegionState::kRegionStateFree; type_ = RegionType::kRegionTypeNone; - objects_allocated_.StoreRelaxed(0); + objects_allocated_.store(0, std::memory_order_relaxed); alloc_time_ = 0; live_bytes_ = static_cast(-1); if (zero_and_release_pages) { @@ -608,7 +678,14 @@ RegionSpace::Region* RegionSpace::AllocateRegion(bool for_evac) { return nullptr; } for (size_t i = 0; i < num_regions_; ++i) { - Region* r = ®ions_[i]; + // When using the cyclic region allocation strategy, try to + // allocate a region starting from the last cyclic allocated + // region marker. Otherwise, try to allocate a region starting + // from the beginning of the region space. + size_t region_index = kCyclicRegionAllocation + ? ((cyclic_alloc_region_index_ + i) % num_regions_) + : i; + Region* r = ®ions_[region_index]; if (r->IsFree()) { r->Unfree(this, time_); if (for_evac) { @@ -618,6 +695,11 @@ RegionSpace::Region* RegionSpace::AllocateRegion(bool for_evac) { r->SetNewlyAllocated(); ++num_non_free_regions_; } + if (kCyclicRegionAllocation) { + // Move the cyclic allocation region marker to the region + // following the one that was just allocated. + cyclic_alloc_region_index_ = (region_index + 1) % num_regions_; + } return r; } } diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h index d63257d928faf4fceef416ca628f31dbc8e28d19..a12917140b06b7734f23ab47206a6dd5540992af 100644 --- a/runtime/gc/space/region_space.h +++ b/runtime/gc/space/region_space.h @@ -31,6 +31,13 @@ class ReadBarrierTable; namespace space { +// Cyclic region allocation strategy. If `true`, region allocation +// will not try to allocate a new region from the beginning of the +// region space, but from the last allocated region. This allocation +// strategy reduces region reuse and should help catch some GC bugs +// earlier. +static constexpr bool kCyclicRegionAllocation = true; + // A space that consists of equal-sized regions. class RegionSpace FINAL : public ContinuousMemMapAllocSpace { public: @@ -292,19 +299,26 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { public: Region() : idx_(static_cast(-1)), - begin_(nullptr), top_(nullptr), end_(nullptr), - state_(RegionState::kRegionStateAllocated), type_(RegionType::kRegionTypeToSpace), - objects_allocated_(0), alloc_time_(0), live_bytes_(static_cast(-1)), - is_newly_allocated_(false), is_a_tlab_(false), thread_(nullptr) {} + live_bytes_(static_cast(-1)), + begin_(nullptr), + thread_(nullptr), + top_(nullptr), + end_(nullptr), + objects_allocated_(0), + alloc_time_(0), + is_newly_allocated_(false), + is_a_tlab_(false), + state_(RegionState::kRegionStateAllocated), + type_(RegionType::kRegionTypeToSpace) {} void Init(size_t idx, uint8_t* begin, uint8_t* end) { idx_ = idx; begin_ = begin; - top_.StoreRelaxed(begin); + top_.store(begin, std::memory_order_relaxed); end_ = end; state_ = RegionState::kRegionStateFree; type_ = RegionType::kRegionTypeNone; - objects_allocated_.StoreRelaxed(0); + objects_allocated_.store(0, std::memory_order_relaxed); alloc_time_ = 0; live_bytes_ = static_cast(-1); is_newly_allocated_ = false; @@ -334,7 +348,7 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { if (is_free) { DCHECK(IsInNoSpace()); DCHECK_EQ(begin_, Top()); - DCHECK_EQ(objects_allocated_.LoadRelaxed(), 0U); + DCHECK_EQ(objects_allocated_.load(std::memory_order_relaxed), 0U); } return is_free; } @@ -461,11 +475,11 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { } ALWAYS_INLINE uint8_t* Top() const { - return top_.LoadRelaxed(); + return top_.load(std::memory_order_relaxed); } void SetTop(uint8_t* new_top) { - top_.StoreRelaxed(new_top); + top_.store(new_top, std::memory_order_relaxed); } uint8_t* End() const { @@ -480,31 +494,31 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { void RecordThreadLocalAllocations(size_t num_objects, size_t num_bytes) { DCHECK(IsAllocated()); - DCHECK_EQ(objects_allocated_.LoadRelaxed(), 0U); + DCHECK_EQ(objects_allocated_.load(std::memory_order_relaxed), 0U); DCHECK_EQ(Top(), end_); - objects_allocated_.StoreRelaxed(num_objects); - top_.StoreRelaxed(begin_ + num_bytes); + objects_allocated_.store(num_objects, std::memory_order_relaxed); + top_.store(begin_ + num_bytes, std::memory_order_relaxed); DCHECK_LE(Top(), end_); } private: size_t idx_; // The region's index in the region space. + size_t live_bytes_; // The live bytes. Used to compute the live percent. uint8_t* begin_; // The begin address of the region. + Thread* thread_; // The owning thread if it's a tlab. // Note that `top_` can be higher than `end_` in the case of a // large region, where an allocated object spans multiple regions // (large region + one or more large tail regions). Atomic top_; // The current position of the allocation. uint8_t* end_; // The end address of the region. - RegionState state_; // The region state (see RegionState). - RegionType type_; // The region type (see RegionType). Atomic objects_allocated_; // The number of objects allocated. uint32_t alloc_time_; // The allocation time of the region. // Note that newly allocated and evacuated regions use -1 as // special value for `live_bytes_`. - size_t live_bytes_; // The live bytes. Used to compute the live percent. bool is_newly_allocated_; // True if it's allocated after the last collection. bool is_a_tlab_; // True if it's a tlab. - Thread* thread_; // The owning thread if it's a tlab. + RegionState state_; // The region state (see RegionState). + RegionType type_; // The region type (see RegionType). friend class RegionSpace; }; @@ -571,6 +585,28 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { Region* AllocateRegion(bool for_evac) REQUIRES(region_lock_); + // Scan region range [`begin`, `end`) in increasing order to try to + // allocate a large region having a size of `num_regs` regions. If + // there is no space in the region space to allocate this large + // region, return null. + // + // If argument `next_region` is not null, use `*next_region` to + // return the index to the region next to the allocated large region + // returned by this method. + template + mirror::Object* AllocLargeInRange(size_t num_regs, + size_t begin, + size_t end, + /* out */ size_t* bytes_allocated, + /* out */ size_t* usable_size, + /* out */ size_t* bytes_tl_bulk_allocated, + /* out */ size_t* next_region = nullptr) REQUIRES(region_lock_); + + // Poison memory areas used by dead objects within unevacuated + // region `r`. This is meant to detect dangling references to dead + // objects earlier in debug mode. + void PoisonDeadObjectsInUnevacuatedRegion(Region* r); + Mutex region_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; uint32_t time_; // The time as the number of collections since the startup. @@ -600,6 +636,11 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { Region* evac_region_; // The region currently used for evacuation. Region full_region_; // The dummy/sentinel region that looks full. + // Index into the region array pointing to the starting region when + // trying to allocate a new region. Only used when + // `kCyclicRegionAllocation` is true. + size_t cyclic_alloc_region_index_ GUARDED_BY(region_lock_); + // Mark bitmap used by the GC. std::unique_ptr mark_bitmap_; diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc index e7865363a104514fe7ee7b33fcc6c8352f425940..b0402e4b8386bd6ad970c02cda6a1f348e02e357 100644 --- a/runtime/gc/space/rosalloc_space.cc +++ b/runtime/gc/space/rosalloc_space.cc @@ -77,7 +77,7 @@ RosAllocSpace* RosAllocSpace::CreateFromMemMap(MemMap* mem_map, const std::strin // 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( @@ -382,12 +382,12 @@ size_t RosAllocSpace::AllocationSizeNonvirtual(mirror::Object* obj, size_t* usab size_t size = obj->SizeOf(); 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 9d16b87b7d4421ac4315c9bdb9ce505789464682..4c17233360b33268a8ec49e97011c02ca6cf5a7b 100644 --- a/runtime/gc/space/rosalloc_space.h +++ b/runtime/gc/space/rosalloc_space.h @@ -159,8 +159,8 @@ class RosAllocSpace : public MallocSpace { void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size, size_t maximum_size, bool low_memory_mode) OVERRIDE { - return CreateRosAlloc(base, morecore_start, initial_size, maximum_size, low_memory_mode, - RUNNING_ON_MEMORY_TOOL != 0); + return CreateRosAlloc( + base, morecore_start, initial_size, maximum_size, low_memory_mode, kRunningOnMemoryTool); } static allocator::RosAlloc* CreateRosAlloc(void* base, size_t morecore_start, size_t initial_size, size_t maximum_size, bool low_memory_mode, diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h index 7af19fae61a04dbb7a9ddf37ebc72ec3453b98a6..4f43d9f5c5edb7a060e6dae96734b53f4e11e006 100644 --- a/runtime/gc/space/space.h +++ b/runtime/gc/space/space.h @@ -21,12 +21,12 @@ #include #include "base/atomic.h" +#include "base/globals.h" #include "base/macros.h" +#include "base/mem_map.h" #include "base/mutex.h" #include "gc/accounting/space_bitmap.h" #include "gc/collector/object_byte_pair.h" -#include "globals.h" -#include "mem_map.h" namespace art { namespace mirror { @@ -272,7 +272,7 @@ class ContinuousSpace : public Space { // Current address at which the space ends, which may vary as the space is filled. uint8_t* End() const { - return end_.LoadRelaxed(); + return end_.load(std::memory_order_relaxed); } // The end of the address range covered by the space. @@ -283,7 +283,7 @@ class ContinuousSpace : public Space { // Change the end of the space. Be careful with use since changing the end of a space to an // invalid value may break the GC. void SetEnd(uint8_t* end) { - end_.StoreRelaxed(end); + end_.store(end, std::memory_order_relaxed); } void SetLimit(uint8_t* limit) { diff --git a/runtime/gc/space/space_test.h b/runtime/gc/space/space_test.h index 1fe3fb2e864f247c9d6a7b099cbc983979e13400..c94b6666950c72f60cd205c8d9439e065186550d 100644 --- a/runtime/gc/space/space_test.h +++ b/runtime/gc/space/space_test.h @@ -20,8 +20,8 @@ #include #include +#include "base/globals.h" #include "common_runtime_test.h" -#include "globals.h" #include "mirror/array-inl.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" @@ -53,13 +53,11 @@ class SpaceTest : public Super { } mirror::Class* GetByteArrayClass(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - StackHandleScope<1> hs(self); - auto null_loader(hs.NewHandle(nullptr)); if (byte_array_class_ == nullptr) { - mirror::Class* byte_array_class = - Runtime::Current()->GetClassLinker()->FindClass(self, "[B", null_loader); + ObjPtr byte_array_class = + Runtime::Current()->GetClassLinker()->FindSystemClass(self, "[B"); EXPECT_TRUE(byte_array_class != nullptr); - byte_array_class_ = self->GetJniEnv()->NewLocalRef(byte_array_class); + byte_array_class_ = self->GetJniEnv()->NewLocalRef(byte_array_class.Ptr()); EXPECT_TRUE(byte_array_class_ != nullptr); } return self->DecodeJObject(byte_array_class_)->AsClass(); diff --git a/runtime/gc/space/zygote_space.cc b/runtime/gc/space/zygote_space.cc index cde155fb221cf321abda63420287918be64f34b4..8c73ef9116114c4e511b27ea9e6617f0d2ab6acc 100644 --- a/runtime/gc/space/zygote_space.cc +++ b/runtime/gc/space/zygote_space.cc @@ -122,7 +122,7 @@ void ZygoteSpace::SweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* ar // Need to mark the card since this will update the mod-union table next GC cycle. card_table->MarkCard(ptrs[i]); } - zygote_space->objects_allocated_.FetchAndSubSequentiallyConsistent(num_ptrs); + zygote_space->objects_allocated_.fetch_sub(num_ptrs, std::memory_order_seq_cst); } } // namespace space diff --git a/runtime/gc/space/zygote_space.h b/runtime/gc/space/zygote_space.h index 08231017e7f44932cf53bf8af6a5b8100bd26a8d..6fe21d99a83f8cec7e0862e556c6fd6ae2921170 100644 --- a/runtime/gc/space/zygote_space.h +++ b/runtime/gc/space/zygote_space.h @@ -17,9 +17,9 @@ #ifndef ART_RUNTIME_GC_SPACE_ZYGOTE_SPACE_H_ #define ART_RUNTIME_GC_SPACE_ZYGOTE_SPACE_H_ +#include "base/mem_map.h" #include "gc/accounting/space_bitmap.h" #include "malloc_space.h" -#include "mem_map.h" namespace art { namespace gc { @@ -67,7 +67,7 @@ class ZygoteSpace FINAL : public ContinuousMemMapAllocSpace { } uint64_t GetObjectsAllocated() { - return objects_allocated_.LoadSequentiallyConsistent(); + return objects_allocated_.load(std::memory_order_seq_cst); } void Clear() OVERRIDE; diff --git a/runtime/gc/task_processor.h b/runtime/gc/task_processor.h index f6b560703724ce7d1883522f2facd85cbc0bd4a9..6db3c37689944892d68ff21ee4019dbd67cbb6f0 100644 --- a/runtime/gc/task_processor.h +++ b/runtime/gc/task_processor.h @@ -20,8 +20,8 @@ #include #include +#include "base/globals.h" #include "base/mutex.h" -#include "globals.h" #include "thread_pool.h" namespace art { diff --git a/runtime/gc/task_processor_test.cc b/runtime/gc/task_processor_test.cc index 77b40e459394a2c3167d0b26e8c3e81fcb0e3031..38581ce8075e3a659c8e1dd4c69c7403c645bcf3 100644 --- a/runtime/gc/task_processor_test.cc +++ b/runtime/gc/task_processor_test.cc @@ -37,7 +37,7 @@ class RecursiveTask : public HeapTask { if (max_recursion_ > 0) { task_processor_->AddTask(self, new RecursiveTask(task_processor_, counter_, max_recursion_ - 1)); - counter_->FetchAndAddSequentiallyConsistent(1U); + counter_->fetch_add(1U, std::memory_order_seq_cst); } } @@ -54,7 +54,7 @@ class WorkUntilDoneTask : public SelfDeletingTask { } virtual void Run(Thread* self) OVERRIDE { task_processor_->RunAllTasks(self); - done_running_->StoreSequentiallyConsistent(true); + done_running_->store(true, std::memory_order_seq_cst); } private: @@ -76,7 +76,7 @@ TEST_F(TaskProcessorTest, Interrupt) { thread_pool.StartWorkers(self); ASSERT_FALSE(done_running); // Wait until all the tasks are done, but since we didn't interrupt, done_running should be 0. - while (counter.LoadSequentiallyConsistent() != kRecursion) { + while (counter.load(std::memory_order_seq_cst) != kRecursion) { usleep(10); } ASSERT_FALSE(done_running); @@ -84,11 +84,11 @@ TEST_F(TaskProcessorTest, Interrupt) { thread_pool.Wait(self, true, false); // After the interrupt and wait, the WorkUntilInterruptedTasktask should have terminated and // set done_running_ to true. - ASSERT_TRUE(done_running.LoadSequentiallyConsistent()); + ASSERT_TRUE(done_running.load(std::memory_order_seq_cst)); // Test that we finish remaining tasks before returning from RunTasksUntilInterrupted. - counter.StoreSequentiallyConsistent(0); - done_running.StoreSequentiallyConsistent(false); + counter.store(0, std::memory_order_seq_cst); + done_running.store(false, std::memory_order_seq_cst); // Self interrupt before any of the other tasks run, but since we added them we should keep on // working until all the tasks are completed. task_processor.Stop(self); @@ -96,8 +96,8 @@ TEST_F(TaskProcessorTest, Interrupt) { thread_pool.AddTask(self, new WorkUntilDoneTask(&task_processor, &done_running)); thread_pool.StartWorkers(self); thread_pool.Wait(self, true, false); - ASSERT_TRUE(done_running.LoadSequentiallyConsistent()); - ASSERT_EQ(counter.LoadSequentiallyConsistent(), kRecursion); + ASSERT_TRUE(done_running.load(std::memory_order_seq_cst)); + ASSERT_EQ(counter.load(std::memory_order_seq_cst), kRecursion); } class TestOrderTask : public HeapTask { @@ -137,10 +137,10 @@ TEST_F(TaskProcessorTest, Ordering) { Atomic done_running(false); // Add a task which will wait until interrupted to the thread pool. thread_pool.AddTask(self, new WorkUntilDoneTask(&task_processor, &done_running)); - ASSERT_FALSE(done_running.LoadSequentiallyConsistent()); + ASSERT_FALSE(done_running.load(std::memory_order_seq_cst)); thread_pool.StartWorkers(self); thread_pool.Wait(self, true, false); - ASSERT_TRUE(done_running.LoadSequentiallyConsistent()); + ASSERT_TRUE(done_running.load(std::memory_order_seq_cst)); ASSERT_EQ(counter, kNumTasks); } diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h index 46630dbeefd99b253ba6a9fad08d568966bc22b9..464c2b749ff3f75ca47697fd45013ad2b9dac8da 100644 --- a/runtime/generated/asm_support_gen.h +++ b/runtime/generated/asm_support_gen.h @@ -90,16 +90,24 @@ DEFINE_CHECK_EQ(static_cast(CARD_TABLE_CARD_SHIFT), (static_cast DEFINE_CHECK_EQ(static_cast(MIN_LARGE_OBJECT_THRESHOLD), (static_cast(art::gc::Heap::kMinLargeObjectThreshold))) #define LOCK_WORD_STATE_SHIFT 30 DEFINE_CHECK_EQ(static_cast(LOCK_WORD_STATE_SHIFT), (static_cast(art::LockWord::kStateShift))) -#define LOCK_WORD_STATE_MASK 0xc0000000 -DEFINE_CHECK_EQ(static_cast(LOCK_WORD_STATE_MASK), (static_cast(art::LockWord::kStateMaskShifted))) +#define LOCK_WORD_STATE_MASK_SHIFTED 0xc0000000 +DEFINE_CHECK_EQ(static_cast(LOCK_WORD_STATE_MASK_SHIFTED), (static_cast(art::LockWord::kStateMaskShifted))) #define LOCK_WORD_READ_BARRIER_STATE_SHIFT 28 DEFINE_CHECK_EQ(static_cast(LOCK_WORD_READ_BARRIER_STATE_SHIFT), (static_cast(art::LockWord::kReadBarrierStateShift))) #define LOCK_WORD_READ_BARRIER_STATE_MASK 0x10000000 DEFINE_CHECK_EQ(static_cast(LOCK_WORD_READ_BARRIER_STATE_MASK), (static_cast(art::LockWord::kReadBarrierStateMaskShifted))) #define LOCK_WORD_READ_BARRIER_STATE_MASK_TOGGLED 0xefffffff DEFINE_CHECK_EQ(static_cast(LOCK_WORD_READ_BARRIER_STATE_MASK_TOGGLED), (static_cast(art::LockWord::kReadBarrierStateMaskShiftedToggled))) -#define LOCK_WORD_THIN_LOCK_COUNT_ONE 65536 -DEFINE_CHECK_EQ(static_cast(LOCK_WORD_THIN_LOCK_COUNT_ONE), (static_cast(art::LockWord::kThinLockCountOne))) +#define LOCK_WORD_THIN_LOCK_COUNT_SIZE 12 +DEFINE_CHECK_EQ(static_cast(LOCK_WORD_THIN_LOCK_COUNT_SIZE), (static_cast(art::LockWord::kThinLockCountSize))) +#define LOCK_WORD_THIN_LOCK_COUNT_SHIFT 16 +DEFINE_CHECK_EQ(static_cast(LOCK_WORD_THIN_LOCK_COUNT_SHIFT), (static_cast(art::LockWord::kThinLockCountShift))) +#define LOCK_WORD_THIN_LOCK_COUNT_MASK_SHIFTED 0xfff0000 +DEFINE_CHECK_EQ(static_cast(LOCK_WORD_THIN_LOCK_COUNT_MASK_SHIFTED), (static_cast(art::LockWord::kThinLockCountMaskShifted))) +#define LOCK_WORD_THIN_LOCK_COUNT_ONE 0x10000 +DEFINE_CHECK_EQ(static_cast(LOCK_WORD_THIN_LOCK_COUNT_ONE), (static_cast(art::LockWord::kThinLockCountOne))) +#define LOCK_WORD_THIN_LOCK_OWNER_MASK_SHIFTED 0xffff +DEFINE_CHECK_EQ(static_cast(LOCK_WORD_THIN_LOCK_OWNER_MASK_SHIFTED), (static_cast(art::LockWord::kThinLockOwnerMaskShifted))) #define LOCK_WORD_STATE_FORWARDING_ADDRESS 0x3 DEFINE_CHECK_EQ(static_cast(LOCK_WORD_STATE_FORWARDING_ADDRESS), (static_cast(art::LockWord::kStateForwardingAddress))) #define LOCK_WORD_STATE_FORWARDING_ADDRESS_OVERFLOW 0x40000000 @@ -110,6 +118,8 @@ DEFINE_CHECK_EQ(static_cast(LOCK_WORD_STATE_FORWARDING_ADDRESS_SHIFT), DEFINE_CHECK_EQ(static_cast(LOCK_WORD_GC_STATE_MASK_SHIFTED), (static_cast(art::LockWord::kGCStateMaskShifted))) #define LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED 0xcfffffff DEFINE_CHECK_EQ(static_cast(LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED), (static_cast(art::LockWord::kGCStateMaskShiftedToggled))) +#define LOCK_WORD_GC_STATE_SIZE 2 +DEFINE_CHECK_EQ(static_cast(LOCK_WORD_GC_STATE_SIZE), (static_cast(art::LockWord::kGCStateSize))) #define LOCK_WORD_GC_STATE_SHIFT 28 DEFINE_CHECK_EQ(static_cast(LOCK_WORD_GC_STATE_SHIFT), (static_cast(art::LockWord::kGCStateShift))) #define LOCK_WORD_MARK_BIT_SHIFT 29 diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index e942ad62635753c9889563fadf872b561df14e00..5729800bb02c7b5ce16c8b3dc38b0a2e59c38af2 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -14,8 +14,6 @@ * limitations under the License. */ -#include - #include "hidden_api.h" #include @@ -24,11 +22,14 @@ #include "thread-current-inl.h" #include "well_known_classes.h" +#ifdef ART_TARGET_ANDROID +#include using android::metricslogger::ComplexEventLogger; using android::metricslogger::ACTION_HIDDEN_API_ACCESSED; using android::metricslogger::FIELD_HIDDEN_API_ACCESS_METHOD; using android::metricslogger::FIELD_HIDDEN_API_ACCESS_DENIED; using android::metricslogger::FIELD_HIDDEN_API_SIGNATURE; +#endif namespace art { namespace hiddenapi { @@ -137,6 +138,7 @@ void MemberSignature::WarnAboutAccess(AccessMethod access_method, LOG(WARNING) << "Accessing hidden " << (type_ == kField ? "field " : "method ") << Dumpable(*this) << " (" << list << ", " << access_method << ")"; } +#ifdef ART_TARGET_ANDROID // Convert an AccessMethod enum to a value for logging from the proto enum. // This method may look odd (the enum values are current the same), but it // prevents coupling the internal enum to the proto enum (which should never @@ -156,8 +158,10 @@ inline static int32_t GetEnumValueForLog(AccessMethod access_method) { DCHECK(false); } } +#endif void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action action_taken) { +#ifdef ART_TARGET_ANDROID if (access_method == kLinking || access_method == kNone) { // Linking warnings come from static analysis/compilation of the bytecode // and can contain false positives (i.e. code that is never run). We choose @@ -178,6 +182,10 @@ void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action act Dump(signature_str); log_maker.AddTaggedData(FIELD_HIDDEN_API_SIGNATURE, signature_str.str()); log_maker.Record(); +#else + UNUSED(access_method); + UNUSED(action_taken); +#endif } static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtField*) { @@ -233,7 +241,7 @@ Action GetMemberActionImpl(T* member, } } - if (kIsTargetBuild) { + if (kIsTargetBuild && !kIsTargetLinux) { uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate(); // Assert that RAND_MAX is big enough, to ensure sampling below works as expected. static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small"); diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc index 68d4eecbb9b4d0225c217c366ab215dfeaa0c400..a41d28492d11cffe927a7092c9f4c0f22e14522b 100644 --- a/runtime/hidden_api_test.cc +++ b/runtime/hidden_api_test.cc @@ -17,7 +17,7 @@ #include "hidden_api.h" #include "common_runtime_test.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "proxy_test.h" namespace art { @@ -325,8 +325,8 @@ TEST_F(HiddenApiTest, CheckMemberSignatureForProxyClass) { ASSERT_TRUE(h_iface != nullptr); // Create the proxy class. - std::vector interfaces; - interfaces.push_back(h_iface.Get()); + std::vector> interfaces; + interfaces.push_back(h_iface); Handle proxyClass = hs.NewHandle(proxy_test::GenerateProxyClass( soa, jclass_loader_, runtime_->GetClassLinker(), "$Proxy1234", interfaces)); ASSERT_TRUE(proxyClass != nullptr); diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index aa716f12ac8e216884ff603aca726cc413d986c7..c9e842634022b5c24f7c55725a2d9d8bedf703f5 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -24,7 +24,6 @@ #include "hprof.h" -#include #include #include #include @@ -42,6 +41,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" #include "base/array_ref.h" +#include "base/globals.h" #include "base/macros.h" #include "base/mutex.h" #include "base/os.h" @@ -49,6 +49,7 @@ #include "base/time_utils.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" +#include "class_root.h" #include "common_throws.h" #include "debugger.h" #include "dex/dex_file-inl.h" @@ -59,7 +60,6 @@ #include "gc/scoped_gc_critical_section.h" #include "gc/space/space.h" #include "gc_root.h" -#include "globals.h" #include "jdwp/jdwp.h" #include "jdwp/jdwp_priv.h" #include "mirror/class-inl.h" @@ -1418,8 +1418,7 @@ void Hprof::DumpFakeObjectArray(mirror::Object* obj, const std::setGetClassLinker()->GetClassRoot(ClassLinker::kObjectArrayClass))); + __ AddClassId(LookupClassId(GetClassRoot>().Ptr())); for (mirror::Object* e : elements) { __ AddObjectId(e); } diff --git a/runtime/image-inl.h b/runtime/image-inl.h index 935a1b6705bad97cbf92e6a30d923c23c1477c04..c527f6fbcc0a52936457623b0bd7782bb7de2c01 100644 --- a/runtime/image-inl.h +++ b/runtime/image-inl.h @@ -22,18 +22,20 @@ #include "art_method.h" #include "imt_conflict_table.h" #include "imtable.h" +#include "mirror/object_array-inl.h" +#include "obj_ptr-inl.h" #include "read_barrier-inl.h" namespace art { template -inline mirror::Object* ImageHeader::GetImageRoot(ImageRoot image_root) const { - mirror::ObjectArray* image_roots = GetImageRoots(); +inline ObjPtr ImageHeader::GetImageRoot(ImageRoot image_root) const { + ObjPtr> image_roots = GetImageRoots(); return image_roots->Get(static_cast(image_root)); } template -inline mirror::ObjectArray* ImageHeader::GetImageRoots() const { +inline ObjPtr> ImageHeader::GetImageRoots() const { // Need a read barrier as it's not visited during root scan. // Pass in the address of the local variable to the read barrier // rather than image_roots_ because it won't move (asserted below) diff --git a/runtime/image.cc b/runtime/image.cc index 3b13d05d808957bfb577c11637767512303eccc8..7819c0bc00ade215adea0241c6f20f1b6a6071f9 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', '5', '6', '\0' }; // ReachabilityFence. +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 159a308fb3059c692b99ab67a7f91625feac37db..c1cde0a74adbe1501b266976bb755b585d12cc90 100644 --- a/runtime/image.h +++ b/runtime/image.h @@ -19,15 +19,15 @@ #include -#include "base/bit_utils.h" #include "base/enums.h" -#include "globals.h" +#include "base/globals.h" #include "mirror/object.h" namespace art { class ArtField; class ArtMethod; +template class ObjPtr; namespace linker { class ImageWriter; @@ -207,8 +207,16 @@ class PACKED(4) ImageHeader { enum ImageRoot { kDexCaches, kClassRoots, - kClassLoader, // App image only. + kOomeWhenThrowingException, // Pre-allocated OOME when throwing exception. + kOomeWhenThrowingOome, // Pre-allocated OOME when throwing OOME. + kOomeWhenHandlingStackOverflow, // Pre-allocated OOME when handling StackOverflowError. + kNoClassDefFoundError, // Pre-allocated NoClassDefFoundError. + 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 { @@ -225,8 +233,10 @@ class PACKED(4) ImageHeader { 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; @@ -278,11 +288,11 @@ class PACKED(4) ImageHeader { } template - mirror::Object* GetImageRoot(ImageRoot image_root) const + ObjPtr GetImageRoot(ImageRoot image_root) const REQUIRES_SHARED(Locks::mutator_lock_); template - mirror::ObjectArray* GetImageRoots() const + ObjPtr> GetImageRoots() const REQUIRES_SHARED(Locks::mutator_lock_); void RelocateImage(off_t delta); @@ -327,22 +337,6 @@ class PACKED(4) ImageHeader { return boot_image_size_ != 0u; } - uint32_t GetBootImageConstantTablesOffset() const { - // Interned strings table and class table for boot image are mmapped read only. - DCHECK(!IsAppImage()); - const ImageSection& interned_strings = GetInternedStringsSection(); - DCHECK_ALIGNED(interned_strings.Offset(), kPageSize); - return interned_strings.Offset(); - } - - uint32_t GetBootImageConstantTablesSize() const { - uint32_t start_offset = GetBootImageConstantTablesOffset(); - const ImageSection& class_table = GetClassTableSection(); - DCHECK_LE(start_offset, class_table.Offset()); - size_t tables_size = class_table.Offset() + class_table.Size() - start_offset; - return RoundUp(tables_size, kPageSize); - } - // Visit mirror::Objects in the section starting at base. // TODO: Delete base parameter if it is always equal to GetImageBegin. void VisitObjects(ObjectVisitor* visitor, diff --git a/runtime/indirect_reference_table-inl.h b/runtime/indirect_reference_table-inl.h index 9673bd9728dfe4243f41f781860760e21f140bd1..2128f8cde8a641efe718315ff0afcf0ad35d9c5c 100644 --- a/runtime/indirect_reference_table-inl.h +++ b/runtime/indirect_reference_table-inl.h @@ -111,12 +111,12 @@ inline void IrtEntry::Add(ObjPtr obj) { if (serial_ == kIRTPrevCount) { serial_ = 0; } - references_[serial_] = GcRoot(obj.Ptr()); + references_[serial_] = GcRoot(obj); } inline void IrtEntry::SetReference(ObjPtr obj) { DCHECK_LT(serial_, kIRTPrevCount); - references_[serial_] = GcRoot(obj.Ptr()); + references_[serial_] = GcRoot(obj); } } // namespace art diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc index 3b9cc0f946962bae007bb6a5623e040d2e0eac8f..950a54d61ee24776b9126c05f6dd0c1faf7e5967 100644 --- a/runtime/indirect_reference_table.cc +++ b/runtime/indirect_reference_table.cc @@ -16,11 +16,11 @@ #include "indirect_reference_table-inl.h" -#include "base/dumpable-inl.h" +#include "base/mutator_locked_dumpable.h" #include "base/systrace.h" #include "base/utils.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "nth_caller_visitor.h" #include "reference_table.h" #include "runtime.h" diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc index 92785090f504a8ff35c2808eb584f2ef20b23125..141feb434fc565201c35be5593afe869f8192588 100644 --- a/runtime/indirect_reference_table_test.cc +++ b/runtime/indirect_reference_table_test.cc @@ -60,8 +60,9 @@ TEST_F(IndirectReferenceTableTest, BasicTest) { &error_msg); ASSERT_TRUE(irt.IsValid()) << error_msg; - mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); - StackHandleScope<4> hs(soa.Self()); + StackHandleScope<5> hs(soa.Self()); + Handle c = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")); ASSERT_TRUE(c != nullptr); Handle obj0 = hs.NewHandle(c->AllocObject(soa.Self())); ASSERT_TRUE(obj0 != nullptr); @@ -278,8 +279,9 @@ TEST_F(IndirectReferenceTableTest, Holes) { ScopedObjectAccess soa(Thread::Current()); static const size_t kTableMax = 10; - mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); - StackHandleScope<5> hs(soa.Self()); + StackHandleScope<6> hs(soa.Self()); + Handle c = hs.NewHandle( + class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")); ASSERT_TRUE(c != nullptr); Handle obj0 = hs.NewHandle(c->AllocObject(soa.Self())); ASSERT_TRUE(obj0 != nullptr); @@ -487,8 +489,9 @@ TEST_F(IndirectReferenceTableTest, Resize) { ScopedObjectAccess soa(Thread::Current()); static const size_t kTableMax = 512; - mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); - StackHandleScope<1> hs(soa.Self()); + StackHandleScope<2> hs(soa.Self()); + Handle c = hs.NewHandle( + class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")); ASSERT_TRUE(c != nullptr); Handle obj0 = hs.NewHandle(c->AllocObject(soa.Self())); ASSERT_TRUE(obj0 != nullptr); diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index d7f33d5e43796896c652967d6ca84e63e063e496..4196e193832e83d147cdedfcaa4f8e52e1a169a7 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -94,6 +94,56 @@ class InstallStubsClassVisitor : public ClassVisitor { Instrumentation* const instrumentation_; }; +InstrumentationStackPopper::InstrumentationStackPopper(Thread* self) + : self_(self), + instrumentation_(Runtime::Current()->GetInstrumentation()), + frames_to_remove_(0) {} + +InstrumentationStackPopper::~InstrumentationStackPopper() { + std::deque* stack = self_->GetInstrumentationStack(); + for (size_t i = 0; i < frames_to_remove_; i++) { + stack->pop_front(); + } +} + +bool InstrumentationStackPopper::PopFramesTo(uint32_t desired_pops, + MutableHandle& exception) { + std::deque* stack = self_->GetInstrumentationStack(); + DCHECK_LE(frames_to_remove_, desired_pops); + DCHECK_GE(stack->size(), desired_pops); + DCHECK(!self_->IsExceptionPending()); + if (!instrumentation_->HasMethodUnwindListeners()) { + frames_to_remove_ = desired_pops; + return true; + } + if (kVerboseInstrumentation) { + LOG(INFO) << "Popping frames for exception " << exception->Dump(); + } + // The instrumentation events expect the exception to be set. + self_->SetException(exception.Get()); + bool new_exception_thrown = false; + for (; frames_to_remove_ < desired_pops && !new_exception_thrown; frames_to_remove_++) { + InstrumentationStackFrame frame = stack->at(frames_to_remove_); + ArtMethod* method = frame.method_; + // Notify listeners of method unwind. + // TODO: improve the dex_pc information here. + uint32_t dex_pc = dex::kDexNoIndex; + if (kVerboseInstrumentation) { + LOG(INFO) << "Popping for unwind " << method->PrettyMethod(); + } + if (!method->IsRuntimeMethod() && !frame.interpreter_entry_) { + instrumentation_->MethodUnwindEvent(self_, frame.this_object_, method, dex_pc); + new_exception_thrown = self_->GetException() != exception.Get(); + } + } + exception.Assign(self_->GetException()); + self_->ClearException(); + if (kVerboseInstrumentation && new_exception_thrown) { + LOG(INFO) << "Failed to pop " << (desired_pops - frames_to_remove_) + << " frames due to new exception"; + } + return !new_exception_thrown; +} Instrumentation::Instrumentation() : instrumentation_stubs_installed_(false), @@ -112,7 +162,7 @@ Instrumentation::Instrumentation() have_branch_listeners_(false), have_invoke_virtual_or_interface_listeners_(false), have_exception_handled_listeners_(false), - deoptimized_methods_lock_("deoptimized methods lock", kDeoptimizedMethodsLock), + deoptimized_methods_lock_("deoptimized methods lock", kGenericBottomLock), deoptimization_enabled_(false), interpreter_handler_table_(kMainHandlerTable), quick_alloc_entry_points_instrumentation_counter_(0), @@ -154,8 +204,16 @@ void Instrumentation::InstallStubsForMethod(ArtMethod* method) { return; } // Don't stub Proxy.. Note that the Proxy class itself is not a proxy class. - if (method->IsConstructor() && - method->GetDeclaringClass()->DescriptorEquals("Ljava/lang/reflect/Proxy;")) { + // TODO We should remove the need for this since it means we cannot always correctly detect calls + // to Proxy. + // Annoyingly this can be called before we have actually initialized WellKnownClasses so therefore + // we also need to check this based on the declaring-class descriptor. The check is valid because + // Proxy only has a single constructor. + ArtMethod* well_known_proxy_init = jni::DecodeArtMethod( + WellKnownClasses::java_lang_reflect_Proxy_init); + if ((LIKELY(well_known_proxy_init != nullptr) && UNLIKELY(method == well_known_proxy_init)) || + UNLIKELY(method->IsConstructor() && + method->GetDeclaringClass()->DescriptorEquals("Ljava/lang/reflect/Proxy;"))) { return; } const void* new_quick_code; @@ -167,11 +225,7 @@ void Instrumentation::InstallStubsForMethod(ArtMethod* method) { if ((forced_interpret_only_ || IsDeoptimized(method)) && !method->IsNative()) { new_quick_code = GetQuickToInterpreterBridge(); } else if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) { - if (NeedDebugVersionFor(method)) { - new_quick_code = GetQuickToInterpreterBridge(); - } else { - new_quick_code = class_linker->GetQuickOatCodeFor(method); - } + new_quick_code = GetCodeForInvoke(method); } else { new_quick_code = GetQuickResolutionStub(); } @@ -184,12 +238,15 @@ void Instrumentation::InstallStubsForMethod(ArtMethod* method) { // class, all its static methods code will be set to the instrumentation entry point. // For more details, see ClassLinker::FixupStaticTrampolines. if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) { - if (NeedDebugVersionFor(method)) { - // Oat code should not be used. Don't install instrumentation stub and - // use interpreter for instrumentation. - new_quick_code = GetQuickToInterpreterBridge(); - } else if (entry_exit_stubs_installed_) { + if (entry_exit_stubs_installed_) { + // This needs to be checked first since the instrumentation entrypoint will be able to + // find the actual JIT compiled code that corresponds to this method. new_quick_code = GetQuickInstrumentationEntryPoint(); + } else if (NeedDebugVersionFor(method)) { + // It would be great to search the JIT for its implementation here but we cannot due to + // the locks we hold. Instead just set to the interpreter bridge and that code will search + // the JIT when it gets called and replace the entrypoint then. + new_quick_code = GetQuickToInterpreterBridge(); } else { new_quick_code = class_linker->GetQuickOatCodeFor(method); } @@ -244,7 +301,7 @@ static void InstrumentationInstallStack(Thread* thread, void* arg) if (m->IsRuntimeMethod()) { const InstrumentationStackFrame& frame = - instrumentation_stack_->at(instrumentation_stack_depth_); + (*instrumentation_stack_)[instrumentation_stack_depth_]; if (frame.interpreter_entry_) { // This instrumentation frame is for an interpreter bridge and is // pushed when executing the instrumented interpreter bridge. So method @@ -263,7 +320,7 @@ static void InstrumentationInstallStack(Thread* thread, void* arg) reached_existing_instrumentation_frames_ = true; const InstrumentationStackFrame& frame = - instrumentation_stack_->at(instrumentation_stack_depth_); + (*instrumentation_stack_)[instrumentation_stack_depth_]; CHECK_EQ(m, frame.method_) << "Expected " << ArtMethod::PrettyMethod(m) << ", Found " << ArtMethod::PrettyMethod(frame.method_); return_pc = frame.return_pc_; @@ -354,7 +411,7 @@ static void InstrumentationInstallStack(Thread* thread, void* arg) } uint32_t dex_pc = visitor.dex_pcs_.back(); visitor.dex_pcs_.pop_back(); - if (!isi->interpreter_entry_) { + if (!isi->interpreter_entry_ && !isi->method_->IsRuntimeMethod()) { instrumentation->MethodEnterEvent(thread, (*isi).this_object_, (*isi).method_, dex_pc); } } @@ -785,8 +842,24 @@ void Instrumentation::UpdateMethodsCodeImpl(ArtMethod* method, const void* quick if (class_linker->IsQuickResolutionStub(quick_code) || class_linker->IsQuickToInterpreterBridge(quick_code)) { new_quick_code = quick_code; - } else if (entry_exit_stubs_installed_) { + } else if (entry_exit_stubs_installed_ && + // We need to make sure not to replace anything that InstallStubsForMethod + // wouldn't. Specifically we cannot stub out Proxy. since subtypes copy the + // implementation directly and this will confuse the instrumentation trampolines. + // TODO We should remove the need for this since it makes it impossible to profile + // Proxy. correctly in all cases. + method != jni::DecodeArtMethod(WellKnownClasses::java_lang_reflect_Proxy_init)) { new_quick_code = GetQuickInstrumentationEntryPoint(); + if (!method->IsNative() && Runtime::Current()->GetJit() != nullptr) { + // Native methods use trampoline entrypoints during interpreter tracing. + DCHECK(!Runtime::Current()->GetJit()->GetCodeCache()->GetGarbageCollectCode()); + ProfilingInfo* profiling_info = method->GetProfilingInfo(kRuntimePointerSize); + // Tracing will look at the saved entry point in the profiling info to know the actual + // entrypoint, so we store it here. + if (profiling_info != nullptr) { + profiling_info->SetSavedEntryPoint(quick_code); + } + } } else { new_quick_code = quick_code; } @@ -912,7 +985,7 @@ void Instrumentation::Undeoptimize(ArtMethod* method) { } // If there is no deoptimized method left, we can restore the stack of each thread. - if (empty) { + if (empty && !entry_exit_stubs_installed_) { MutexLock mu(self, *Locks::thread_list_lock_); Runtime::Current()->GetThreadList()->ForEach(InstrumentationRestoreStack, this); instrumentation_stubs_installed_ = false; @@ -981,6 +1054,14 @@ void Instrumentation::EnableMethodTracing(const char* key, bool needs_interprete level = InstrumentationLevel::kInstrumentWithInterpreter; } else { level = InstrumentationLevel::kInstrumentWithInstrumentationStubs; + if (Runtime::Current()->GetJit() != nullptr) { + // TODO b/110263880 It would be better if we didn't need to do this. + // Since we need to hold the method entrypoint across a suspend to ensure instrumentation + // hooks are called correctly we have to disable jit-gc to ensure that the entrypoint doesn't + // go away. Furthermore we need to leave this off permanently since one could get the same + // effect by causing this to be toggled on and off. + Runtime::Current()->GetJit()->GetCodeCache()->SetGarbageCollectCode(false); + } } ConfigureStubs(key, level); } @@ -989,6 +1070,49 @@ void Instrumentation::DisableMethodTracing(const char* key) { ConfigureStubs(key, InstrumentationLevel::kInstrumentNothing); } +const void* Instrumentation::GetCodeForInvoke(ArtMethod* method) const { + // This is called by instrumentation entry only and that should never be getting proxy methods. + DCHECK(!method->IsProxyMethod()) << method->PrettyMethod(); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + if (LIKELY(!instrumentation_stubs_installed_ && !interpreter_stubs_installed_)) { + // In general we just return whatever the method thinks its entrypoint is here. The only + // exception is if it still has the instrumentation entrypoint. That means we are racing another + // thread getting rid of instrumentation which is unexpected but possible. In that case we want + // to wait and try to get it from the oat file or jit. + const void* code = method->GetEntryPointFromQuickCompiledCodePtrSize(kRuntimePointerSize); + DCHECK(code != nullptr); + if (code != GetQuickInstrumentationEntryPoint()) { + return code; + } else if (method->IsNative()) { + return class_linker->GetQuickOatCodeFor(method); + } + // We don't know what it is. Fallthough to try to find the code from the JIT or Oat file. + } else if (method->IsNative()) { + // TODO We could have JIT compiled native entrypoints. It might be worth it to find these. + return class_linker->GetQuickOatCodeFor(method); + } else if (UNLIKELY(interpreter_stubs_installed_)) { + return GetQuickToInterpreterBridge(); + } + // Since the method cannot be native due to ifs above we can always fall back to interpreter + // bridge. + const void* result = GetQuickToInterpreterBridge(); + if (!NeedDebugVersionFor(method)) { + // If we don't need a debug version we should see what the oat file/class linker has to say. + result = class_linker->GetQuickOatCodeFor(method); + } + // If both those fail try the jit. + if (result == GetQuickToInterpreterBridge()) { + jit::Jit* jit = Runtime::Current()->GetJit(); + if (jit != nullptr) { + const void* res = jit->GetCodeCache()->FindCompiledCodeForInstrumentation(method); + if (res != nullptr) { + result = res; + } + } + } + return result; +} + const void* Instrumentation::GetQuickCodeFor(ArtMethod* method, PointerSize pointer_size) const { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); if (LIKELY(!instrumentation_stubs_installed_)) { @@ -1414,36 +1538,29 @@ TwoWordReturn Instrumentation::PopInstrumentationStackFrame(Thread* self, } } -uintptr_t Instrumentation::PopMethodForUnwind(Thread* self, bool is_deoptimization) const { - // Do the pop. +uintptr_t Instrumentation::PopFramesForDeoptimization(Thread* self, size_t nframes) const { std::deque* stack = self->GetInstrumentationStack(); - CHECK_GT(stack->size(), 0U); - size_t idx = stack->size(); - InstrumentationStackFrame instrumentation_frame = stack->front(); - - ArtMethod* method = instrumentation_frame.method_; - if (is_deoptimization) { - if (kVerboseInstrumentation) { - LOG(INFO) << "Popping for deoptimization " << ArtMethod::PrettyMethod(method); - } - } else { - if (kVerboseInstrumentation) { - LOG(INFO) << "Popping for unwind " << ArtMethod::PrettyMethod(method); - } - - // Notify listeners of method unwind. - // TODO: improve the dex pc information here, requires knowledge of current PC as opposed to - // return_pc. - uint32_t dex_pc = dex::kDexNoIndex; - if (!method->IsRuntimeMethod()) { - MethodUnwindEvent(self, instrumentation_frame.this_object_, method, dex_pc); + CHECK_GE(stack->size(), nframes); + if (nframes == 0) { + return 0u; + } + // Only need to send instrumentation events if it's not for deopt (do give the log messages if we + // have verbose-instrumentation anyway though). + if (kVerboseInstrumentation) { + for (size_t i = 0; i < nframes; i++) { + LOG(INFO) << "Popping for deoptimization " << stack->at(i).method_->PrettyMethod(); } } - // TODO: bring back CheckStackDepth(self, instrumentation_frame, 2); - CHECK_EQ(stack->size(), idx); - DCHECK(instrumentation_frame.method_ == stack->front().method_); + // Now that we've sent all the instrumentation events we can actually modify the + // instrumentation-stack. We cannot do this earlier since MethodUnwindEvent can re-enter java and + // do other things that require the instrumentation stack to be in a consistent state with the + // actual stack. + for (size_t i = 0; i < nframes - 1; i++) { + stack->pop_front(); + } + uintptr_t return_pc = stack->front().return_pc_; stack->pop_front(); - return instrumentation_frame.return_pc_; + return return_pc; } std::string InstrumentationStackFrame::Dump() const { diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index 8e7a638d59b332097d8f7cc5871219b136898c6c..e5d8800fcce1e270b649fb6ff645388f56897cd1 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -37,6 +37,7 @@ class Throwable; class ArtField; class ArtMethod; template class Handle; +template class MutableHandle; union JValue; class ShadowFrame; class Thread; @@ -158,6 +159,25 @@ struct InstrumentationListener { REQUIRES_SHARED(Locks::mutator_lock_) = 0; }; +class Instrumentation; +// A helper to send instrumentation events while popping the stack in a safe way. +class InstrumentationStackPopper { + public: + explicit InstrumentationStackPopper(Thread* self); + ~InstrumentationStackPopper() REQUIRES_SHARED(Locks::mutator_lock_); + + // Increase the number of frames being popped to 'desired_pops' return true if the frames were + // popped without any exceptions, false otherwise. The exception that caused the pop is + // 'exception'. + bool PopFramesTo(uint32_t desired_pops, /*in-out*/MutableHandle& exception) + REQUIRES_SHARED(Locks::mutator_lock_); + + private: + Thread* self_; + Instrumentation* instrumentation_; + uint32_t frames_to_remove_; +}; + // Instrumentation is a catch-all for when extra information is required from the runtime. The // typical use for instrumentation is for profiling and debugging. Instrumentation may add stubs // to method entry and exit, it may also force execution to be switched to the interpreter and @@ -292,6 +312,10 @@ class Instrumentation { void UpdateMethodsCodeForJavaDebuggable(ArtMethod* method, const void* quick_code) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!deoptimized_methods_lock_); + // Return the code that we can execute for an invoke including from the JIT. + const void* GetCodeForInvoke(ArtMethod* method) const + REQUIRES_SHARED(Locks::mutator_lock_); + // Get the quick code for the given method. More efficient than asking the class linker as it // will short-cut to GetCode if instrumentation and static method resolution stubs aren't // installed. @@ -494,9 +518,9 @@ class Instrumentation { uint64_t* gpr_result, uint64_t* fpr_result) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!deoptimized_methods_lock_); - // Pops an instrumentation frame from the current thread and generate an unwind event. - // Returns the return pc for the instrumentation frame that's popped. - uintptr_t PopMethodForUnwind(Thread* self, bool is_deoptimization) const + // Pops nframes instrumentation frames from the current thread. Returns the return pc for the last + // instrumentation frame that's popped. + uintptr_t PopFramesForDeoptimization(Thread* self, size_t nframes) const REQUIRES_SHARED(Locks::mutator_lock_); // Call back for configure stubs. @@ -696,7 +720,7 @@ class Instrumentation { // The set of methods being deoptimized (by the debugger) which must be executed with interpreter // only. - mutable ReaderWriterMutex deoptimized_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + mutable ReaderWriterMutex deoptimized_methods_lock_ BOTTOM_MUTEX_ACQUIRED_AFTER; std::unordered_set deoptimized_methods_ GUARDED_BY(deoptimized_methods_lock_); bool deoptimization_enabled_; @@ -713,6 +737,7 @@ class Instrumentation { bool alloc_entrypoints_instrumented_; friend class InstrumentationTest; // For GetCurrentInstrumentationLevel and ConfigureStubs. + friend class InstrumentationStackPopper; // For popping instrumentation frames. DISALLOW_COPY_AND_ASSIGN(Instrumentation); }; diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc index 836bbe711fc9f59337a6ad20060a4f2380238cca..8ac26afe9f25f9974201dfe2846e1eab62869b95 100644 --- a/runtime/instrumentation_test.cc +++ b/runtime/instrumentation_test.cc @@ -24,7 +24,7 @@ #include "dex/dex_file.h" #include "gc/scoped_gc_critical_section.h" #include "handle_scope-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "jvalue.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" @@ -520,7 +520,7 @@ TEST_F(InstrumentationTest, MethodEntryEvent) { ClassLinker* class_linker = runtime->GetClassLinker(); StackHandleScope<1> hs(soa.Self()); Handle loader(hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); + ObjPtr klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); ASSERT_TRUE(klass != nullptr); ArtMethod* method = klass->FindClassMethod("returnReference", "()Ljava/lang/Object;", kRuntimePointerSize); @@ -540,7 +540,7 @@ TEST_F(InstrumentationTest, MethodExitObjectEvent) { ClassLinker* class_linker = runtime->GetClassLinker(); StackHandleScope<1> hs(soa.Self()); Handle loader(hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); + ObjPtr klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); ASSERT_TRUE(klass != nullptr); ArtMethod* method = klass->FindClassMethod("returnReference", "()Ljava/lang/Object;", kRuntimePointerSize); @@ -560,7 +560,7 @@ TEST_F(InstrumentationTest, MethodExitPrimEvent) { ClassLinker* class_linker = runtime->GetClassLinker(); StackHandleScope<1> hs(soa.Self()); Handle loader(hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); + ObjPtr klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); ASSERT_TRUE(klass != nullptr); ArtMethod* method = klass->FindClassMethod("returnPrimitive", "()I", kRuntimePointerSize); ASSERT_TRUE(method != nullptr); @@ -595,7 +595,7 @@ TEST_F(InstrumentationTest, FieldWriteObjectEvent) { ClassLinker* class_linker = runtime->GetClassLinker(); StackHandleScope<1> hs(soa.Self()); Handle loader(hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); + ObjPtr klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); ASSERT_TRUE(klass != nullptr); ArtField* field = klass->FindDeclaredStaticField("referenceField", "Ljava/lang/Object;"); ASSERT_TRUE(field != nullptr); @@ -613,7 +613,7 @@ TEST_F(InstrumentationTest, FieldWritePrimEvent) { ClassLinker* class_linker = runtime->GetClassLinker(); StackHandleScope<1> hs(soa.Self()); Handle loader(hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); + ObjPtr klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); ASSERT_TRUE(klass != nullptr); ArtField* field = klass->FindDeclaredStaticField("primitiveField", "I"); ASSERT_TRUE(field != nullptr); @@ -648,7 +648,7 @@ TEST_F(InstrumentationTest, DeoptimizeDirectMethod) { ClassLinker* class_linker = runtime->GetClassLinker(); StackHandleScope<1> hs(soa.Self()); Handle loader(hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); + ObjPtr klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); ASSERT_TRUE(klass != nullptr); ArtMethod* method_to_deoptimize = klass->FindClassMethod("instanceMethod", "()V", kRuntimePointerSize); @@ -697,7 +697,7 @@ TEST_F(InstrumentationTest, MixedDeoptimization) { ClassLinker* class_linker = runtime->GetClassLinker(); StackHandleScope<1> hs(soa.Self()); Handle loader(hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); + ObjPtr klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); ASSERT_TRUE(klass != nullptr); ArtMethod* method_to_deoptimize = klass->FindClassMethod("instanceMethod", "()V", kRuntimePointerSize); diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc index 2db8815fdd3e0a9470729f97af1900737efc5ab7..c8aaa215893eb0f123608f26f32d0a2d4ca37555 100644 --- a/runtime/intern_table.cc +++ b/runtime/intern_table.cc @@ -366,7 +366,7 @@ bool InternTable::StringHashEquals::operator()(const GcRoot& a, size_t InternTable::Table::AddTableFromMemory(const uint8_t* ptr) { size_t read_count = 0; UnorderedSet set(ptr, /*make copy*/false, &read_count); - if (set.Empty()) { + if (set.empty()) { // Avoid inserting empty sets. return read_count; } @@ -392,7 +392,7 @@ size_t InternTable::Table::WriteToMemory(uint8_t* ptr) { table_to_write = &combined; for (UnorderedSet& table : tables_) { for (GcRoot& string : table) { - combined.Insert(string); + combined.insert(string); } } } else { @@ -403,9 +403,9 @@ size_t InternTable::Table::WriteToMemory(uint8_t* ptr) { void InternTable::Table::Remove(ObjPtr s) { for (UnorderedSet& table : tables_) { - auto it = table.Find(GcRoot(s)); + auto it = table.find(GcRoot(s)); if (it != table.end()) { - table.Erase(it); + table.erase(it); return; } } @@ -415,7 +415,7 @@ void InternTable::Table::Remove(ObjPtr s) { ObjPtr InternTable::Table::Find(ObjPtr s) { Locks::intern_table_lock_->AssertHeld(Thread::Current()); for (UnorderedSet& table : tables_) { - auto it = table.Find(GcRoot(s)); + auto it = table.find(GcRoot(s)); if (it != table.end()) { return it->Read(); } @@ -426,7 +426,7 @@ ObjPtr InternTable::Table::Find(ObjPtr s) { ObjPtr InternTable::Table::Find(const Utf8String& string) { Locks::intern_table_lock_->AssertHeld(Thread::Current()); for (UnorderedSet& table : tables_) { - auto it = table.Find(string); + auto it = table.find(string); if (it != table.end()) { return it->Read(); } @@ -442,7 +442,7 @@ void InternTable::Table::Insert(ObjPtr s) { // Always insert the last table, the image tables are before and we avoid inserting into these // to prevent dirty pages. DCHECK(!tables_.empty()); - tables_.back().Insert(GcRoot(s)); + tables_.back().insert(GcRoot(s)); } void InternTable::Table::VisitRoots(RootVisitor* visitor) { @@ -467,7 +467,7 @@ void InternTable::Table::SweepWeaks(UnorderedSet* set, IsMarkedVisitor* visitor) mirror::Object* object = it->Read(); mirror::Object* new_object = visitor->IsMarked(object); if (new_object == nullptr) { - it = set->Erase(it); + it = set->erase(it); } else { *it = GcRoot(new_object->AsString()); ++it; @@ -480,7 +480,7 @@ size_t InternTable::Table::Size() const { tables_.end(), 0U, [](size_t sum, const UnorderedSet& set) { - return sum + set.Size(); + return sum + set.size(); }); } diff --git a/runtime/intern_table.h b/runtime/intern_table.h index cb976917e6e071bad05301b78013938f22a83fcd..c9127d698770eac0356d42635dc62901bcec7771 100644 --- a/runtime/intern_table.h +++ b/runtime/intern_table.h @@ -227,7 +227,6 @@ class InternTable { // modifying the zygote intern table. The back of table is modified when strings are interned. std::vector tables_; - friend class linker::OatWriter; // for boot image string table slot address lookup. ART_FRIEND_TEST(InternTableTest, CrossHash); }; @@ -287,7 +286,6 @@ class InternTable { // Weak root state, used for concurrent system weak processing and more. gc::WeakRootState weak_root_state_ GUARDED_BY(Locks::intern_table_lock_); - friend class linker::OatWriter; // for boot image string table slot address lookup. friend class Transaction; ART_FRIEND_TEST(InternTableTest, CrossHash); DISALLOW_COPY_AND_ASSIGN(InternTable); diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 735c0e815ab3d92c03b5ac171802bff01223368f..048c6e4d6633121358df3798d445a13625f74ffa 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -31,6 +31,7 @@ #include "mterp/mterp.h" #include "nativehelper/scoped_local_ref.h" #include "scoped_thread_state_change-inl.h" +#include "shadow_frame-inl.h" #include "stack.h" #include "thread-inl.h" #include "unstarted_runtime.h" @@ -243,11 +244,13 @@ static inline JValue Execute( const CodeItemDataAccessor& accessor, ShadowFrame& shadow_frame, JValue result_register, - bool stay_in_interpreter = false) REQUIRES_SHARED(Locks::mutator_lock_) { + bool stay_in_interpreter = false, + bool from_deoptimize = false) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(!shadow_frame.GetMethod()->IsAbstract()); DCHECK(!shadow_frame.GetMethod()->IsNative()); - if (LIKELY(shadow_frame.GetDexPC() == 0)) { // Entering the method, but not via deoptimization. + if (LIKELY(!from_deoptimize)) { // Entering the method, but not via deoptimization. if (kIsDebugBuild) { + CHECK_EQ(shadow_frame.GetDexPC(), 0u); self->AssertNoPendingException(); } instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); @@ -417,7 +420,7 @@ void EnterInterpreterFromInvoke(Thread* self, size_t cur_reg = num_regs - num_ins; if (!method->IsStatic()) { CHECK(receiver != nullptr); - shadow_frame->SetVRegReference(cur_reg, receiver.Ptr()); + shadow_frame->SetVRegReference(cur_reg, receiver); ++cur_reg; } uint32_t shorty_len = 0; @@ -428,7 +431,7 @@ void EnterInterpreterFromInvoke(Thread* self, case 'L': { ObjPtr o = reinterpret_cast*>(&args[arg_pos])->AsMirrorPtr(); - shadow_frame->SetVRegReference(cur_reg, o.Ptr()); + shadow_frame->SetVRegReference(cur_reg, o); break; } case 'J': case 'D': { @@ -568,7 +571,12 @@ void EnterInterpreterFromDeoptimize(Thread* self, } if (new_dex_pc != dex::kDexNoIndex) { shadow_frame->SetDexPC(new_dex_pc); - value = Execute(self, accessor, *shadow_frame, value); + value = Execute(self, + accessor, + *shadow_frame, + value, + /* stay_in_interpreter */ true, + /* from_deoptimize */ true); } ShadowFrame* old_frame = shadow_frame; shadow_frame = shadow_frame->GetLink(); diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 380a981d0943569646fc2523fc00b103b5a92cb9..92d4731480f734247f4716a0efa0ebe973434a29 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -19,12 +19,13 @@ #include #include "base/enums.h" +#include "class_root.h" #include "debugger.h" #include "dex/dex_file_types.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "intrinsics_enum.h" #include "jit/jit.h" -#include "jvalue.h" +#include "jvalue-inl.h" #include "method_handles-inl.h" #include "method_handles.h" #include "mirror/array-inl.h" @@ -34,9 +35,11 @@ #include "mirror/var_handle.h" #include "reflection-inl.h" #include "reflection.h" +#include "shadow_frame-inl.h" #include "stack.h" #include "thread-inl.h" #include "transaction.h" +#include "var_handles.h" #include "well_known_classes.h" namespace art { @@ -626,7 +629,8 @@ static bool DoMethodHandleInvokeCommon(Thread* self, // The vRegH value gives the index of the proto_id associated with this // signature polymorphic call site. - const uint32_t callsite_proto_id = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc(); + const uint16_t vRegH = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc(); + const dex::ProtoIndex callsite_proto_id(vRegH); // Call through to the classlinker and ask it to resolve the static type associated // with the callsite. This information is stored in the dex cache so it's @@ -724,38 +728,6 @@ bool DoMethodHandleInvoke(Thread* self, } } -static bool DoVarHandleInvokeChecked(Thread* self, - Handle var_handle, - Handle callsite_type, - mirror::VarHandle::AccessMode access_mode, - ShadowFrame& shadow_frame, - InstructionOperands* operands, - JValue* result) - REQUIRES_SHARED(Locks::mutator_lock_) { - // TODO(oth): GetMethodTypeForAccessMode() allocates a MethodType() - // which is only required if we need to convert argument and/or - // return types. - StackHandleScope<1> hs(self); - Handle accessor_type(hs.NewHandle( - var_handle->GetMethodTypeForAccessMode(self, access_mode))); - const size_t num_vregs = accessor_type->NumberOfVRegs(); - const int num_params = accessor_type->GetPTypes()->GetLength(); - ShadowFrameAllocaUniquePtr accessor_frame = - CREATE_SHADOW_FRAME(num_vregs, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC()); - ShadowFrameGetter getter(shadow_frame, operands); - static const uint32_t kFirstDestinationReg = 0; - ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg); - if (!PerformConversions(self, callsite_type, accessor_type, &getter, &setter, num_params)) { - return false; - } - RangeInstructionOperands accessor_operands(kFirstDestinationReg, - kFirstDestinationReg + num_vregs); - if (!var_handle->Access(access_mode, accessor_frame.get(), &accessor_operands, result)) { - return false; - } - return ConvertReturnValue(callsite_type, accessor_type, result); -} - static bool DoVarHandleInvokeCommon(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, @@ -768,59 +740,43 @@ static bool DoVarHandleInvokeCommon(Thread* self, return false; } - bool is_var_args = inst->HasVarArgs(); - const uint32_t vRegC = is_var_args ? inst->VRegC_45cc() : inst->VRegC_4rcc(); - ObjPtr receiver(shadow_frame.GetVRegReference(vRegC)); - if (receiver.IsNull()) { - ThrowNullPointerExceptionFromDexPC(); - return false; - } - StackHandleScope<2> hs(self); - Handle var_handle(hs.NewHandle(down_cast(receiver.Ptr()))); - if (!var_handle->IsAccessModeSupported(access_mode)) { - ThrowUnsupportedOperationException(); - return false; - } - - const uint32_t vRegH = is_var_args ? inst->VRegH_45cc() : inst->VRegH_4rcc(); + bool is_var_args = inst->HasVarArgs(); + const uint16_t vRegH = is_var_args ? inst->VRegH_45cc() : inst->VRegH_4rcc(); ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); Handle callsite_type(hs.NewHandle( - class_linker->ResolveMethodType(self, vRegH, shadow_frame.GetMethod()))); + class_linker->ResolveMethodType(self, dex::ProtoIndex(vRegH), shadow_frame.GetMethod()))); // This implies we couldn't resolve one or more types in this VarHandle. if (UNLIKELY(callsite_type == nullptr)) { CHECK(self->IsExceptionPending()); return false; } - if (!var_handle->IsMethodTypeCompatible(access_mode, callsite_type.Get())) { - ThrowWrongMethodTypeException(var_handle->GetMethodTypeForAccessMode(self, access_mode), - callsite_type.Get()); - return false; - } - + const uint32_t vRegC = is_var_args ? inst->VRegC_45cc() : inst->VRegC_4rcc(); + ObjPtr receiver(shadow_frame.GetVRegReference(vRegC)); + Handle var_handle(hs.NewHandle(down_cast(receiver.Ptr()))); if (is_var_args) { uint32_t args[Instruction::kMaxVarArgRegs]; inst->GetVarArgs(args, inst_data); VarArgsInstructionOperands all_operands(args, inst->VRegA_45cc()); NoReceiverInstructionOperands operands(&all_operands); - return DoVarHandleInvokeChecked(self, - var_handle, - callsite_type, - access_mode, - shadow_frame, - &operands, - result); + return VarHandleInvokeAccessor(self, + shadow_frame, + var_handle, + callsite_type, + access_mode, + &operands, + result); } else { RangeInstructionOperands all_operands(inst->VRegC_4rcc(), inst->VRegA_4rcc()); NoReceiverInstructionOperands operands(&all_operands); - return DoVarHandleInvokeChecked(self, - var_handle, - callsite_type, - access_mode, - shadow_frame, - &operands, - result); + return VarHandleInvokeAccessor(self, + shadow_frame, + var_handle, + callsite_type, + access_mode, + &operands, + result); } } @@ -900,171 +856,488 @@ bool DoInvokePolymorphic(Thread* self, } } +static JValue ConvertScalarBootstrapArgument(jvalue value) { + // value either contains a primitive scalar value if it corresponds + // to a primitive type, or it contains an integer value if it + // corresponds to an object instance reference id (e.g. a string id). + return JValue::FromPrimitive(value.j); +} + +static ObjPtr GetClassForBootstrapArgument(EncodedArrayValueIterator::ValueType type) + REQUIRES_SHARED(Locks::mutator_lock_) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + ObjPtr> class_roots = class_linker->GetClassRoots(); + switch (type) { + case EncodedArrayValueIterator::ValueType::kBoolean: + case EncodedArrayValueIterator::ValueType::kByte: + case EncodedArrayValueIterator::ValueType::kChar: + case EncodedArrayValueIterator::ValueType::kShort: + // These types are disallowed by JVMS. Treat as integers. This + // will result in CCE's being raised if the BSM has one of these + // types. + case EncodedArrayValueIterator::ValueType::kInt: + return GetClassRoot(ClassRoot::kPrimitiveInt, class_roots); + case EncodedArrayValueIterator::ValueType::kLong: + return GetClassRoot(ClassRoot::kPrimitiveLong, class_roots); + case EncodedArrayValueIterator::ValueType::kFloat: + return GetClassRoot(ClassRoot::kPrimitiveFloat, class_roots); + case EncodedArrayValueIterator::ValueType::kDouble: + return GetClassRoot(ClassRoot::kPrimitiveDouble, class_roots); + case EncodedArrayValueIterator::ValueType::kMethodType: + return GetClassRoot(class_roots); + case EncodedArrayValueIterator::ValueType::kMethodHandle: + return GetClassRoot(class_roots); + case EncodedArrayValueIterator::ValueType::kString: + return GetClassRoot(); + case EncodedArrayValueIterator::ValueType::kType: + return GetClassRoot(); + case EncodedArrayValueIterator::ValueType::kField: + case EncodedArrayValueIterator::ValueType::kMethod: + case EncodedArrayValueIterator::ValueType::kEnum: + case EncodedArrayValueIterator::ValueType::kArray: + case EncodedArrayValueIterator::ValueType::kAnnotation: + case EncodedArrayValueIterator::ValueType::kNull: + return nullptr; + } +} + +static bool GetArgumentForBootstrapMethod(Thread* self, + ArtMethod* referrer, + EncodedArrayValueIterator::ValueType type, + const JValue* encoded_value, + JValue* decoded_value) + REQUIRES_SHARED(Locks::mutator_lock_) { + // The encoded_value contains either a scalar value (IJDF) or a + // scalar DEX file index to a reference type to be materialized. + switch (type) { + case EncodedArrayValueIterator::ValueType::kInt: + case EncodedArrayValueIterator::ValueType::kFloat: + decoded_value->SetI(encoded_value->GetI()); + return true; + case EncodedArrayValueIterator::ValueType::kLong: + case EncodedArrayValueIterator::ValueType::kDouble: + decoded_value->SetJ(encoded_value->GetJ()); + return true; + case EncodedArrayValueIterator::ValueType::kMethodType: { + StackHandleScope<2> hs(self); + Handle class_loader(hs.NewHandle(referrer->GetClassLoader())); + Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); + dex::ProtoIndex proto_idx(encoded_value->GetC()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + ObjPtr o = + cl->ResolveMethodType(self, proto_idx, dex_cache, class_loader); + if (UNLIKELY(o.IsNull())) { + DCHECK(self->IsExceptionPending()); + return false; + } + decoded_value->SetL(o); + return true; + } + case EncodedArrayValueIterator::ValueType::kMethodHandle: { + uint32_t index = static_cast(encoded_value->GetI()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + ObjPtr o = cl->ResolveMethodHandle(self, index, referrer); + if (UNLIKELY(o.IsNull())) { + DCHECK(self->IsExceptionPending()); + return false; + } + decoded_value->SetL(o); + return true; + } + case EncodedArrayValueIterator::ValueType::kString: { + dex::StringIndex index(static_cast(encoded_value->GetI())); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + ObjPtr o = cl->ResolveString(index, referrer); + if (UNLIKELY(o.IsNull())) { + DCHECK(self->IsExceptionPending()); + return false; + } + decoded_value->SetL(o); + return true; + } + case EncodedArrayValueIterator::ValueType::kType: { + dex::TypeIndex index(static_cast(encoded_value->GetI())); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + ObjPtr o = cl->ResolveType(index, referrer); + if (UNLIKELY(o.IsNull())) { + DCHECK(self->IsExceptionPending()); + return false; + } + decoded_value->SetL(o); + return true; + } + case EncodedArrayValueIterator::ValueType::kBoolean: + case EncodedArrayValueIterator::ValueType::kByte: + case EncodedArrayValueIterator::ValueType::kChar: + case EncodedArrayValueIterator::ValueType::kShort: + case EncodedArrayValueIterator::ValueType::kField: + case EncodedArrayValueIterator::ValueType::kMethod: + case EncodedArrayValueIterator::ValueType::kEnum: + case EncodedArrayValueIterator::ValueType::kArray: + case EncodedArrayValueIterator::ValueType::kAnnotation: + case EncodedArrayValueIterator::ValueType::kNull: + // Unreachable - unsupported types that have been checked when + // determining the effect call site type based on the bootstrap + // argument types. + UNREACHABLE(); + } +} + +static bool PackArgumentForBootstrapMethod(Thread* self, + ArtMethod* referrer, + CallSiteArrayValueIterator* it, + ShadowFrameSetter* setter) + REQUIRES_SHARED(Locks::mutator_lock_) { + auto type = it->GetValueType(); + const JValue encoded_value = ConvertScalarBootstrapArgument(it->GetJavaValue()); + JValue decoded_value; + if (!GetArgumentForBootstrapMethod(self, referrer, type, &encoded_value, &decoded_value)) { + return false; + } + switch (it->GetValueType()) { + case EncodedArrayValueIterator::ValueType::kInt: + case EncodedArrayValueIterator::ValueType::kFloat: + setter->Set(static_cast(decoded_value.GetI())); + return true; + case EncodedArrayValueIterator::ValueType::kLong: + case EncodedArrayValueIterator::ValueType::kDouble: + setter->SetLong(decoded_value.GetJ()); + return true; + case EncodedArrayValueIterator::ValueType::kMethodType: + case EncodedArrayValueIterator::ValueType::kMethodHandle: + case EncodedArrayValueIterator::ValueType::kString: + case EncodedArrayValueIterator::ValueType::kType: + setter->SetReference(decoded_value.GetL()); + return true; + case EncodedArrayValueIterator::ValueType::kBoolean: + case EncodedArrayValueIterator::ValueType::kByte: + case EncodedArrayValueIterator::ValueType::kChar: + case EncodedArrayValueIterator::ValueType::kShort: + case EncodedArrayValueIterator::ValueType::kField: + case EncodedArrayValueIterator::ValueType::kMethod: + case EncodedArrayValueIterator::ValueType::kEnum: + case EncodedArrayValueIterator::ValueType::kArray: + case EncodedArrayValueIterator::ValueType::kAnnotation: + case EncodedArrayValueIterator::ValueType::kNull: + // Unreachable - unsupported types that have been checked when + // determining the effect call site type based on the bootstrap + // argument types. + UNREACHABLE(); + } +} + +static bool PackCollectorArrayForBootstrapMethod(Thread* self, + ArtMethod* referrer, + ObjPtr array_type, + int32_t array_length, + CallSiteArrayValueIterator* it, + ShadowFrameSetter* setter) + REQUIRES_SHARED(Locks::mutator_lock_) { + StackHandleScope<1> hs(self); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + JValue decoded_value; + +#define COLLECT_PRIMITIVE_ARRAY(Descriptor, Type) \ + Handle array = \ + hs.NewHandle(mirror::Type ## Array::Alloc(self, array_length)); \ + if (array.IsNull()) { \ + return false; \ + } \ + for (int32_t i = 0; it->HasNext(); it->Next(), ++i) { \ + auto type = it->GetValueType(); \ + DCHECK_EQ(type, EncodedArrayValueIterator::ValueType::k ## Type); \ + const JValue encoded_value = \ + ConvertScalarBootstrapArgument(it->GetJavaValue()); \ + GetArgumentForBootstrapMethod(self, \ + referrer, \ + type, \ + &encoded_value, \ + &decoded_value); \ + array->Set(i, decoded_value.Get ## Descriptor()); \ + } \ + setter->SetReference(array.Get()); \ + return true; + +#define COLLECT_REFERENCE_ARRAY(T, Type) \ + Handle> array = \ + hs.NewHandle(mirror::ObjectArray::Alloc(self, \ + array_type, \ + array_length)); \ + if (array.IsNull()) { \ + return false; \ + } \ + for (int32_t i = 0; it->HasNext(); it->Next(), ++i) { \ + auto type = it->GetValueType(); \ + DCHECK_EQ(type, EncodedArrayValueIterator::ValueType::k ## Type); \ + const JValue encoded_value = \ + ConvertScalarBootstrapArgument(it->GetJavaValue()); \ + if (!GetArgumentForBootstrapMethod(self, \ + referrer, \ + type, \ + &encoded_value, \ + &decoded_value)) { \ + return false; \ + } \ + ObjPtr o = decoded_value.GetL(); \ + if (Runtime::Current()->IsActiveTransaction()) { \ + array->Set(i, ObjPtr::DownCast(o)); \ + } else { \ + array->Set(i, ObjPtr::DownCast(o)); \ + } \ + } \ + setter->SetReference(array.Get()); \ + return true; + + ObjPtr> class_roots = class_linker->GetClassRoots(); + ObjPtr component_type = array_type->GetComponentType(); + if (component_type == GetClassRoot(ClassRoot::kPrimitiveInt, class_roots)) { + COLLECT_PRIMITIVE_ARRAY(I, Int); + } else if (component_type == GetClassRoot(ClassRoot::kPrimitiveLong, class_roots)) { + COLLECT_PRIMITIVE_ARRAY(J, Long); + } else if (component_type == GetClassRoot(ClassRoot::kPrimitiveFloat, class_roots)) { + COLLECT_PRIMITIVE_ARRAY(F, Float); + } else if (component_type == GetClassRoot(ClassRoot::kPrimitiveDouble, class_roots)) { + COLLECT_PRIMITIVE_ARRAY(D, Double); + } else if (component_type == GetClassRoot()) { + COLLECT_REFERENCE_ARRAY(mirror::MethodType, MethodType); + } else if (component_type == GetClassRoot()) { + COLLECT_REFERENCE_ARRAY(mirror::MethodHandle, MethodHandle); + } else if (component_type == GetClassRoot(class_roots)) { + COLLECT_REFERENCE_ARRAY(mirror::String, String); + } else if (component_type == GetClassRoot()) { + COLLECT_REFERENCE_ARRAY(mirror::Class, Type); + } else { + UNREACHABLE(); + } + #undef COLLECT_PRIMITIVE_ARRAY + #undef COLLECT_REFERENCE_ARRAY +} + +static ObjPtr BuildCallSiteForBootstrapMethod(Thread* self, + const DexFile* dex_file, + uint32_t call_site_idx) + REQUIRES_SHARED(Locks::mutator_lock_) { + const DexFile::CallSiteIdItem& csi = dex_file->GetCallSiteId(call_site_idx); + CallSiteArrayValueIterator it(*dex_file, csi); + DCHECK_GE(it.Size(), 1u); + + StackHandleScope<2> hs(self); + // Create array for parameter types. + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + ObjPtr class_array_type = + GetClassRoot>(class_linker); + Handle> ptypes = hs.NewHandle( + mirror::ObjectArray::Alloc(self, + class_array_type, + static_cast(it.Size()))); + if (ptypes.IsNull()) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + + // Populate the first argument with an instance of j.l.i.MethodHandles.Lookup + // that the runtime will construct. + ptypes->Set(0, GetClassRoot(class_linker)); + it.Next(); + + // The remaining parameter types are derived from the types of + // arguments present in the DEX file. + int index = 1; + while (it.HasNext()) { + ObjPtr ptype = GetClassForBootstrapArgument(it.GetValueType()); + if (ptype.IsNull()) { + ThrowClassCastException("Unsupported bootstrap argument type"); + return nullptr; + } + ptypes->Set(index, ptype); + index++; + it.Next(); + } + DCHECK_EQ(static_cast(index), it.Size()); + + // By definition, the return type is always a j.l.i.CallSite. + Handle rtype = hs.NewHandle(GetClassRoot()); + return mirror::MethodType::Create(self, rtype, ptypes); +} + static ObjPtr InvokeBootstrapMethod(Thread* self, ShadowFrame& shadow_frame, uint32_t call_site_idx) REQUIRES_SHARED(Locks::mutator_lock_) { + 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 + // to pass to the bootstrap method. + static constexpr size_t kMandatoryArgumentsCount = 3; ArtMethod* referrer = shadow_frame.GetMethod(); const DexFile* dex_file = referrer->GetDexFile(); const DexFile::CallSiteIdItem& csi = dex_file->GetCallSiteId(call_site_idx); + CallSiteArrayValueIterator it(*dex_file, csi); + if (it.Size() < kMandatoryArgumentsCount) { + ThrowBootstrapMethodError("Truncated bootstrap arguments (%zu < %zu)", + it.Size(), kMandatoryArgumentsCount); + return nullptr; + } - StackHandleScope<10> hs(self); - Handle class_loader(hs.NewHandle(referrer->GetClassLoader())); - Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); + if (it.GetValueType() != EncodedArrayValueIterator::ValueType::kMethodHandle) { + ThrowBootstrapMethodError("First bootstrap argument is not a method handle"); + return nullptr; + } + + uint32_t bsm_index = static_cast(it.GetJavaValue().i); + it.Next(); - CallSiteArrayValueIterator it(*dex_file, csi); - uint32_t method_handle_idx = static_cast(it.GetJavaValue().i); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - Handle - bootstrap(hs.NewHandle(class_linker->ResolveMethodHandle(self, method_handle_idx, referrer))); - if (bootstrap.IsNull()) { + Handle bsm = + hs.NewHandle(class_linker->ResolveMethodHandle(self, bsm_index, referrer)); + if (bsm.IsNull()) { DCHECK(self->IsExceptionPending()); return nullptr; } - Handle bootstrap_method_type = hs.NewHandle(bootstrap->GetMethodType()); - it.Next(); - DCHECK_EQ(static_cast(bootstrap->GetMethodType()->GetPTypes()->GetLength()), it.Size()); - const size_t num_bootstrap_vregs = bootstrap->GetMethodType()->NumberOfVRegs(); + if (bsm->GetHandleKind() != mirror::MethodHandle::Kind::kInvokeStatic) { + // JLS suggests also accepting constructors. This is currently + // hard as constructor invocations happen via transformers in ART + // today. The constructor would need to be a class derived from java.lang.invoke.CallSite. + ThrowBootstrapMethodError("Unsupported bootstrap method invocation kind"); + return nullptr; + } - // Set-up a shadow frame for invoking the bootstrap method handle. - ShadowFrameAllocaUniquePtr bootstrap_frame = - CREATE_SHADOW_FRAME(num_bootstrap_vregs, nullptr, referrer, shadow_frame.GetDexPC()); - ScopedStackedShadowFramePusher pusher( - self, bootstrap_frame.get(), StackedShadowFrameType::kShadowFrameUnderConstruction); - size_t vreg = 0; + // Construct the local call site type information based on the 3 + // mandatory arguments provided by the runtime and the static arguments + // in the DEX file. We will use these arguments to build a shadow frame. + MutableHandle call_site_type = + hs.NewHandle(BuildCallSiteForBootstrapMethod(self, dex_file, call_site_idx)); + if (call_site_type.IsNull()) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } - // The first parameter is a MethodHandles lookup instance. - { - Handle lookup_class = - hs.NewHandle(shadow_frame.GetMethod()->GetDeclaringClass()); - ObjPtr lookup = - mirror::MethodHandlesLookup::Create(self, lookup_class); - if (lookup.IsNull()) { + // Check if this BSM is targeting a variable arity method. If so, + // we'll need to collect the trailing arguments into an array. + Handle collector_arguments; + int32_t collector_arguments_length; + if (bsm->GetTargetMethod()->IsVarargs()) { + int number_of_bsm_parameters = bsm->GetMethodType()->GetNumberOfPTypes(); + if (number_of_bsm_parameters == 0) { + ThrowBootstrapMethodError("Variable arity BSM does not have any arguments"); + return nullptr; + } + Handle collector_array_class = + hs.NewHandle(bsm->GetMethodType()->GetPTypes()->Get(number_of_bsm_parameters - 1)); + if (!collector_array_class->IsArrayClass()) { + ThrowBootstrapMethodError("Variable arity BSM does not have array as final argument"); + return nullptr; + } + // The call site may include no arguments to be collected. In this + // case the number of arguments must be at least the number of BSM + // parameters less the collector array. + if (call_site_type->GetNumberOfPTypes() < number_of_bsm_parameters - 1) { + ThrowWrongMethodTypeException(bsm->GetMethodType(), call_site_type.Get()); + return nullptr; + } + // Check all the arguments to be collected match the collector array component type. + for (int i = number_of_bsm_parameters - 1; i < call_site_type->GetNumberOfPTypes(); ++i) { + if (call_site_type->GetPTypes()->Get(i) != collector_array_class->GetComponentType()) { + ThrowClassCastException(collector_array_class->GetComponentType(), + call_site_type->GetPTypes()->Get(i)); + return nullptr; + } + } + // Update the call site method type so it now includes the collector array. + int32_t collector_arguments_start = number_of_bsm_parameters - 1; + collector_arguments_length = call_site_type->GetNumberOfPTypes() - number_of_bsm_parameters + 1; + call_site_type.Assign( + mirror::MethodType::CollectTrailingArguments(self, + call_site_type.Get(), + collector_array_class.Get(), + collector_arguments_start)); + if (call_site_type.IsNull()) { DCHECK(self->IsExceptionPending()); return nullptr; } - bootstrap_frame->SetVRegReference(vreg++, lookup.Ptr()); + } else { + collector_arguments_length = 0; } - // The second parameter is the name to lookup. - { - dex::StringIndex name_idx(static_cast(it.GetJavaValue().i)); - ObjPtr name = class_linker->ResolveString(name_idx, dex_cache); - if (name.IsNull()) { - DCHECK(self->IsExceptionPending()); + if (call_site_type->GetNumberOfPTypes() != bsm->GetMethodType()->GetNumberOfPTypes()) { + ThrowWrongMethodTypeException(bsm->GetMethodType(), call_site_type.Get()); + return nullptr; + } + + // BSM invocation has a different set of exceptions that + // j.l.i.MethodHandle.invoke(). Scan arguments looking for CCE + // "opportunities". Unfortunately we cannot just leave this to the + // method handle invocation as this might generate a WMTE. + for (int32_t i = 0; i < call_site_type->GetNumberOfPTypes(); ++i) { + ObjPtr from = call_site_type->GetPTypes()->Get(i); + ObjPtr to = bsm->GetMethodType()->GetPTypes()->Get(i); + if (!IsParameterTypeConvertible(from, to)) { + ThrowClassCastException(from, to); return nullptr; } - bootstrap_frame->SetVRegReference(vreg++, name.Ptr()); } - it.Next(); + if (!IsReturnTypeConvertible(call_site_type->GetRType(), bsm->GetMethodType()->GetRType())) { + ThrowClassCastException(bsm->GetMethodType()->GetRType(), call_site_type->GetRType()); + return nullptr; + } + + // Set-up a shadow frame for invoking the bootstrap method handle. + ShadowFrameAllocaUniquePtr bootstrap_frame = + CREATE_SHADOW_FRAME(call_site_type->NumberOfVRegs(), + nullptr, + referrer, + shadow_frame.GetDexPC()); + ScopedStackedShadowFramePusher pusher( + self, bootstrap_frame.get(), StackedShadowFrameType::kShadowFrameUnderConstruction); + ShadowFrameSetter setter(bootstrap_frame.get(), 0u); - // The third parameter is the method type associated with the name. - uint32_t method_type_idx = static_cast(it.GetJavaValue().i); - Handle method_type(hs.NewHandle( - class_linker->ResolveMethodType(self, method_type_idx, dex_cache, class_loader))); - if (method_type.IsNull()) { + // The first parameter is a MethodHandles lookup instance. + Handle lookup_class = + hs.NewHandle(shadow_frame.GetMethod()->GetDeclaringClass()); + ObjPtr lookup = + mirror::MethodHandlesLookup::Create(self, lookup_class); + if (lookup.IsNull()) { DCHECK(self->IsExceptionPending()); return nullptr; } - bootstrap_frame->SetVRegReference(vreg++, method_type.Get()); - it.Next(); - - // Append remaining arguments (if any). - while (it.HasNext()) { - const jvalue& jvalue = it.GetJavaValue(); - switch (it.GetValueType()) { - case EncodedArrayValueIterator::ValueType::kBoolean: - case EncodedArrayValueIterator::ValueType::kByte: - case EncodedArrayValueIterator::ValueType::kChar: - case EncodedArrayValueIterator::ValueType::kShort: - case EncodedArrayValueIterator::ValueType::kInt: - bootstrap_frame->SetVReg(vreg, jvalue.i); - vreg += 1; - break; - case EncodedArrayValueIterator::ValueType::kLong: - bootstrap_frame->SetVRegLong(vreg, jvalue.j); - vreg += 2; - break; - case EncodedArrayValueIterator::ValueType::kFloat: - bootstrap_frame->SetVRegFloat(vreg, jvalue.f); - vreg += 1; - break; - case EncodedArrayValueIterator::ValueType::kDouble: - bootstrap_frame->SetVRegDouble(vreg, jvalue.d); - vreg += 2; - break; - case EncodedArrayValueIterator::ValueType::kMethodType: { - uint32_t idx = static_cast(jvalue.i); - ObjPtr ref = - class_linker->ResolveMethodType(self, idx, dex_cache, class_loader); - if (ref.IsNull()) { - DCHECK(self->IsExceptionPending()); - return nullptr; - } - bootstrap_frame->SetVRegReference(vreg, ref.Ptr()); - vreg += 1; - break; - } - case EncodedArrayValueIterator::ValueType::kMethodHandle: { - uint32_t idx = static_cast(jvalue.i); - ObjPtr ref = - class_linker->ResolveMethodHandle(self, idx, referrer); - if (ref.IsNull()) { - DCHECK(self->IsExceptionPending()); - return nullptr; - } - bootstrap_frame->SetVRegReference(vreg, ref.Ptr()); - vreg += 1; - break; - } - case EncodedArrayValueIterator::ValueType::kString: { - dex::StringIndex idx(static_cast(jvalue.i)); - ObjPtr ref = class_linker->ResolveString(idx, dex_cache); - if (ref.IsNull()) { - DCHECK(self->IsExceptionPending()); - return nullptr; - } - bootstrap_frame->SetVRegReference(vreg, ref.Ptr()); - vreg += 1; - break; - } - case EncodedArrayValueIterator::ValueType::kType: { - dex::TypeIndex idx(static_cast(jvalue.i)); - ObjPtr ref = class_linker->ResolveType(idx, dex_cache, class_loader); - if (ref.IsNull()) { - DCHECK(self->IsExceptionPending()); - return nullptr; - } - bootstrap_frame->SetVRegReference(vreg, ref.Ptr()); - vreg += 1; - break; + setter.SetReference(lookup); + + // Pack the remaining arguments into the frame. + int number_of_arguments = call_site_type->GetNumberOfPTypes(); + int argument_index; + for (argument_index = 1; argument_index < number_of_arguments; ++argument_index) { + if (argument_index == number_of_arguments - 1 && + call_site_type->GetPTypes()->Get(argument_index)->IsArrayClass()) { + ObjPtr array_type = call_site_type->GetPTypes()->Get(argument_index); + if (!PackCollectorArrayForBootstrapMethod(self, + referrer, + array_type, + collector_arguments_length, + &it, + &setter)) { + DCHECK(self->IsExceptionPending()); + return nullptr; } - case EncodedArrayValueIterator::ValueType::kNull: - bootstrap_frame->SetVRegReference(vreg, nullptr); - vreg += 1; - break; - case EncodedArrayValueIterator::ValueType::kField: - case EncodedArrayValueIterator::ValueType::kMethod: - case EncodedArrayValueIterator::ValueType::kEnum: - case EncodedArrayValueIterator::ValueType::kArray: - case EncodedArrayValueIterator::ValueType::kAnnotation: - // Unreachable based on current EncodedArrayValueIterator::Next(). - UNREACHABLE(); + } else if (!PackArgumentForBootstrapMethod(self, referrer, &it, &setter)) { + DCHECK(self->IsExceptionPending()); + return nullptr; } - it.Next(); } + DCHECK(!it.HasNext()); + DCHECK(setter.Done()); // Invoke the bootstrap method handle. JValue result; - RangeInstructionOperands operands(0, vreg); - bool invoke_success = MethodHandleInvokeExact(self, - *bootstrap_frame, - bootstrap, - bootstrap_method_type, - &operands, - &result); + RangeInstructionOperands operands(0, bootstrap_frame->NumberOfVRegs()); + bool invoke_success = MethodHandleInvoke(self, + *bootstrap_frame, + bsm, + call_site_type, + &operands, + &result); if (!invoke_success) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -1077,92 +1350,105 @@ static ObjPtr InvokeBootstrapMethod(Thread* self, return nullptr; } - // Check the result type is a subclass of CallSite. - if (UNLIKELY(!object->InstanceOf(mirror::CallSite::StaticClass()))) { - ThrowClassCastException(object->GetClass(), mirror::CallSite::StaticClass()); + // Check the result type is a subclass of j.l.i.CallSite. + ObjPtr call_site_class = GetClassRoot(class_linker); + if (UNLIKELY(!object->InstanceOf(call_site_class))) { + ThrowClassCastException(object->GetClass(), call_site_class); return nullptr; } - Handle call_site = - hs.NewHandle(ObjPtr::DownCast(ObjPtr(result.GetL()))); // Check the call site target is not null as we're going to invoke it. - Handle target = hs.NewHandle(call_site->GetTarget()); - if (UNLIKELY(target.IsNull())) { - ThrowClassCastException("Bootstrap method did not return a callsite"); + ObjPtr call_site = + ObjPtr::DownCast(ObjPtr(result.GetL())); + ObjPtr target = call_site->GetTarget(); + if (UNLIKELY(target == nullptr)) { + ThrowClassCastException("Bootstrap method returned a CallSite with a null target"); return nullptr; } + return call_site; +} - // Check the target method type matches the method type requested modulo the receiver - // needs to be compatible rather than exact. - Handle target_method_type = hs.NewHandle(target->GetMethodType()); - if (UNLIKELY(!target_method_type->IsExactMatch(method_type.Get()) && - !IsParameterTypeConvertible(target_method_type->GetPTypes()->GetWithoutChecks(0), - method_type->GetPTypes()->GetWithoutChecks(0)))) { - ThrowWrongMethodTypeException(target_method_type.Get(), method_type.Get()); +namespace { + +ObjPtr DoResolveCallSite(Thread* self, + ShadowFrame& shadow_frame, + uint32_t call_site_idx) + REQUIRES_SHARED(Locks::mutator_lock_) { + StackHandleScope<1> hs(self); + Handle dex_cache(hs.NewHandle(shadow_frame.GetMethod()->GetDexCache())); + + // Get the call site from the DexCache if present. + ObjPtr 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; } - return call_site.Get(); + // Attempt to place the candidate call site into the DexCache, return the winning call site. + return dex_cache->SetResolvedCallSite(call_site_idx, call_site); } -template +} // 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 dex_cache(hs.NewHandle(shadow_frame.GetMethod()->GetDexCache())); - const uint32_t call_site_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); - MutableHandle - call_site(hs.NewHandle(dex_cache->GetResolvedCallSite(call_site_idx))); + + ObjPtr 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()); - 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; } - // CallSite.java checks the re-assignment of the call site target - // when mutating call site targets. We only check the target is - // non-null and has the right type during bootstrap method execution. + StackHandleScope<2> hs(self); Handle target = hs.NewHandle(call_site->GetTarget()); Handle target_method_type = hs.NewHandle(target->GetMethodType()); - DCHECK_EQ(static_cast(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); + 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. +static inline void AssignRegister(ShadowFrame* new_shadow_frame, const ShadowFrame& shadow_frame, + size_t dest_reg, size_t src_reg) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Uint required, so that sign extension does not make this wrong on 64b systems + uint32_t src_value = shadow_frame.GetVReg(src_reg); + ObjPtr o = shadow_frame.GetVRegReference(src_reg); + + // If both register locations contains the same value, the register probably holds a reference. + // Note: As an optimization, non-moving collectors leave a stale reference value + // in the references array even after the original vreg was overwritten to a non-reference. + if (src_value == reinterpret_cast(o.Ptr())) { + new_shadow_frame->SetVRegReference(dest_reg, o); } 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); + new_shadow_frame->SetVReg(dest_reg, src_value); } } @@ -1350,7 +1636,7 @@ static inline bool DoCallCommon(ArtMethod* called_method, return false; } } - new_shadow_frame->SetVRegReference(dest_reg, o.Ptr()); + new_shadow_frame->SetVRegReference(dest_reg, o); break; } // Handle doubles and longs. 2 consecutive virtual register slots. @@ -1566,16 +1852,6 @@ EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false); 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 0818e066759e642fd8173d9f3d56e10d3e365053..b324b4c99d00bb97feb059c8edceb1478fd5654d 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -35,6 +35,7 @@ #include "base/macros.h" #include "base/mutex.h" #include "class_linker-inl.h" +#include "class_root.h" #include "common_dex_operations.h" #include "common_throws.h" #include "dex/dex_file-inl.h" @@ -217,7 +218,7 @@ static inline ObjPtr ResolveMethodHandle(Thread* self, } static inline ObjPtr ResolveMethodType(Thread* self, - uint32_t method_type_index, + dex::ProtoIndex method_type_index, ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); @@ -241,7 +242,15 @@ bool DoInvokePolymorphic(Thread* self, 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 @@ -249,7 +258,19 @@ bool DoInvokeCustom(Thread* self, 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. @@ -328,7 +349,7 @@ static inline ObjPtr ResolveString(Thread* self, ShadowFrame& shadow_frame, dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr java_lang_string_class = mirror::String::GetJavaLangString(); + ObjPtr java_lang_string_class = GetClassRoot(); if (UNLIKELY(!java_lang_string_class->IsInitialized())) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); StackHandleScope<1> hs(self); @@ -339,12 +360,8 @@ static inline ObjPtr ResolveString(Thread* self, } } ArtMethod* method = shadow_frame.GetMethod(); - ObjPtr string_ptr = method->GetDexCache()->GetResolvedString(string_idx); - if (UNLIKELY(string_ptr == nullptr)) { - StackHandleScope<1> hs(self); - Handle dex_cache(hs.NewHandle(method->GetDexCache())); - string_ptr = Runtime::Current()->GetClassLinker()->ResolveString(string_idx, dex_cache); - } + ObjPtr string_ptr = + Runtime::Current()->GetClassLinker()->ResolveString(string_idx, method); return string_ptr; } @@ -543,24 +560,6 @@ static inline bool IsBackwardBranch(int32_t branch_offset) { return branch_offset <= 0; } -// Assign register 'src_reg' from shadow_frame to register 'dest_reg' into new_shadow_frame. -static inline void AssignRegister(ShadowFrame* new_shadow_frame, const ShadowFrame& shadow_frame, - size_t dest_reg, size_t src_reg) - REQUIRES_SHARED(Locks::mutator_lock_) { - // Uint required, so that sign extension does not make this wrong on 64b systems - uint32_t src_value = shadow_frame.GetVReg(src_reg); - ObjPtr o = shadow_frame.GetVRegReference(src_reg); - - // If both register locations contains the same value, the register probably holds a reference. - // Note: As an optimization, non-moving collectors leave a stale reference value - // in the references array even after the original vreg was overwritten to a non-reference. - if (src_value == reinterpret_cast(o.Ptr())) { - new_shadow_frame->SetVRegReference(dest_reg, o.Ptr()); - } else { - new_shadow_frame->SetVReg(dest_reg, src_value); - } -} - // The arg_offset is the offset to the first input register in the frame. void ArtInterpreterToCompiledCodeBridge(Thread* self, ArtMethod* caller, diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc index f0cf2af7c75992c749f05699621049cdefc4fc19..69dae31b37c1b2222504bc2b8ba22e5d2e612df3 100644 --- a/runtime/interpreter/interpreter_intrinsics.cc +++ b/runtime/interpreter/interpreter_intrinsics.cc @@ -91,7 +91,7 @@ BINARY_II_INTRINSIC(MterpIntegerRotateLeft, (Rot), SetI); // java.lang.Integer.signum(I)I UNARY_INTRINSIC(MterpIntegerSignum, Signum, GetVReg, SetI); -// java.lang.Long.reverse(I)I +// java.lang.Long.reverse(J)J UNARY_INTRINSIC(MterpLongReverse, ReverseBits64, GetVRegLong, SetJ); // java.lang.Long.reverseBytes(J)J diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 283885e5222e0533c21779c5e48636d57d53da18..27626295c1c8838327b26b5ec710a812ea9394a1 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -24,6 +24,7 @@ #include "jit/jit.h" #include "jvalue-inl.h" #include "safe_math.h" +#include "shadow_frame-inl.h" namespace art { namespace interpreter { @@ -299,7 +300,7 @@ void ExecuteSwitchImplCpp(SwitchImplContext* ctx) { PREAMBLE(); ObjPtr exception = self->GetException(); DCHECK(exception != nullptr) << "No pending exception on MOVE_EXCEPTION instruction"; - shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception.Ptr()); + shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception); self->ClearException(); inst = inst->Next_1xx(); break; @@ -514,7 +515,7 @@ void ExecuteSwitchImplCpp(SwitchImplContext* ctx) { if (UNLIKELY(s == nullptr)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), s.Ptr()); + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), s); inst = inst->Next_2xx(); } break; @@ -527,7 +528,7 @@ void ExecuteSwitchImplCpp(SwitchImplContext* ctx) { if (UNLIKELY(s == nullptr)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_31c(inst_data), s.Ptr()); + shadow_frame.SetVRegReference(inst->VRegA_31c(inst_data), s); inst = inst->Next_3xx(); } break; @@ -542,7 +543,7 @@ void ExecuteSwitchImplCpp(SwitchImplContext* ctx) { if (UNLIKELY(c == nullptr)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), c.Ptr()); + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), c); inst = inst->Next_2xx(); } break; @@ -556,7 +557,7 @@ void ExecuteSwitchImplCpp(SwitchImplContext* ctx) { if (UNLIKELY(mh == nullptr)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mh.Ptr()); + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mh); inst = inst->Next_2xx(); } break; @@ -565,12 +566,12 @@ void ExecuteSwitchImplCpp(SwitchImplContext* ctx) { PREAMBLE(); ClassLinker* cl = Runtime::Current()->GetClassLinker(); ObjPtr mt = cl->ResolveMethodType(self, - inst->VRegB_21c(), + dex::ProtoIndex(inst->VRegB_21c()), shadow_frame.GetMethod()); if (UNLIKELY(mt == nullptr)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mt.Ptr()); + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mt); inst = inst->Next_2xx(); } break; @@ -681,7 +682,7 @@ void ExecuteSwitchImplCpp(SwitchImplContext* ctx) { HANDLE_PENDING_EXCEPTION(); break; } - shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), obj.Ptr()); + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), obj); inst = inst->Next_2xx(); } break; @@ -698,7 +699,7 @@ void ExecuteSwitchImplCpp(SwitchImplContext* ctx) { if (UNLIKELY(obj == nullptr)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_22c(inst_data), obj.Ptr()); + shadow_frame.SetVRegReference(inst->VRegA_22c(inst_data), obj); inst = inst->Next_2xx(); } break; diff --git a/runtime/interpreter/mterp/arm/alt_stub.S b/runtime/interpreter/mterp/arm/alt_stub.S index 9db5bf746202874d88efab0f18b29ef9133be418..8799d9520c60c1c51c22b3f3db39acee190061f0 100644 --- a/runtime/interpreter/mterp/arm/alt_stub.S +++ b/runtime/interpreter/mterp/arm/alt_stub.S @@ -5,7 +5,8 @@ */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (${opnum} * 128) @ Addr of primary handler. + adr lr, .L_ALT_${opcode} + sub lr, lr, #(.L_ALT_${opcode} - .L_${opcode}) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC diff --git a/runtime/interpreter/mterp/arm/footer.S b/runtime/interpreter/mterp/arm/footer.S index f3a3ad25fc3bbf041bd62d3bdd0e60bad4b707e5..8e9c3c22fe913adf501d6fbe5d9d7704dfa9b4c1 100644 --- a/runtime/interpreter/mterp/arm/footer.S +++ b/runtime/interpreter/mterp/arm/footer.S @@ -148,7 +148,7 @@ MterpCommonTakenBranch: #endif cmp rPROFILE, #JIT_CHECK_OSR beq .L_osr_check - subgts rPROFILE, #1 + subsgt rPROFILE, #1 beq .L_add_batch @ counted down to zero - report .L_resume_backward_branch: ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] diff --git a/runtime/interpreter/mterp/arm/instruction_end.S b/runtime/interpreter/mterp/arm/instruction_end.S new file mode 100644 index 0000000000000000000000000000000000000000..32c725c7d9655633fcd95e67023080ff36bdc515 --- /dev/null +++ b/runtime/interpreter/mterp/arm/instruction_end.S @@ -0,0 +1,3 @@ + + .global artMterpAsmInstructionEnd +artMterpAsmInstructionEnd: diff --git a/runtime/interpreter/mterp/arm/instruction_end_alt.S b/runtime/interpreter/mterp/arm/instruction_end_alt.S new file mode 100644 index 0000000000000000000000000000000000000000..f90916fc02cda411d0a0c9c40c77bb3c6848793c --- /dev/null +++ b/runtime/interpreter/mterp/arm/instruction_end_alt.S @@ -0,0 +1,3 @@ + + .global artMterpAsmAltInstructionEnd +artMterpAsmAltInstructionEnd: diff --git a/runtime/interpreter/mterp/arm/instruction_end_sister.S b/runtime/interpreter/mterp/arm/instruction_end_sister.S new file mode 100644 index 0000000000000000000000000000000000000000..c5f4886697bb135e2a6a8bd74cf2fb7df2e5fab0 --- /dev/null +++ b/runtime/interpreter/mterp/arm/instruction_end_sister.S @@ -0,0 +1,3 @@ + + .global artMterpAsmSisterEnd +artMterpAsmSisterEnd: diff --git a/runtime/interpreter/mterp/arm/instruction_start.S b/runtime/interpreter/mterp/arm/instruction_start.S new file mode 100644 index 0000000000000000000000000000000000000000..8874c205404d9f5012a99d67dfd2ef472160d109 --- /dev/null +++ b/runtime/interpreter/mterp/arm/instruction_start.S @@ -0,0 +1,4 @@ + + .global artMterpAsmInstructionStart +artMterpAsmInstructionStart = .L_op_nop + .text diff --git a/runtime/interpreter/mterp/arm/instruction_start_alt.S b/runtime/interpreter/mterp/arm/instruction_start_alt.S new file mode 100644 index 0000000000000000000000000000000000000000..0c9ffdb7d6c01b8470682ee5715d27a475517e5b --- /dev/null +++ b/runtime/interpreter/mterp/arm/instruction_start_alt.S @@ -0,0 +1,4 @@ + + .global artMterpAsmAltInstructionStart +artMterpAsmAltInstructionStart = .L_ALT_op_nop + .text diff --git a/runtime/interpreter/mterp/arm/instruction_start_sister.S b/runtime/interpreter/mterp/arm/instruction_start_sister.S new file mode 100644 index 0000000000000000000000000000000000000000..2ec51f7261de40fd2ad0d3ba9ed6826bf532d43e --- /dev/null +++ b/runtime/interpreter/mterp/arm/instruction_start_sister.S @@ -0,0 +1,5 @@ + + .global artMterpAsmSisterStart + .text + .balign 4 +artMterpAsmSisterStart: diff --git a/runtime/interpreter/mterp/arm/op_mul_long.S b/runtime/interpreter/mterp/arm/op_mul_long.S index a13c803301e8b8ec0ce77ae25ef786367e95f9c3..4f55280871a4a8b65ec267b4985efe1c75c8c789 100644 --- a/runtime/interpreter/mterp/arm/op_mul_long.S +++ b/runtime/interpreter/mterp/arm/op_mul_long.S @@ -29,6 +29,7 @@ mla r2, r0, r3, ip @ r2<- YxX + (ZxW) mov r0, rINST, lsr #8 @ r0<- AA add r2, r2, lr @ r2<- lr + low(ZxW + (YxX)) + CLEAR_SHADOW_PAIR r0, lr, ip @ Zero out the shadow regs VREG_INDEX_TO_ADDR r0, r0 @ r0<- &fp[AA] FETCH_ADVANCE_INST 2 @ advance rPC, load rINST GET_INST_OPCODE ip @ extract opcode from rINST diff --git a/runtime/interpreter/mterp/arm64/header.S b/runtime/interpreter/mterp/arm64/header.S index 7017dd149c7a97abe93ebebc35ff097a50ff33e3..072280426560a32c22ca415b4f237f49c23a7332 100644 --- a/runtime/interpreter/mterp/arm64/header.S +++ b/runtime/interpreter/mterp/arm64/header.S @@ -339,6 +339,7 @@ codes. */ .macro ENTRY name .type \name, #function + .hidden \name // Hide this as a global symbol, so we do not incur plt calls. .global \name /* Cache alignment for function entry */ .balign 16 diff --git a/runtime/interpreter/mterp/arm64/instruction_end.S b/runtime/interpreter/mterp/arm64/instruction_end.S new file mode 100644 index 0000000000000000000000000000000000000000..32c725c7d9655633fcd95e67023080ff36bdc515 --- /dev/null +++ b/runtime/interpreter/mterp/arm64/instruction_end.S @@ -0,0 +1,3 @@ + + .global artMterpAsmInstructionEnd +artMterpAsmInstructionEnd: diff --git a/runtime/interpreter/mterp/arm64/instruction_end_alt.S b/runtime/interpreter/mterp/arm64/instruction_end_alt.S new file mode 100644 index 0000000000000000000000000000000000000000..f90916fc02cda411d0a0c9c40c77bb3c6848793c --- /dev/null +++ b/runtime/interpreter/mterp/arm64/instruction_end_alt.S @@ -0,0 +1,3 @@ + + .global artMterpAsmAltInstructionEnd +artMterpAsmAltInstructionEnd: diff --git a/runtime/interpreter/mterp/arm64/instruction_end_sister.S b/runtime/interpreter/mterp/arm64/instruction_end_sister.S new file mode 100644 index 0000000000000000000000000000000000000000..c5f4886697bb135e2a6a8bd74cf2fb7df2e5fab0 --- /dev/null +++ b/runtime/interpreter/mterp/arm64/instruction_end_sister.S @@ -0,0 +1,3 @@ + + .global artMterpAsmSisterEnd +artMterpAsmSisterEnd: diff --git a/runtime/interpreter/mterp/arm64/instruction_start.S b/runtime/interpreter/mterp/arm64/instruction_start.S new file mode 100644 index 0000000000000000000000000000000000000000..8874c205404d9f5012a99d67dfd2ef472160d109 --- /dev/null +++ b/runtime/interpreter/mterp/arm64/instruction_start.S @@ -0,0 +1,4 @@ + + .global artMterpAsmInstructionStart +artMterpAsmInstructionStart = .L_op_nop + .text diff --git a/runtime/interpreter/mterp/arm64/instruction_start_alt.S b/runtime/interpreter/mterp/arm64/instruction_start_alt.S new file mode 100644 index 0000000000000000000000000000000000000000..0c9ffdb7d6c01b8470682ee5715d27a475517e5b --- /dev/null +++ b/runtime/interpreter/mterp/arm64/instruction_start_alt.S @@ -0,0 +1,4 @@ + + .global artMterpAsmAltInstructionStart +artMterpAsmAltInstructionStart = .L_ALT_op_nop + .text diff --git a/runtime/interpreter/mterp/arm64/instruction_start_sister.S b/runtime/interpreter/mterp/arm64/instruction_start_sister.S new file mode 100644 index 0000000000000000000000000000000000000000..2ec51f7261de40fd2ad0d3ba9ed6826bf532d43e --- /dev/null +++ b/runtime/interpreter/mterp/arm64/instruction_start_sister.S @@ -0,0 +1,5 @@ + + .global artMterpAsmSisterStart + .text + .balign 4 +artMterpAsmSisterStart: diff --git a/runtime/interpreter/mterp/gen_mterp.py b/runtime/interpreter/mterp/gen_mterp.py index 64114d747acab06f04b2eb5f355009154834c040..75c5174bcbc487cc29639c0b7f9620d4b5e71130 100755 --- a/runtime/interpreter/mterp/gen_mterp.py +++ b/runtime/interpreter/mterp/gen_mterp.py @@ -279,13 +279,8 @@ def loadAndEmitOpcodes(): sister_list = [] assert len(opcodes) == kNumPackedOpcodes need_dummy_start = False - start_label = global_name_format % "artMterpAsmInstructionStart" - end_label = global_name_format % "artMterpAsmInstructionEnd" - # point MterpAsmInstructionStart at the first handler or stub - asm_fp.write("\n .global %s\n" % start_label) - asm_fp.write("%s = " % start_label + label_prefix + "_op_nop\n") - asm_fp.write(" .text\n\n") + loadAndEmitGenericAsm("instruction_start") for i in xrange(kNumPackedOpcodes): op = opcodes[i] @@ -309,20 +304,14 @@ def loadAndEmitOpcodes(): asm_fp.write(label_prefix + "_op_nop: /* dummy */\n"); emitAlign() - asm_fp.write(" .global %s\n" % end_label) - asm_fp.write("%s:\n" % end_label) + + loadAndEmitGenericAsm("instruction_end") if style == "computed-goto": - start_sister_label = global_name_format % "artMterpAsmSisterStart" - end_sister_label = global_name_format % "artMterpAsmSisterEnd" emitSectionComment("Sister implementations", asm_fp) - asm_fp.write(" .global %s\n" % start_sister_label) - asm_fp.write(" .text\n") - asm_fp.write(" .balign 4\n") - asm_fp.write("%s:\n" % start_sister_label) + loadAndEmitGenericAsm("instruction_start_sister") asm_fp.writelines(sister_list) - asm_fp.write(" .global %s\n" % end_sister_label) - asm_fp.write("%s:\n\n" % end_sister_label) + loadAndEmitGenericAsm("instruction_end_sister") # # Load an alternate entry stub @@ -345,10 +334,7 @@ def loadAndEmitAltOpcodes(): start_label = global_name_format % "artMterpAsmAltInstructionStart" end_label = global_name_format % "artMterpAsmAltInstructionEnd" - # point MterpAsmInstructionStart at the first handler or stub - asm_fp.write("\n .global %s\n" % start_label) - asm_fp.write(" .text\n\n") - asm_fp.write("%s = " % start_label + label_prefix + "_ALT_op_nop\n") + loadAndEmitGenericAsm("instruction_start_alt") for i in xrange(kNumPackedOpcodes): op = opcodes[i] @@ -359,8 +345,8 @@ def loadAndEmitAltOpcodes(): loadAndEmitAltStub(source, i) emitAlign() - asm_fp.write(" .global %s\n" % end_label) - asm_fp.write("%s:\n" % end_label) + + loadAndEmitGenericAsm("instruction_end_alt") # # Load an assembly fragment and emit it. @@ -376,6 +362,14 @@ def loadAndEmitAsm(location, opindex, sister_list): emitAsmHeader(asm_fp, dict, label_prefix) appendSourceFile(source, dict, asm_fp, sister_list) +# +# Load a non-handler assembly fragment and emit it. +# +def loadAndEmitGenericAsm(name): + source = "%s/%s.S" % (default_op_dir, name) + dict = getGlobalSubDict() + appendSourceFile(source, dict, asm_fp, None) + # # Emit fallback fragment # diff --git a/runtime/interpreter/mterp/mips/instruction_end.S b/runtime/interpreter/mterp/mips/instruction_end.S new file mode 100644 index 0000000000000000000000000000000000000000..32c725c7d9655633fcd95e67023080ff36bdc515 --- /dev/null +++ b/runtime/interpreter/mterp/mips/instruction_end.S @@ -0,0 +1,3 @@ + + .global artMterpAsmInstructionEnd +artMterpAsmInstructionEnd: diff --git a/runtime/interpreter/mterp/mips/instruction_end_alt.S b/runtime/interpreter/mterp/mips/instruction_end_alt.S new file mode 100644 index 0000000000000000000000000000000000000000..f90916fc02cda411d0a0c9c40c77bb3c6848793c --- /dev/null +++ b/runtime/interpreter/mterp/mips/instruction_end_alt.S @@ -0,0 +1,3 @@ + + .global artMterpAsmAltInstructionEnd +artMterpAsmAltInstructionEnd: diff --git a/runtime/interpreter/mterp/mips/instruction_end_sister.S b/runtime/interpreter/mterp/mips/instruction_end_sister.S new file mode 100644 index 0000000000000000000000000000000000000000..c5f4886697bb135e2a6a8bd74cf2fb7df2e5fab0 --- /dev/null +++ b/runtime/interpreter/mterp/mips/instruction_end_sister.S @@ -0,0 +1,3 @@ + + .global artMterpAsmSisterEnd +artMterpAsmSisterEnd: diff --git a/runtime/interpreter/mterp/mips/instruction_start.S b/runtime/interpreter/mterp/mips/instruction_start.S new file mode 100644 index 0000000000000000000000000000000000000000..8874c205404d9f5012a99d67dfd2ef472160d109 --- /dev/null +++ b/runtime/interpreter/mterp/mips/instruction_start.S @@ -0,0 +1,4 @@ + + .global artMterpAsmInstructionStart +artMterpAsmInstructionStart = .L_op_nop + .text diff --git a/runtime/interpreter/mterp/mips/instruction_start_alt.S b/runtime/interpreter/mterp/mips/instruction_start_alt.S new file mode 100644 index 0000000000000000000000000000000000000000..0c9ffdb7d6c01b8470682ee5715d27a475517e5b --- /dev/null +++ b/runtime/interpreter/mterp/mips/instruction_start_alt.S @@ -0,0 +1,4 @@ + + .global artMterpAsmAltInstructionStart +artMterpAsmAltInstructionStart = .L_ALT_op_nop + .text diff --git a/runtime/interpreter/mterp/mips/instruction_start_sister.S b/runtime/interpreter/mterp/mips/instruction_start_sister.S new file mode 100644 index 0000000000000000000000000000000000000000..2ec51f7261de40fd2ad0d3ba9ed6826bf532d43e --- /dev/null +++ b/runtime/interpreter/mterp/mips/instruction_start_sister.S @@ -0,0 +1,5 @@ + + .global artMterpAsmSisterStart + .text + .balign 4 +artMterpAsmSisterStart: diff --git a/runtime/interpreter/mterp/mips64/instruction_end.S b/runtime/interpreter/mterp/mips64/instruction_end.S new file mode 100644 index 0000000000000000000000000000000000000000..32c725c7d9655633fcd95e67023080ff36bdc515 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/instruction_end.S @@ -0,0 +1,3 @@ + + .global artMterpAsmInstructionEnd +artMterpAsmInstructionEnd: diff --git a/runtime/interpreter/mterp/mips64/instruction_end_alt.S b/runtime/interpreter/mterp/mips64/instruction_end_alt.S new file mode 100644 index 0000000000000000000000000000000000000000..f90916fc02cda411d0a0c9c40c77bb3c6848793c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/instruction_end_alt.S @@ -0,0 +1,3 @@ + + .global artMterpAsmAltInstructionEnd +artMterpAsmAltInstructionEnd: diff --git a/runtime/interpreter/mterp/mips64/instruction_end_sister.S b/runtime/interpreter/mterp/mips64/instruction_end_sister.S new file mode 100644 index 0000000000000000000000000000000000000000..c5f4886697bb135e2a6a8bd74cf2fb7df2e5fab0 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/instruction_end_sister.S @@ -0,0 +1,3 @@ + + .global artMterpAsmSisterEnd +artMterpAsmSisterEnd: diff --git a/runtime/interpreter/mterp/mips64/instruction_start.S b/runtime/interpreter/mterp/mips64/instruction_start.S new file mode 100644 index 0000000000000000000000000000000000000000..8874c205404d9f5012a99d67dfd2ef472160d109 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/instruction_start.S @@ -0,0 +1,4 @@ + + .global artMterpAsmInstructionStart +artMterpAsmInstructionStart = .L_op_nop + .text diff --git a/runtime/interpreter/mterp/mips64/instruction_start_alt.S b/runtime/interpreter/mterp/mips64/instruction_start_alt.S new file mode 100644 index 0000000000000000000000000000000000000000..0c9ffdb7d6c01b8470682ee5715d27a475517e5b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/instruction_start_alt.S @@ -0,0 +1,4 @@ + + .global artMterpAsmAltInstructionStart +artMterpAsmAltInstructionStart = .L_ALT_op_nop + .text diff --git a/runtime/interpreter/mterp/mips64/instruction_start_sister.S b/runtime/interpreter/mterp/mips64/instruction_start_sister.S new file mode 100644 index 0000000000000000000000000000000000000000..2ec51f7261de40fd2ad0d3ba9ed6826bf532d43e --- /dev/null +++ b/runtime/interpreter/mterp/mips64/instruction_start_sister.S @@ -0,0 +1,5 @@ + + .global artMterpAsmSisterStart + .text + .balign 4 +artMterpAsmSisterStart: diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 2a9ef2ce98ee21cb8976a99120535a84e0b30e86..e4cc6d3f9f5e9da3e20b9e22d55690570d989ba7 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -24,6 +24,7 @@ #include "entrypoints/entrypoint_utils-inl.h" #include "interpreter/interpreter_common.h" #include "interpreter/interpreter_intrinsics.h" +#include "interpreter/shadow_frame-inl.h" namespace art { namespace interpreter { @@ -369,7 +370,7 @@ extern "C" size_t MterpConstString(uint32_t index, if (UNLIKELY(s == nullptr)) { return true; } - shadow_frame->SetVRegReference(tgt_vreg, s.Ptr()); + shadow_frame->SetVRegReference(tgt_vreg, s); return false; } @@ -386,7 +387,7 @@ extern "C" size_t MterpConstClass(uint32_t index, if (UNLIKELY(c == nullptr)) { return true; } - shadow_frame->SetVRegReference(tgt_vreg, c.Ptr()); + shadow_frame->SetVRegReference(tgt_vreg, c); return false; } @@ -399,7 +400,7 @@ extern "C" size_t MterpConstMethodHandle(uint32_t index, if (UNLIKELY(mh == nullptr)) { return true; } - shadow_frame->SetVRegReference(tgt_vreg, mh.Ptr()); + shadow_frame->SetVRegReference(tgt_vreg, mh); return false; } @@ -408,11 +409,12 @@ extern "C" size_t MterpConstMethodType(uint32_t index, ShadowFrame* shadow_frame, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr mt = ResolveMethodType(self, index, shadow_frame->GetMethod()); + ObjPtr mt = + ResolveMethodType(self, dex::ProtoIndex(index), shadow_frame->GetMethod()); if (UNLIKELY(mt == nullptr)) { return true; } - shadow_frame->SetVRegReference(tgt_vreg, mt.Ptr()); + shadow_frame->SetVRegReference(tgt_vreg, mt); return false; } @@ -557,7 +559,7 @@ extern "C" size_t MterpNewArray(ShadowFrame* shadow_frame, REQUIRES_SHARED(Locks::mutator_lock_) { const Instruction* inst = Instruction::At(dex_pc_ptr); int32_t length = shadow_frame->GetVReg(inst->VRegB_22c(inst_data)); - mirror::Object* obj = AllocArrayFromCode( + ObjPtr obj = AllocArrayFromCode( dex::TypeIndex(inst->VRegC_22c()), length, shadow_frame->GetMethod(), self, Runtime::Current()->GetHeap()->GetCurrentAllocator()); if (UNLIKELY(obj == nullptr)) { diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S index 7ea79821b4fb7e863279d37b443f7cd55af4612d..73b957f285d22ec3d8f71fe18b93f2e6a2d5ac81 100644 --- a/runtime/interpreter/mterp/out/mterp_arm.S +++ b/runtime/interpreter/mterp/out/mterp_arm.S @@ -396,6 +396,7 @@ ENTRY ExecuteMterpImpl GOTO_OPCODE ip @ jump to next instruction /* NOTE: no fallthrough */ +/* File: arm/instruction_start.S */ .global artMterpAsmInstructionStart artMterpAsmInstructionStart = .L_op_nop @@ -4474,6 +4475,7 @@ constvalop_long_to_double: mla r2, r0, r3, ip @ r2<- YxX + (ZxW) mov r0, rINST, lsr #8 @ r0<- AA add r2, r2, lr @ r2<- lr + low(ZxW + (YxX)) + CLEAR_SHADOW_PAIR r0, lr, ip @ Zero out the shadow regs VREG_INDEX_TO_ADDR r0, r0 @ r0<- &fp[AA] FETCH_ADVANCE_INST 2 @ advance rPC, load rINST GET_INST_OPCODE ip @ extract opcode from rINST @@ -7509,19 +7511,25 @@ constvalop_long_to_double: .balign 128 +/* File: arm/instruction_end.S */ + .global artMterpAsmInstructionEnd artMterpAsmInstructionEnd: + /* * =========================================================================== * Sister implementations * =========================================================================== */ +/* File: arm/instruction_start_sister.S */ + .global artMterpAsmSisterStart .text .balign 4 artMterpAsmSisterStart: + /* continuation for op_float_to_long */ /* * Convert the float in r0 to a long in r0/r1. @@ -7583,14 +7591,17 @@ d2l_maybeNaN: mov r0, #0 mov r1, #0 bx lr @ return 0 for NaN +/* File: arm/instruction_end_sister.S */ + .global artMterpAsmSisterEnd artMterpAsmSisterEnd: +/* File: arm/instruction_start_alt.S */ .global artMterpAsmAltInstructionStart +artMterpAsmAltInstructionStart = .L_ALT_op_nop .text -artMterpAsmAltInstructionStart = .L_ALT_op_nop /* ------------------------------ */ .balign 128 .L_ALT_op_nop: /* 0x00 */ @@ -7602,7 +7613,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (0 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_nop + sub lr, lr, #(.L_ALT_op_nop - .L_op_nop) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7619,7 +7631,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (1 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_move + sub lr, lr, #(.L_ALT_op_move - .L_op_move) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7636,7 +7649,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (2 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_move_from16 + sub lr, lr, #(.L_ALT_op_move_from16 - .L_op_move_from16) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7653,7 +7667,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (3 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_move_16 + sub lr, lr, #(.L_ALT_op_move_16 - .L_op_move_16) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7670,7 +7685,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (4 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_move_wide + sub lr, lr, #(.L_ALT_op_move_wide - .L_op_move_wide) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7687,7 +7703,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (5 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_move_wide_from16 + sub lr, lr, #(.L_ALT_op_move_wide_from16 - .L_op_move_wide_from16) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7704,7 +7721,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (6 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_move_wide_16 + sub lr, lr, #(.L_ALT_op_move_wide_16 - .L_op_move_wide_16) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7721,7 +7739,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (7 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_move_object + sub lr, lr, #(.L_ALT_op_move_object - .L_op_move_object) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7738,7 +7757,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (8 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_move_object_from16 + sub lr, lr, #(.L_ALT_op_move_object_from16 - .L_op_move_object_from16) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7755,7 +7775,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (9 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_move_object_16 + sub lr, lr, #(.L_ALT_op_move_object_16 - .L_op_move_object_16) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7772,7 +7793,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (10 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_move_result + sub lr, lr, #(.L_ALT_op_move_result - .L_op_move_result) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7789,7 +7811,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (11 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_move_result_wide + sub lr, lr, #(.L_ALT_op_move_result_wide - .L_op_move_result_wide) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7806,7 +7829,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (12 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_move_result_object + sub lr, lr, #(.L_ALT_op_move_result_object - .L_op_move_result_object) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7823,7 +7847,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (13 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_move_exception + sub lr, lr, #(.L_ALT_op_move_exception - .L_op_move_exception) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7840,7 +7865,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (14 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_return_void + sub lr, lr, #(.L_ALT_op_return_void - .L_op_return_void) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7857,7 +7883,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (15 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_return + sub lr, lr, #(.L_ALT_op_return - .L_op_return) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7874,7 +7901,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (16 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_return_wide + sub lr, lr, #(.L_ALT_op_return_wide - .L_op_return_wide) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7891,7 +7919,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (17 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_return_object + sub lr, lr, #(.L_ALT_op_return_object - .L_op_return_object) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7908,7 +7937,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (18 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_const_4 + sub lr, lr, #(.L_ALT_op_const_4 - .L_op_const_4) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7925,7 +7955,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (19 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_const_16 + sub lr, lr, #(.L_ALT_op_const_16 - .L_op_const_16) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7942,7 +7973,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (20 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_const + sub lr, lr, #(.L_ALT_op_const - .L_op_const) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7959,7 +7991,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (21 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_const_high16 + sub lr, lr, #(.L_ALT_op_const_high16 - .L_op_const_high16) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7976,7 +8009,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (22 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_const_wide_16 + sub lr, lr, #(.L_ALT_op_const_wide_16 - .L_op_const_wide_16) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -7993,7 +8027,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (23 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_const_wide_32 + sub lr, lr, #(.L_ALT_op_const_wide_32 - .L_op_const_wide_32) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8010,7 +8045,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (24 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_const_wide + sub lr, lr, #(.L_ALT_op_const_wide - .L_op_const_wide) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8027,7 +8063,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (25 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_const_wide_high16 + sub lr, lr, #(.L_ALT_op_const_wide_high16 - .L_op_const_wide_high16) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8044,7 +8081,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (26 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_const_string + sub lr, lr, #(.L_ALT_op_const_string - .L_op_const_string) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8061,7 +8099,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (27 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_const_string_jumbo + sub lr, lr, #(.L_ALT_op_const_string_jumbo - .L_op_const_string_jumbo) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8078,7 +8117,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (28 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_const_class + sub lr, lr, #(.L_ALT_op_const_class - .L_op_const_class) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8095,7 +8135,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (29 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_monitor_enter + sub lr, lr, #(.L_ALT_op_monitor_enter - .L_op_monitor_enter) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8112,7 +8153,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (30 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_monitor_exit + sub lr, lr, #(.L_ALT_op_monitor_exit - .L_op_monitor_exit) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8129,7 +8171,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (31 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_check_cast + sub lr, lr, #(.L_ALT_op_check_cast - .L_op_check_cast) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8146,7 +8189,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (32 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_instance_of + sub lr, lr, #(.L_ALT_op_instance_of - .L_op_instance_of) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8163,7 +8207,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (33 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_array_length + sub lr, lr, #(.L_ALT_op_array_length - .L_op_array_length) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8180,7 +8225,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (34 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_new_instance + sub lr, lr, #(.L_ALT_op_new_instance - .L_op_new_instance) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8197,7 +8243,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (35 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_new_array + sub lr, lr, #(.L_ALT_op_new_array - .L_op_new_array) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8214,7 +8261,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (36 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_filled_new_array + sub lr, lr, #(.L_ALT_op_filled_new_array - .L_op_filled_new_array) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8231,7 +8279,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (37 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_filled_new_array_range + sub lr, lr, #(.L_ALT_op_filled_new_array_range - .L_op_filled_new_array_range) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8248,7 +8297,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (38 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_fill_array_data + sub lr, lr, #(.L_ALT_op_fill_array_data - .L_op_fill_array_data) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8265,7 +8315,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (39 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_throw + sub lr, lr, #(.L_ALT_op_throw - .L_op_throw) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8282,7 +8333,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (40 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_goto + sub lr, lr, #(.L_ALT_op_goto - .L_op_goto) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8299,7 +8351,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (41 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_goto_16 + sub lr, lr, #(.L_ALT_op_goto_16 - .L_op_goto_16) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8316,7 +8369,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (42 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_goto_32 + sub lr, lr, #(.L_ALT_op_goto_32 - .L_op_goto_32) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8333,7 +8387,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (43 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_packed_switch + sub lr, lr, #(.L_ALT_op_packed_switch - .L_op_packed_switch) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8350,7 +8405,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (44 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sparse_switch + sub lr, lr, #(.L_ALT_op_sparse_switch - .L_op_sparse_switch) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8367,7 +8423,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (45 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_cmpl_float + sub lr, lr, #(.L_ALT_op_cmpl_float - .L_op_cmpl_float) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8384,7 +8441,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (46 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_cmpg_float + sub lr, lr, #(.L_ALT_op_cmpg_float - .L_op_cmpg_float) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8401,7 +8459,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (47 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_cmpl_double + sub lr, lr, #(.L_ALT_op_cmpl_double - .L_op_cmpl_double) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8418,7 +8477,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (48 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_cmpg_double + sub lr, lr, #(.L_ALT_op_cmpg_double - .L_op_cmpg_double) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8435,7 +8495,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (49 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_cmp_long + sub lr, lr, #(.L_ALT_op_cmp_long - .L_op_cmp_long) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8452,7 +8513,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (50 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_if_eq + sub lr, lr, #(.L_ALT_op_if_eq - .L_op_if_eq) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8469,7 +8531,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (51 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_if_ne + sub lr, lr, #(.L_ALT_op_if_ne - .L_op_if_ne) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8486,7 +8549,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (52 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_if_lt + sub lr, lr, #(.L_ALT_op_if_lt - .L_op_if_lt) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8503,7 +8567,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (53 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_if_ge + sub lr, lr, #(.L_ALT_op_if_ge - .L_op_if_ge) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8520,7 +8585,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (54 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_if_gt + sub lr, lr, #(.L_ALT_op_if_gt - .L_op_if_gt) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8537,7 +8603,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (55 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_if_le + sub lr, lr, #(.L_ALT_op_if_le - .L_op_if_le) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8554,7 +8621,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (56 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_if_eqz + sub lr, lr, #(.L_ALT_op_if_eqz - .L_op_if_eqz) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8571,7 +8639,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (57 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_if_nez + sub lr, lr, #(.L_ALT_op_if_nez - .L_op_if_nez) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8588,7 +8657,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (58 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_if_ltz + sub lr, lr, #(.L_ALT_op_if_ltz - .L_op_if_ltz) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8605,7 +8675,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (59 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_if_gez + sub lr, lr, #(.L_ALT_op_if_gez - .L_op_if_gez) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8622,7 +8693,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (60 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_if_gtz + sub lr, lr, #(.L_ALT_op_if_gtz - .L_op_if_gtz) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8639,7 +8711,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (61 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_if_lez + sub lr, lr, #(.L_ALT_op_if_lez - .L_op_if_lez) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8656,7 +8729,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (62 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_unused_3e + sub lr, lr, #(.L_ALT_op_unused_3e - .L_op_unused_3e) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8673,7 +8747,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (63 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_unused_3f + sub lr, lr, #(.L_ALT_op_unused_3f - .L_op_unused_3f) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8690,7 +8765,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (64 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_unused_40 + sub lr, lr, #(.L_ALT_op_unused_40 - .L_op_unused_40) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8707,7 +8783,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (65 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_unused_41 + sub lr, lr, #(.L_ALT_op_unused_41 - .L_op_unused_41) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8724,7 +8801,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (66 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_unused_42 + sub lr, lr, #(.L_ALT_op_unused_42 - .L_op_unused_42) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8741,7 +8819,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (67 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_unused_43 + sub lr, lr, #(.L_ALT_op_unused_43 - .L_op_unused_43) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8758,7 +8837,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (68 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_aget + sub lr, lr, #(.L_ALT_op_aget - .L_op_aget) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8775,7 +8855,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (69 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_aget_wide + sub lr, lr, #(.L_ALT_op_aget_wide - .L_op_aget_wide) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8792,7 +8873,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (70 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_aget_object + sub lr, lr, #(.L_ALT_op_aget_object - .L_op_aget_object) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8809,7 +8891,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (71 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_aget_boolean + sub lr, lr, #(.L_ALT_op_aget_boolean - .L_op_aget_boolean) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8826,7 +8909,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (72 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_aget_byte + sub lr, lr, #(.L_ALT_op_aget_byte - .L_op_aget_byte) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8843,7 +8927,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (73 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_aget_char + sub lr, lr, #(.L_ALT_op_aget_char - .L_op_aget_char) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8860,7 +8945,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (74 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_aget_short + sub lr, lr, #(.L_ALT_op_aget_short - .L_op_aget_short) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8877,7 +8963,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (75 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_aput + sub lr, lr, #(.L_ALT_op_aput - .L_op_aput) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8894,7 +8981,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (76 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_aput_wide + sub lr, lr, #(.L_ALT_op_aput_wide - .L_op_aput_wide) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8911,7 +8999,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (77 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_aput_object + sub lr, lr, #(.L_ALT_op_aput_object - .L_op_aput_object) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8928,7 +9017,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (78 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_aput_boolean + sub lr, lr, #(.L_ALT_op_aput_boolean - .L_op_aput_boolean) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8945,7 +9035,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (79 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_aput_byte + sub lr, lr, #(.L_ALT_op_aput_byte - .L_op_aput_byte) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8962,7 +9053,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (80 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_aput_char + sub lr, lr, #(.L_ALT_op_aput_char - .L_op_aput_char) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8979,7 +9071,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (81 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_aput_short + sub lr, lr, #(.L_ALT_op_aput_short - .L_op_aput_short) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -8996,7 +9089,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (82 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iget + sub lr, lr, #(.L_ALT_op_iget - .L_op_iget) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9013,7 +9107,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (83 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iget_wide + sub lr, lr, #(.L_ALT_op_iget_wide - .L_op_iget_wide) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9030,7 +9125,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (84 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iget_object + sub lr, lr, #(.L_ALT_op_iget_object - .L_op_iget_object) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9047,7 +9143,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (85 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iget_boolean + sub lr, lr, #(.L_ALT_op_iget_boolean - .L_op_iget_boolean) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9064,7 +9161,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (86 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iget_byte + sub lr, lr, #(.L_ALT_op_iget_byte - .L_op_iget_byte) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9081,7 +9179,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (87 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iget_char + sub lr, lr, #(.L_ALT_op_iget_char - .L_op_iget_char) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9098,7 +9197,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (88 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iget_short + sub lr, lr, #(.L_ALT_op_iget_short - .L_op_iget_short) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9115,7 +9215,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (89 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iput + sub lr, lr, #(.L_ALT_op_iput - .L_op_iput) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9132,7 +9233,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (90 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iput_wide + sub lr, lr, #(.L_ALT_op_iput_wide - .L_op_iput_wide) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9149,7 +9251,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (91 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iput_object + sub lr, lr, #(.L_ALT_op_iput_object - .L_op_iput_object) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9166,7 +9269,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (92 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iput_boolean + sub lr, lr, #(.L_ALT_op_iput_boolean - .L_op_iput_boolean) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9183,7 +9287,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (93 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iput_byte + sub lr, lr, #(.L_ALT_op_iput_byte - .L_op_iput_byte) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9200,7 +9305,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (94 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iput_char + sub lr, lr, #(.L_ALT_op_iput_char - .L_op_iput_char) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9217,7 +9323,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (95 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iput_short + sub lr, lr, #(.L_ALT_op_iput_short - .L_op_iput_short) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9234,7 +9341,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (96 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sget + sub lr, lr, #(.L_ALT_op_sget - .L_op_sget) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9251,7 +9359,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (97 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sget_wide + sub lr, lr, #(.L_ALT_op_sget_wide - .L_op_sget_wide) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9268,7 +9377,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (98 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sget_object + sub lr, lr, #(.L_ALT_op_sget_object - .L_op_sget_object) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9285,7 +9395,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (99 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sget_boolean + sub lr, lr, #(.L_ALT_op_sget_boolean - .L_op_sget_boolean) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9302,7 +9413,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (100 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sget_byte + sub lr, lr, #(.L_ALT_op_sget_byte - .L_op_sget_byte) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9319,7 +9431,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (101 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sget_char + sub lr, lr, #(.L_ALT_op_sget_char - .L_op_sget_char) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9336,7 +9449,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (102 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sget_short + sub lr, lr, #(.L_ALT_op_sget_short - .L_op_sget_short) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9353,7 +9467,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (103 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sput + sub lr, lr, #(.L_ALT_op_sput - .L_op_sput) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9370,7 +9485,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (104 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sput_wide + sub lr, lr, #(.L_ALT_op_sput_wide - .L_op_sput_wide) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9387,7 +9503,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (105 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sput_object + sub lr, lr, #(.L_ALT_op_sput_object - .L_op_sput_object) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9404,7 +9521,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (106 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sput_boolean + sub lr, lr, #(.L_ALT_op_sput_boolean - .L_op_sput_boolean) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9421,7 +9539,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (107 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sput_byte + sub lr, lr, #(.L_ALT_op_sput_byte - .L_op_sput_byte) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9438,7 +9557,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (108 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sput_char + sub lr, lr, #(.L_ALT_op_sput_char - .L_op_sput_char) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9455,7 +9575,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (109 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sput_short + sub lr, lr, #(.L_ALT_op_sput_short - .L_op_sput_short) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9472,7 +9593,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (110 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_invoke_virtual + sub lr, lr, #(.L_ALT_op_invoke_virtual - .L_op_invoke_virtual) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9489,7 +9611,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (111 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_invoke_super + sub lr, lr, #(.L_ALT_op_invoke_super - .L_op_invoke_super) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9506,7 +9629,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (112 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_invoke_direct + sub lr, lr, #(.L_ALT_op_invoke_direct - .L_op_invoke_direct) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9523,7 +9647,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (113 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_invoke_static + sub lr, lr, #(.L_ALT_op_invoke_static - .L_op_invoke_static) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9540,7 +9665,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (114 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_invoke_interface + sub lr, lr, #(.L_ALT_op_invoke_interface - .L_op_invoke_interface) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9557,7 +9683,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (115 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_return_void_no_barrier + sub lr, lr, #(.L_ALT_op_return_void_no_barrier - .L_op_return_void_no_barrier) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9574,7 +9701,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (116 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_invoke_virtual_range + sub lr, lr, #(.L_ALT_op_invoke_virtual_range - .L_op_invoke_virtual_range) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9591,7 +9719,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (117 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_invoke_super_range + sub lr, lr, #(.L_ALT_op_invoke_super_range - .L_op_invoke_super_range) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9608,7 +9737,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (118 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_invoke_direct_range + sub lr, lr, #(.L_ALT_op_invoke_direct_range - .L_op_invoke_direct_range) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9625,7 +9755,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (119 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_invoke_static_range + sub lr, lr, #(.L_ALT_op_invoke_static_range - .L_op_invoke_static_range) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9642,7 +9773,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (120 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_invoke_interface_range + sub lr, lr, #(.L_ALT_op_invoke_interface_range - .L_op_invoke_interface_range) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9659,7 +9791,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (121 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_unused_79 + sub lr, lr, #(.L_ALT_op_unused_79 - .L_op_unused_79) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9676,7 +9809,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (122 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_unused_7a + sub lr, lr, #(.L_ALT_op_unused_7a - .L_op_unused_7a) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9693,7 +9827,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (123 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_neg_int + sub lr, lr, #(.L_ALT_op_neg_int - .L_op_neg_int) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9710,7 +9845,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (124 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_not_int + sub lr, lr, #(.L_ALT_op_not_int - .L_op_not_int) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9727,7 +9863,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (125 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_neg_long + sub lr, lr, #(.L_ALT_op_neg_long - .L_op_neg_long) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9744,7 +9881,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (126 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_not_long + sub lr, lr, #(.L_ALT_op_not_long - .L_op_not_long) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9761,7 +9899,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (127 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_neg_float + sub lr, lr, #(.L_ALT_op_neg_float - .L_op_neg_float) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9778,7 +9917,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (128 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_neg_double + sub lr, lr, #(.L_ALT_op_neg_double - .L_op_neg_double) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9795,7 +9935,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (129 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_int_to_long + sub lr, lr, #(.L_ALT_op_int_to_long - .L_op_int_to_long) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9812,7 +9953,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (130 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_int_to_float + sub lr, lr, #(.L_ALT_op_int_to_float - .L_op_int_to_float) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9829,7 +9971,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (131 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_int_to_double + sub lr, lr, #(.L_ALT_op_int_to_double - .L_op_int_to_double) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9846,7 +9989,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (132 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_long_to_int + sub lr, lr, #(.L_ALT_op_long_to_int - .L_op_long_to_int) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9863,7 +10007,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (133 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_long_to_float + sub lr, lr, #(.L_ALT_op_long_to_float - .L_op_long_to_float) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9880,7 +10025,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (134 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_long_to_double + sub lr, lr, #(.L_ALT_op_long_to_double - .L_op_long_to_double) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9897,7 +10043,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (135 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_float_to_int + sub lr, lr, #(.L_ALT_op_float_to_int - .L_op_float_to_int) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9914,7 +10061,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (136 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_float_to_long + sub lr, lr, #(.L_ALT_op_float_to_long - .L_op_float_to_long) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9931,7 +10079,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (137 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_float_to_double + sub lr, lr, #(.L_ALT_op_float_to_double - .L_op_float_to_double) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9948,7 +10097,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (138 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_double_to_int + sub lr, lr, #(.L_ALT_op_double_to_int - .L_op_double_to_int) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9965,7 +10115,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (139 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_double_to_long + sub lr, lr, #(.L_ALT_op_double_to_long - .L_op_double_to_long) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9982,7 +10133,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (140 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_double_to_float + sub lr, lr, #(.L_ALT_op_double_to_float - .L_op_double_to_float) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -9999,7 +10151,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (141 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_int_to_byte + sub lr, lr, #(.L_ALT_op_int_to_byte - .L_op_int_to_byte) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10016,7 +10169,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (142 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_int_to_char + sub lr, lr, #(.L_ALT_op_int_to_char - .L_op_int_to_char) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10033,7 +10187,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (143 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_int_to_short + sub lr, lr, #(.L_ALT_op_int_to_short - .L_op_int_to_short) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10050,7 +10205,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (144 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_add_int + sub lr, lr, #(.L_ALT_op_add_int - .L_op_add_int) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10067,7 +10223,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (145 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sub_int + sub lr, lr, #(.L_ALT_op_sub_int - .L_op_sub_int) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10084,7 +10241,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (146 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_mul_int + sub lr, lr, #(.L_ALT_op_mul_int - .L_op_mul_int) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10101,7 +10259,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (147 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_div_int + sub lr, lr, #(.L_ALT_op_div_int - .L_op_div_int) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10118,7 +10277,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (148 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_rem_int + sub lr, lr, #(.L_ALT_op_rem_int - .L_op_rem_int) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10135,7 +10295,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (149 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_and_int + sub lr, lr, #(.L_ALT_op_and_int - .L_op_and_int) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10152,7 +10313,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (150 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_or_int + sub lr, lr, #(.L_ALT_op_or_int - .L_op_or_int) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10169,7 +10331,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (151 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_xor_int + sub lr, lr, #(.L_ALT_op_xor_int - .L_op_xor_int) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10186,7 +10349,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (152 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_shl_int + sub lr, lr, #(.L_ALT_op_shl_int - .L_op_shl_int) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10203,7 +10367,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (153 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_shr_int + sub lr, lr, #(.L_ALT_op_shr_int - .L_op_shr_int) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10220,7 +10385,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (154 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_ushr_int + sub lr, lr, #(.L_ALT_op_ushr_int - .L_op_ushr_int) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10237,7 +10403,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (155 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_add_long + sub lr, lr, #(.L_ALT_op_add_long - .L_op_add_long) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10254,7 +10421,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (156 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sub_long + sub lr, lr, #(.L_ALT_op_sub_long - .L_op_sub_long) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10271,7 +10439,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (157 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_mul_long + sub lr, lr, #(.L_ALT_op_mul_long - .L_op_mul_long) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10288,7 +10457,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (158 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_div_long + sub lr, lr, #(.L_ALT_op_div_long - .L_op_div_long) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10305,7 +10475,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (159 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_rem_long + sub lr, lr, #(.L_ALT_op_rem_long - .L_op_rem_long) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10322,7 +10493,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (160 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_and_long + sub lr, lr, #(.L_ALT_op_and_long - .L_op_and_long) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10339,7 +10511,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (161 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_or_long + sub lr, lr, #(.L_ALT_op_or_long - .L_op_or_long) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10356,7 +10529,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (162 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_xor_long + sub lr, lr, #(.L_ALT_op_xor_long - .L_op_xor_long) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10373,7 +10547,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (163 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_shl_long + sub lr, lr, #(.L_ALT_op_shl_long - .L_op_shl_long) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10390,7 +10565,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (164 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_shr_long + sub lr, lr, #(.L_ALT_op_shr_long - .L_op_shr_long) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10407,7 +10583,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (165 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_ushr_long + sub lr, lr, #(.L_ALT_op_ushr_long - .L_op_ushr_long) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10424,7 +10601,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (166 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_add_float + sub lr, lr, #(.L_ALT_op_add_float - .L_op_add_float) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10441,7 +10619,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (167 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sub_float + sub lr, lr, #(.L_ALT_op_sub_float - .L_op_sub_float) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10458,7 +10637,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (168 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_mul_float + sub lr, lr, #(.L_ALT_op_mul_float - .L_op_mul_float) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10475,7 +10655,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (169 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_div_float + sub lr, lr, #(.L_ALT_op_div_float - .L_op_div_float) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10492,7 +10673,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (170 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_rem_float + sub lr, lr, #(.L_ALT_op_rem_float - .L_op_rem_float) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10509,7 +10691,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (171 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_add_double + sub lr, lr, #(.L_ALT_op_add_double - .L_op_add_double) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10526,7 +10709,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (172 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sub_double + sub lr, lr, #(.L_ALT_op_sub_double - .L_op_sub_double) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10543,7 +10727,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (173 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_mul_double + sub lr, lr, #(.L_ALT_op_mul_double - .L_op_mul_double) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10560,7 +10745,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (174 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_div_double + sub lr, lr, #(.L_ALT_op_div_double - .L_op_div_double) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10577,7 +10763,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (175 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_rem_double + sub lr, lr, #(.L_ALT_op_rem_double - .L_op_rem_double) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10594,7 +10781,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (176 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_add_int_2addr + sub lr, lr, #(.L_ALT_op_add_int_2addr - .L_op_add_int_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10611,7 +10799,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (177 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sub_int_2addr + sub lr, lr, #(.L_ALT_op_sub_int_2addr - .L_op_sub_int_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10628,7 +10817,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (178 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_mul_int_2addr + sub lr, lr, #(.L_ALT_op_mul_int_2addr - .L_op_mul_int_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10645,7 +10835,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (179 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_div_int_2addr + sub lr, lr, #(.L_ALT_op_div_int_2addr - .L_op_div_int_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10662,7 +10853,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (180 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_rem_int_2addr + sub lr, lr, #(.L_ALT_op_rem_int_2addr - .L_op_rem_int_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10679,7 +10871,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (181 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_and_int_2addr + sub lr, lr, #(.L_ALT_op_and_int_2addr - .L_op_and_int_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10696,7 +10889,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (182 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_or_int_2addr + sub lr, lr, #(.L_ALT_op_or_int_2addr - .L_op_or_int_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10713,7 +10907,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (183 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_xor_int_2addr + sub lr, lr, #(.L_ALT_op_xor_int_2addr - .L_op_xor_int_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10730,7 +10925,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (184 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_shl_int_2addr + sub lr, lr, #(.L_ALT_op_shl_int_2addr - .L_op_shl_int_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10747,7 +10943,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (185 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_shr_int_2addr + sub lr, lr, #(.L_ALT_op_shr_int_2addr - .L_op_shr_int_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10764,7 +10961,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (186 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_ushr_int_2addr + sub lr, lr, #(.L_ALT_op_ushr_int_2addr - .L_op_ushr_int_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10781,7 +10979,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (187 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_add_long_2addr + sub lr, lr, #(.L_ALT_op_add_long_2addr - .L_op_add_long_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10798,7 +10997,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (188 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sub_long_2addr + sub lr, lr, #(.L_ALT_op_sub_long_2addr - .L_op_sub_long_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10815,7 +11015,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (189 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_mul_long_2addr + sub lr, lr, #(.L_ALT_op_mul_long_2addr - .L_op_mul_long_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10832,7 +11033,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (190 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_div_long_2addr + sub lr, lr, #(.L_ALT_op_div_long_2addr - .L_op_div_long_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10849,7 +11051,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (191 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_rem_long_2addr + sub lr, lr, #(.L_ALT_op_rem_long_2addr - .L_op_rem_long_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10866,7 +11069,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (192 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_and_long_2addr + sub lr, lr, #(.L_ALT_op_and_long_2addr - .L_op_and_long_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10883,7 +11087,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (193 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_or_long_2addr + sub lr, lr, #(.L_ALT_op_or_long_2addr - .L_op_or_long_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10900,7 +11105,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (194 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_xor_long_2addr + sub lr, lr, #(.L_ALT_op_xor_long_2addr - .L_op_xor_long_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10917,7 +11123,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (195 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_shl_long_2addr + sub lr, lr, #(.L_ALT_op_shl_long_2addr - .L_op_shl_long_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10934,7 +11141,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (196 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_shr_long_2addr + sub lr, lr, #(.L_ALT_op_shr_long_2addr - .L_op_shr_long_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10951,7 +11159,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (197 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_ushr_long_2addr + sub lr, lr, #(.L_ALT_op_ushr_long_2addr - .L_op_ushr_long_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10968,7 +11177,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (198 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_add_float_2addr + sub lr, lr, #(.L_ALT_op_add_float_2addr - .L_op_add_float_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -10985,7 +11195,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (199 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sub_float_2addr + sub lr, lr, #(.L_ALT_op_sub_float_2addr - .L_op_sub_float_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11002,7 +11213,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (200 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_mul_float_2addr + sub lr, lr, #(.L_ALT_op_mul_float_2addr - .L_op_mul_float_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11019,7 +11231,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (201 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_div_float_2addr + sub lr, lr, #(.L_ALT_op_div_float_2addr - .L_op_div_float_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11036,7 +11249,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (202 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_rem_float_2addr + sub lr, lr, #(.L_ALT_op_rem_float_2addr - .L_op_rem_float_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11053,7 +11267,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (203 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_add_double_2addr + sub lr, lr, #(.L_ALT_op_add_double_2addr - .L_op_add_double_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11070,7 +11285,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (204 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_sub_double_2addr + sub lr, lr, #(.L_ALT_op_sub_double_2addr - .L_op_sub_double_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11087,7 +11303,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (205 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_mul_double_2addr + sub lr, lr, #(.L_ALT_op_mul_double_2addr - .L_op_mul_double_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11104,7 +11321,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (206 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_div_double_2addr + sub lr, lr, #(.L_ALT_op_div_double_2addr - .L_op_div_double_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11121,7 +11339,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (207 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_rem_double_2addr + sub lr, lr, #(.L_ALT_op_rem_double_2addr - .L_op_rem_double_2addr) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11138,7 +11357,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (208 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_add_int_lit16 + sub lr, lr, #(.L_ALT_op_add_int_lit16 - .L_op_add_int_lit16) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11155,7 +11375,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (209 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_rsub_int + sub lr, lr, #(.L_ALT_op_rsub_int - .L_op_rsub_int) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11172,7 +11393,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (210 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_mul_int_lit16 + sub lr, lr, #(.L_ALT_op_mul_int_lit16 - .L_op_mul_int_lit16) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11189,7 +11411,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (211 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_div_int_lit16 + sub lr, lr, #(.L_ALT_op_div_int_lit16 - .L_op_div_int_lit16) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11206,7 +11429,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (212 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_rem_int_lit16 + sub lr, lr, #(.L_ALT_op_rem_int_lit16 - .L_op_rem_int_lit16) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11223,7 +11447,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (213 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_and_int_lit16 + sub lr, lr, #(.L_ALT_op_and_int_lit16 - .L_op_and_int_lit16) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11240,7 +11465,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (214 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_or_int_lit16 + sub lr, lr, #(.L_ALT_op_or_int_lit16 - .L_op_or_int_lit16) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11257,7 +11483,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (215 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_xor_int_lit16 + sub lr, lr, #(.L_ALT_op_xor_int_lit16 - .L_op_xor_int_lit16) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11274,7 +11501,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (216 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_add_int_lit8 + sub lr, lr, #(.L_ALT_op_add_int_lit8 - .L_op_add_int_lit8) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11291,7 +11519,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (217 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_rsub_int_lit8 + sub lr, lr, #(.L_ALT_op_rsub_int_lit8 - .L_op_rsub_int_lit8) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11308,7 +11537,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (218 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_mul_int_lit8 + sub lr, lr, #(.L_ALT_op_mul_int_lit8 - .L_op_mul_int_lit8) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11325,7 +11555,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (219 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_div_int_lit8 + sub lr, lr, #(.L_ALT_op_div_int_lit8 - .L_op_div_int_lit8) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11342,7 +11573,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (220 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_rem_int_lit8 + sub lr, lr, #(.L_ALT_op_rem_int_lit8 - .L_op_rem_int_lit8) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11359,7 +11591,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (221 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_and_int_lit8 + sub lr, lr, #(.L_ALT_op_and_int_lit8 - .L_op_and_int_lit8) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11376,7 +11609,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (222 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_or_int_lit8 + sub lr, lr, #(.L_ALT_op_or_int_lit8 - .L_op_or_int_lit8) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11393,7 +11627,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (223 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_xor_int_lit8 + sub lr, lr, #(.L_ALT_op_xor_int_lit8 - .L_op_xor_int_lit8) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11410,7 +11645,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (224 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_shl_int_lit8 + sub lr, lr, #(.L_ALT_op_shl_int_lit8 - .L_op_shl_int_lit8) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11427,7 +11663,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (225 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_shr_int_lit8 + sub lr, lr, #(.L_ALT_op_shr_int_lit8 - .L_op_shr_int_lit8) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11444,7 +11681,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (226 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_ushr_int_lit8 + sub lr, lr, #(.L_ALT_op_ushr_int_lit8 - .L_op_ushr_int_lit8) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11461,7 +11699,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (227 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iget_quick + sub lr, lr, #(.L_ALT_op_iget_quick - .L_op_iget_quick) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11478,7 +11717,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (228 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iget_wide_quick + sub lr, lr, #(.L_ALT_op_iget_wide_quick - .L_op_iget_wide_quick) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11495,7 +11735,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (229 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iget_object_quick + sub lr, lr, #(.L_ALT_op_iget_object_quick - .L_op_iget_object_quick) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11512,7 +11753,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (230 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iput_quick + sub lr, lr, #(.L_ALT_op_iput_quick - .L_op_iput_quick) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11529,7 +11771,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (231 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iput_wide_quick + sub lr, lr, #(.L_ALT_op_iput_wide_quick - .L_op_iput_wide_quick) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11546,7 +11789,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (232 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iput_object_quick + sub lr, lr, #(.L_ALT_op_iput_object_quick - .L_op_iput_object_quick) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11563,7 +11807,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (233 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_invoke_virtual_quick + sub lr, lr, #(.L_ALT_op_invoke_virtual_quick - .L_op_invoke_virtual_quick) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11580,7 +11825,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (234 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_invoke_virtual_range_quick + sub lr, lr, #(.L_ALT_op_invoke_virtual_range_quick - .L_op_invoke_virtual_range_quick) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11597,7 +11843,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (235 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iput_boolean_quick + sub lr, lr, #(.L_ALT_op_iput_boolean_quick - .L_op_iput_boolean_quick) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11614,7 +11861,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (236 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iput_byte_quick + sub lr, lr, #(.L_ALT_op_iput_byte_quick - .L_op_iput_byte_quick) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11631,7 +11879,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (237 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iput_char_quick + sub lr, lr, #(.L_ALT_op_iput_char_quick - .L_op_iput_char_quick) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11648,7 +11897,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (238 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iput_short_quick + sub lr, lr, #(.L_ALT_op_iput_short_quick - .L_op_iput_short_quick) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11665,7 +11915,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (239 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iget_boolean_quick + sub lr, lr, #(.L_ALT_op_iget_boolean_quick - .L_op_iget_boolean_quick) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11682,7 +11933,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (240 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iget_byte_quick + sub lr, lr, #(.L_ALT_op_iget_byte_quick - .L_op_iget_byte_quick) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11699,7 +11951,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (241 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iget_char_quick + sub lr, lr, #(.L_ALT_op_iget_char_quick - .L_op_iget_char_quick) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11716,7 +11969,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (242 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_iget_short_quick + sub lr, lr, #(.L_ALT_op_iget_short_quick - .L_op_iget_short_quick) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11733,7 +11987,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (243 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_unused_f3 + sub lr, lr, #(.L_ALT_op_unused_f3 - .L_op_unused_f3) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11750,7 +12005,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (244 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_unused_f4 + sub lr, lr, #(.L_ALT_op_unused_f4 - .L_op_unused_f4) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11767,7 +12023,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (245 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_unused_f5 + sub lr, lr, #(.L_ALT_op_unused_f5 - .L_op_unused_f5) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11784,7 +12041,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (246 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_unused_f6 + sub lr, lr, #(.L_ALT_op_unused_f6 - .L_op_unused_f6) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11801,7 +12059,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (247 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_unused_f7 + sub lr, lr, #(.L_ALT_op_unused_f7 - .L_op_unused_f7) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11818,7 +12077,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (248 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_unused_f8 + sub lr, lr, #(.L_ALT_op_unused_f8 - .L_op_unused_f8) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11835,7 +12095,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (249 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_unused_f9 + sub lr, lr, #(.L_ALT_op_unused_f9 - .L_op_unused_f9) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11852,7 +12113,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (250 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_invoke_polymorphic + sub lr, lr, #(.L_ALT_op_invoke_polymorphic - .L_op_invoke_polymorphic) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11869,7 +12131,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (251 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_invoke_polymorphic_range + sub lr, lr, #(.L_ALT_op_invoke_polymorphic_range - .L_op_invoke_polymorphic_range) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11886,7 +12149,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (252 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_invoke_custom + sub lr, lr, #(.L_ALT_op_invoke_custom - .L_op_invoke_custom) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11903,7 +12167,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (253 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_invoke_custom_range + sub lr, lr, #(.L_ALT_op_invoke_custom_range - .L_op_invoke_custom_range) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11920,7 +12185,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (254 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_const_method_handle + sub lr, lr, #(.L_ALT_op_const_method_handle - .L_op_const_method_handle) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC @@ -11937,15 +12203,19 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop */ .extern MterpCheckBefore ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh IBASE. - adrl lr, artMterpAsmInstructionStart + (255 * 128) @ Addr of primary handler. + adr lr, .L_ALT_op_const_method_type + sub lr, lr, #(.L_ALT_op_const_method_type - .L_op_const_method_type) @ Addr of primary handler. mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME mov r2, rPC b MterpCheckBefore @ (self, shadow_frame, dex_pc_ptr) @ Tail call. .balign 128 +/* File: arm/instruction_end_alt.S */ + .global artMterpAsmAltInstructionEnd artMterpAsmAltInstructionEnd: + /* File: arm/footer.S */ /* * =========================================================================== @@ -12097,7 +12367,7 @@ MterpCommonTakenBranch: #endif cmp rPROFILE, #JIT_CHECK_OSR beq .L_osr_check - subgts rPROFILE, #1 + subsgt rPROFILE, #1 beq .L_add_batch @ counted down to zero - report .L_resume_backward_branch: ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S index d5374d2a8ad968190c46d9c549ee710a8d454710..2a0c4df3e23c90cf7209fde98f3b058df8a01dcf 100644 --- a/runtime/interpreter/mterp/out/mterp_arm64.S +++ b/runtime/interpreter/mterp/out/mterp_arm64.S @@ -346,6 +346,7 @@ codes. */ .macro ENTRY name .type \name, #function + .hidden \name // Hide this as a global symbol, so we do not incur plt calls. .global \name /* Cache alignment for function entry */ .balign 16 @@ -426,6 +427,7 @@ ENTRY ExecuteMterpImpl GOTO_OPCODE ip // jump to next instruction /* NOTE: no fallthrough */ +/* File: arm64/instruction_start.S */ .global artMterpAsmInstructionStart artMterpAsmInstructionStart = .L_op_nop @@ -7074,18 +7076,26 @@ artMterpAsmInstructionStart = .L_op_nop .balign 128 +/* File: arm64/instruction_end.S */ + .global artMterpAsmInstructionEnd artMterpAsmInstructionEnd: + /* * =========================================================================== * Sister implementations * =========================================================================== */ +/* File: arm64/instruction_start_sister.S */ + .global artMterpAsmSisterStart .text .balign 4 artMterpAsmSisterStart: + +/* File: arm64/instruction_end_sister.S */ + .global artMterpAsmSisterEnd artMterpAsmSisterEnd: @@ -7397,11 +7407,12 @@ MterpProfileActive: ret +/* File: arm64/instruction_start_alt.S */ .global artMterpAsmAltInstructionStart +artMterpAsmAltInstructionStart = .L_ALT_op_nop .text -artMterpAsmAltInstructionStart = .L_ALT_op_nop /* ------------------------------ */ .balign 128 .L_ALT_op_nop: /* 0x00 */ @@ -11755,8 +11766,11 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop b MterpCheckBefore // (self, shadow_frame, dex_pc_ptr) Note: tail call. .balign 128 +/* File: arm64/instruction_end_alt.S */ + .global artMterpAsmAltInstructionEnd artMterpAsmAltInstructionEnd: + /* File: arm64/close_cfi.S */ // Close out the cfi info. We're treating mterp as a single function. diff --git a/runtime/interpreter/mterp/out/mterp_mips.S b/runtime/interpreter/mterp/out/mterp_mips.S index 69568eaf441bd610376ed087d748be278a1cf3c7..3b86279b473c8e701e81c11d289ebbff74d2f3c0 100644 --- a/runtime/interpreter/mterp/out/mterp_mips.S +++ b/runtime/interpreter/mterp/out/mterp_mips.S @@ -810,6 +810,7 @@ ExecuteMterpImpl: GOTO_OPCODE(t0) # jump to next instruction /* NOTE: no fallthrough */ +/* File: mips/instruction_start.S */ .global artMterpAsmInstructionStart artMterpAsmInstructionStart = .L_op_nop @@ -7873,19 +7874,25 @@ artMterpAsmInstructionStart = .L_op_nop .balign 128 +/* File: mips/instruction_end.S */ + .global artMterpAsmInstructionEnd artMterpAsmInstructionEnd: + /* * =========================================================================== * Sister implementations * =========================================================================== */ +/* File: mips/instruction_start_sister.S */ + .global artMterpAsmSisterStart .text .balign 4 artMterpAsmSisterStart: + /* continuation for op_float_to_long */ #ifndef MIPS32REVGE6 @@ -7941,14 +7948,17 @@ artMterpAsmSisterStart: .Lop_ushr_long_2addr_finish: SET_VREG64_GOTO(v1, zero, t3, t0) # vA/vA+1 <- rlo/rhi +/* File: mips/instruction_end_sister.S */ + .global artMterpAsmSisterEnd artMterpAsmSisterEnd: +/* File: mips/instruction_start_alt.S */ .global artMterpAsmAltInstructionStart +artMterpAsmAltInstructionStart = .L_ALT_op_nop .text -artMterpAsmAltInstructionStart = .L_ALT_op_nop /* ------------------------------ */ .balign 128 .L_ALT_op_nop: /* 0x00 */ @@ -12558,8 +12568,11 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop jalr zero, t9 # Tail call to Mterp(self, shadow_frame, dex_pc_ptr) .balign 128 +/* File: mips/instruction_end_alt.S */ + .global artMterpAsmAltInstructionEnd artMterpAsmAltInstructionEnd: + /* File: mips/footer.S */ /* * =========================================================================== diff --git a/runtime/interpreter/mterp/out/mterp_mips64.S b/runtime/interpreter/mterp/out/mterp_mips64.S index 83a6613e37b660fafef970ec11fe9779dc2a8778..58f98dfabb67bbcb87783e240ccc59bd181a364f 100644 --- a/runtime/interpreter/mterp/out/mterp_mips64.S +++ b/runtime/interpreter/mterp/out/mterp_mips64.S @@ -430,6 +430,7 @@ ExecuteMterpImpl: /* NOTE: no fallthrough */ +/* File: mips64/instruction_start.S */ .global artMterpAsmInstructionStart artMterpAsmInstructionStart = .L_op_nop @@ -7299,26 +7300,35 @@ artMterpAsmInstructionStart = .L_op_nop .balign 128 +/* File: mips64/instruction_end.S */ + .global artMterpAsmInstructionEnd artMterpAsmInstructionEnd: + /* * =========================================================================== * Sister implementations * =========================================================================== */ +/* File: mips64/instruction_start_sister.S */ + .global artMterpAsmSisterStart .text .balign 4 artMterpAsmSisterStart: + +/* File: mips64/instruction_end_sister.S */ + .global artMterpAsmSisterEnd artMterpAsmSisterEnd: +/* File: mips64/instruction_start_alt.S */ .global artMterpAsmAltInstructionStart +artMterpAsmAltInstructionStart = .L_ALT_op_nop .text -artMterpAsmAltInstructionStart = .L_ALT_op_nop /* ------------------------------ */ .balign 128 .L_ALT_op_nop: /* 0x00 */ @@ -12184,8 +12194,11 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop jalr zero, t9 # (self, shadow_frame, dex_pc_ptr) Note: tail call. .balign 128 +/* File: mips64/instruction_end_alt.S */ + .global artMterpAsmAltInstructionEnd artMterpAsmAltInstructionEnd: + /* File: mips64/footer.S */ /* * We've detected a condition that will result in an exception, but the exception diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S index 6f4752f3129a1f962cd5ffc176ee7a1f805109b3..6be70cce4c7a8ec35222de4fc352069c3cb9eb6e 100644 --- a/runtime/interpreter/mterp/out/mterp_x86.S +++ b/runtime/interpreter/mterp/out/mterp_x86.S @@ -106,11 +106,13 @@ unspecified registers or condition codes. #define SIZE(start,end) // Mac OS' symbols have an _ prefix. #define SYMBOL(name) _ ## name + #define ASM_HIDDEN .private_extern #else #define MACRO_LITERAL(value) $value #define FUNCTION_TYPE(name) .type name, @function #define SIZE(start,end) .size start, .-end #define SYMBOL(name) name + #define ASM_HIDDEN .hidden #endif .macro PUSH _reg @@ -339,6 +341,7 @@ unspecified registers or condition codes. */ .text + ASM_HIDDEN SYMBOL(ExecuteMterpImpl) .global SYMBOL(ExecuteMterpImpl) FUNCTION_TYPE(ExecuteMterpImpl) @@ -402,6 +405,7 @@ SYMBOL(ExecuteMterpImpl): GOTO_NEXT /* NOTE: no fallthrough */ +/* File: x86/instruction_start.S */ .global SYMBOL(artMterpAsmInstructionStart) SYMBOL(artMterpAsmInstructionStart) = .L_op_nop @@ -6467,26 +6471,35 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop .balign 128 +/* File: x86/instruction_end.S */ + .global SYMBOL(artMterpAsmInstructionEnd) SYMBOL(artMterpAsmInstructionEnd): + /* * =========================================================================== * Sister implementations * =========================================================================== */ +/* File: x86/instruction_start_sister.S */ + .global SYMBOL(artMterpAsmSisterStart) .text .balign 4 SYMBOL(artMterpAsmSisterStart): + +/* File: x86/instruction_end_sister.S */ + .global SYMBOL(artMterpAsmSisterEnd) SYMBOL(artMterpAsmSisterEnd): +/* File: x86/instruction_start_alt.S */ .global SYMBOL(artMterpAsmAltInstructionStart) .text - SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop + /* ------------------------------ */ .balign 128 .L_ALT_op_nop: /* 0x00 */ @@ -12632,8 +12645,11 @@ SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop jmp .L_op_nop+(255*128) .balign 128 +/* File: x86/instruction_end_alt.S */ + .global SYMBOL(artMterpAsmAltInstructionEnd) SYMBOL(artMterpAsmAltInstructionEnd): + /* File: x86/footer.S */ /* * =========================================================================== diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S index fca2515698a7dc8c35ade7b2302f257d97cda97e..562cf7ceb68d641c4227fecdea3cdc12f90fe412 100644 --- a/runtime/interpreter/mterp/out/mterp_x86_64.S +++ b/runtime/interpreter/mterp/out/mterp_x86_64.S @@ -102,11 +102,13 @@ unspecified registers or condition codes. #define SIZE(start,end) // Mac OS' symbols have an _ prefix. #define SYMBOL(name) _ ## name + #define ASM_HIDDEN .private_extern #else #define MACRO_LITERAL(value) $value #define FUNCTION_TYPE(name) .type name, @function #define SIZE(start,end) .size start, .-end #define SYMBOL(name) name + #define ASM_HIDDEN .hidden #endif .macro PUSH _reg @@ -325,6 +327,7 @@ unspecified registers or condition codes. */ .text + ASM_HIDDEN SYMBOL(ExecuteMterpImpl) .global SYMBOL(ExecuteMterpImpl) FUNCTION_TYPE(ExecuteMterpImpl) @@ -384,6 +387,7 @@ SYMBOL(ExecuteMterpImpl): GOTO_NEXT /* NOTE: no fallthrough */ +/* File: x86_64/instruction_start.S */ .global SYMBOL(artMterpAsmInstructionStart) SYMBOL(artMterpAsmInstructionStart) = .L_op_nop @@ -6214,26 +6218,35 @@ movswl %ax, %eax .balign 128 +/* File: x86_64/instruction_end.S */ + .global SYMBOL(artMterpAsmInstructionEnd) SYMBOL(artMterpAsmInstructionEnd): + /* * =========================================================================== * Sister implementations * =========================================================================== */ +/* File: x86_64/instruction_start_sister.S */ + .global SYMBOL(artMterpAsmSisterStart) .text .balign 4 SYMBOL(artMterpAsmSisterStart): + +/* File: x86_64/instruction_end_sister.S */ + .global SYMBOL(artMterpAsmSisterEnd) SYMBOL(artMterpAsmSisterEnd): +/* File: x86_64/instruction_start_alt.S */ .global SYMBOL(artMterpAsmAltInstructionStart) .text - SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop + /* ------------------------------ */ .balign 128 .L_ALT_op_nop: /* 0x00 */ @@ -11867,8 +11880,11 @@ SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop jmp .L_op_nop+(255*128) .balign 128 +/* File: x86_64/instruction_end_alt.S */ + .global SYMBOL(artMterpAsmAltInstructionEnd) SYMBOL(artMterpAsmAltInstructionEnd): + /* File: x86_64/footer.S */ /* * =========================================================================== diff --git a/runtime/interpreter/mterp/x86/entry.S b/runtime/interpreter/mterp/x86/entry.S index 324637bf9a284061ae0a8ae97ead71b76fd924f2..939dc61d952c0efb0d6627071e1b87cb9bcb3000 100644 --- a/runtime/interpreter/mterp/x86/entry.S +++ b/runtime/interpreter/mterp/x86/entry.S @@ -18,6 +18,7 @@ */ .text + ASM_HIDDEN SYMBOL(ExecuteMterpImpl) .global SYMBOL(ExecuteMterpImpl) FUNCTION_TYPE(ExecuteMterpImpl) diff --git a/runtime/interpreter/mterp/x86/header.S b/runtime/interpreter/mterp/x86/header.S index 9d826c2ce2b7751a8e82f3313f7149e58ac6aae5..6f31228005d08fcef1067c4933851546eab5f616 100644 --- a/runtime/interpreter/mterp/x86/header.S +++ b/runtime/interpreter/mterp/x86/header.S @@ -99,11 +99,13 @@ unspecified registers or condition codes. #define SIZE(start,end) // Mac OS' symbols have an _ prefix. #define SYMBOL(name) _ ## name + #define ASM_HIDDEN .private_extern #else #define MACRO_LITERAL(value) $$value #define FUNCTION_TYPE(name) .type name, @function #define SIZE(start,end) .size start, .-end #define SYMBOL(name) name + #define ASM_HIDDEN .hidden #endif .macro PUSH _reg diff --git a/runtime/interpreter/mterp/x86/instruction_end.S b/runtime/interpreter/mterp/x86/instruction_end.S new file mode 100644 index 0000000000000000000000000000000000000000..3a02a212e61bfac870f2155e672edbce112270f9 --- /dev/null +++ b/runtime/interpreter/mterp/x86/instruction_end.S @@ -0,0 +1,3 @@ + + .global SYMBOL(artMterpAsmInstructionEnd) +SYMBOL(artMterpAsmInstructionEnd): diff --git a/runtime/interpreter/mterp/x86/instruction_end_alt.S b/runtime/interpreter/mterp/x86/instruction_end_alt.S new file mode 100644 index 0000000000000000000000000000000000000000..33c2b8e2a08ef38b538b25a775a4d4baaf5fae1a --- /dev/null +++ b/runtime/interpreter/mterp/x86/instruction_end_alt.S @@ -0,0 +1,3 @@ + + .global SYMBOL(artMterpAsmAltInstructionEnd) +SYMBOL(artMterpAsmAltInstructionEnd): diff --git a/runtime/interpreter/mterp/x86/instruction_end_sister.S b/runtime/interpreter/mterp/x86/instruction_end_sister.S new file mode 100644 index 0000000000000000000000000000000000000000..ea14b11ede48f73e8b9c87051a570f3ef0002bc6 --- /dev/null +++ b/runtime/interpreter/mterp/x86/instruction_end_sister.S @@ -0,0 +1,3 @@ + + .global SYMBOL(artMterpAsmSisterEnd) +SYMBOL(artMterpAsmSisterEnd): diff --git a/runtime/interpreter/mterp/x86/instruction_start.S b/runtime/interpreter/mterp/x86/instruction_start.S new file mode 100644 index 0000000000000000000000000000000000000000..ca711de00c2ab40fe4b560d6f5f128a93922a837 --- /dev/null +++ b/runtime/interpreter/mterp/x86/instruction_start.S @@ -0,0 +1,4 @@ + + .global SYMBOL(artMterpAsmInstructionStart) +SYMBOL(artMterpAsmInstructionStart) = .L_op_nop + .text diff --git a/runtime/interpreter/mterp/x86/instruction_start_alt.S b/runtime/interpreter/mterp/x86/instruction_start_alt.S new file mode 100644 index 0000000000000000000000000000000000000000..9272a6a7b091c5e1dcead188453713518ea68ab9 --- /dev/null +++ b/runtime/interpreter/mterp/x86/instruction_start_alt.S @@ -0,0 +1,4 @@ + + .global SYMBOL(artMterpAsmAltInstructionStart) + .text +SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop diff --git a/runtime/interpreter/mterp/x86/instruction_start_sister.S b/runtime/interpreter/mterp/x86/instruction_start_sister.S new file mode 100644 index 0000000000000000000000000000000000000000..b9ac994d321f3e910e5f573060d0b0636a25bf7a --- /dev/null +++ b/runtime/interpreter/mterp/x86/instruction_start_sister.S @@ -0,0 +1,5 @@ + + .global SYMBOL(artMterpAsmSisterStart) + .text + .balign 4 +SYMBOL(artMterpAsmSisterStart): diff --git a/runtime/interpreter/mterp/x86_64/entry.S b/runtime/interpreter/mterp/x86_64/entry.S index 2f69226206c9a3ae1dc56f10fd7da1ba282af19a..b08419b2199c410a24af64ea5ba2c63c3ba69bdb 100644 --- a/runtime/interpreter/mterp/x86_64/entry.S +++ b/runtime/interpreter/mterp/x86_64/entry.S @@ -18,6 +18,7 @@ */ .text + ASM_HIDDEN SYMBOL(ExecuteMterpImpl) .global SYMBOL(ExecuteMterpImpl) FUNCTION_TYPE(ExecuteMterpImpl) diff --git a/runtime/interpreter/mterp/x86_64/header.S b/runtime/interpreter/mterp/x86_64/header.S index 55638106ed486e9a5cef76ee101a7c9387526fbe..4ebe95e9873caa292df4eccb2fc5766ff5f79591 100644 --- a/runtime/interpreter/mterp/x86_64/header.S +++ b/runtime/interpreter/mterp/x86_64/header.S @@ -95,11 +95,13 @@ unspecified registers or condition codes. #define SIZE(start,end) // Mac OS' symbols have an _ prefix. #define SYMBOL(name) _ ## name + #define ASM_HIDDEN .private_extern #else #define MACRO_LITERAL(value) $$value #define FUNCTION_TYPE(name) .type name, @function #define SIZE(start,end) .size start, .-end #define SYMBOL(name) name + #define ASM_HIDDEN .hidden #endif .macro PUSH _reg diff --git a/runtime/interpreter/mterp/x86_64/instruction_end.S b/runtime/interpreter/mterp/x86_64/instruction_end.S new file mode 100644 index 0000000000000000000000000000000000000000..3a02a212e61bfac870f2155e672edbce112270f9 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/instruction_end.S @@ -0,0 +1,3 @@ + + .global SYMBOL(artMterpAsmInstructionEnd) +SYMBOL(artMterpAsmInstructionEnd): diff --git a/runtime/interpreter/mterp/x86_64/instruction_end_alt.S b/runtime/interpreter/mterp/x86_64/instruction_end_alt.S new file mode 100644 index 0000000000000000000000000000000000000000..33c2b8e2a08ef38b538b25a775a4d4baaf5fae1a --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/instruction_end_alt.S @@ -0,0 +1,3 @@ + + .global SYMBOL(artMterpAsmAltInstructionEnd) +SYMBOL(artMterpAsmAltInstructionEnd): diff --git a/runtime/interpreter/mterp/x86_64/instruction_end_sister.S b/runtime/interpreter/mterp/x86_64/instruction_end_sister.S new file mode 100644 index 0000000000000000000000000000000000000000..ea14b11ede48f73e8b9c87051a570f3ef0002bc6 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/instruction_end_sister.S @@ -0,0 +1,3 @@ + + .global SYMBOL(artMterpAsmSisterEnd) +SYMBOL(artMterpAsmSisterEnd): diff --git a/runtime/interpreter/mterp/x86_64/instruction_start.S b/runtime/interpreter/mterp/x86_64/instruction_start.S new file mode 100644 index 0000000000000000000000000000000000000000..ca711de00c2ab40fe4b560d6f5f128a93922a837 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/instruction_start.S @@ -0,0 +1,4 @@ + + .global SYMBOL(artMterpAsmInstructionStart) +SYMBOL(artMterpAsmInstructionStart) = .L_op_nop + .text diff --git a/runtime/interpreter/mterp/x86_64/instruction_start_alt.S b/runtime/interpreter/mterp/x86_64/instruction_start_alt.S new file mode 100644 index 0000000000000000000000000000000000000000..9272a6a7b091c5e1dcead188453713518ea68ab9 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/instruction_start_alt.S @@ -0,0 +1,4 @@ + + .global SYMBOL(artMterpAsmAltInstructionStart) + .text +SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop diff --git a/runtime/interpreter/mterp/x86_64/instruction_start_sister.S b/runtime/interpreter/mterp/x86_64/instruction_start_sister.S new file mode 100644 index 0000000000000000000000000000000000000000..b9ac994d321f3e910e5f573060d0b0636a25bf7a --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/instruction_start_sister.S @@ -0,0 +1,5 @@ + + .global SYMBOL(artMterpAsmSisterStart) + .text + .balign 4 +SYMBOL(artMterpAsmSisterStart): diff --git a/runtime/interpreter/shadow_frame-inl.h b/runtime/interpreter/shadow_frame-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..7eaad597d2b4f8611480225ff617af7ad0931775 --- /dev/null +++ b/runtime/interpreter/shadow_frame-inl.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_INTERPRETER_SHADOW_FRAME_INL_H_ +#define ART_RUNTIME_INTERPRETER_SHADOW_FRAME_INL_H_ + +#include "shadow_frame.h" + +#include "obj_ptr-inl.h" + +namespace art { + +template +inline void ShadowFrame::SetVRegReference(size_t i, ObjPtr val) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_LT(i, NumberOfVRegs()); + if (kVerifyFlags & kVerifyWrites) { + VerifyObject(val); + } + ReadBarrier::MaybeAssertToSpaceInvariant(val.Ptr()); + uint32_t* vreg = &vregs_[i]; + reinterpret_cast*>(vreg)->Assign(val); + if (HasReferenceArray()) { + References()[i].Assign(val); + } +} + +} // namespace art + +#endif // ART_RUNTIME_INTERPRETER_SHADOW_FRAME_INL_H_ diff --git a/runtime/interpreter/shadow_frame.h b/runtime/interpreter/shadow_frame.h index d5451ffded866a226eb9cad79e372f512139a135..f76b86c94f66c24d97730a373bec16487580fb01 100644 --- a/runtime/interpreter/shadow_frame.h +++ b/runtime/interpreter/shadow_frame.h @@ -37,6 +37,7 @@ class Object; class ArtMethod; class ShadowFrame; +template class ObjPtr; class Thread; union JValue; @@ -245,18 +246,8 @@ class ShadowFrame { } template - void SetVRegReference(size_t i, mirror::Object* val) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK_LT(i, NumberOfVRegs()); - if (kVerifyFlags & kVerifyWrites) { - VerifyObject(val); - } - ReadBarrier::MaybeAssertToSpaceInvariant(val); - uint32_t* vreg = &vregs_[i]; - reinterpret_cast*>(vreg)->Assign(val); - if (HasReferenceArray()) { - References()[i].Assign(val); - } - } + void SetVRegReference(size_t i, ObjPtr val) + REQUIRES_SHARED(Locks::mutator_lock_); void SetMethod(ArtMethod* method) REQUIRES(Locks::mutator_lock_) { DCHECK(method != nullptr); diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 6e920363e8ef88df762d349044c15d4b8c7b61c8..22a6e9d9411689ac6074212f939e9339f0689f4d 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -34,6 +34,7 @@ #include "base/enums.h" #include "base/macros.h" #include "base/quasi_atomic.h" +#include "base/zip_archive.h" #include "class_linker.h" #include "common_throws.h" #include "dex/descriptors_names.h" @@ -56,7 +57,6 @@ #include "thread-inl.h" #include "transaction.h" #include "well_known_classes.h" -#include "zip_archive.h" namespace art { namespace interpreter { @@ -131,7 +131,7 @@ static void UnstartedRuntimeFindClass(Thread* self, Handle class std::string descriptor(DotToDescriptor(className->ToModifiedUtf8().c_str())); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - mirror::Class* found = class_linker->FindClass(self, descriptor.c_str(), class_loader); + ObjPtr found = class_linker->FindClass(self, descriptor.c_str(), class_loader); if (found == nullptr && abort_if_not_found) { if (!self->IsExceptionPending()) { AbortTransactionOrFail(self, "%s failed in un-started runtime for class: %s", @@ -142,7 +142,7 @@ static void UnstartedRuntimeFindClass(Thread* self, Handle class } if (found != nullptr && initialize_class) { StackHandleScope<1> hs(self); - Handle h_class(hs.NewHandle(found)); + HandleWrapperObjPtr h_class = hs.NewHandleWrapper(&found); if (!class_linker->EnsureInitialized(self, h_class, true, true)) { CHECK(self->IsExceptionPending()); return; @@ -269,8 +269,7 @@ void UnstartedRuntime::UnstartedClassNewInstance( AbortTransactionOrFail(self, "Null-pointer in Class.newInstance."); return; } - mirror::Class* klass = param->AsClass(); - Handle h_klass(hs.NewHandle(klass)); + Handle h_klass(hs.NewHandle(param->AsClass())); // Check that it's not null. if (h_klass == nullptr) { @@ -280,7 +279,7 @@ void UnstartedRuntime::UnstartedClassNewInstance( // If we're in a transaction, class must not be finalizable (it or a superclass has a finalizer). if (Runtime::Current()->IsActiveTransaction()) { - if (h_klass.Get()->IsFinalizable()) { + if (h_klass->IsFinalizable()) { AbortTransactionF(self, "Class for newInstance is finalizable: '%s'", h_klass->PrettyClass().c_str()); return; @@ -299,7 +298,7 @@ void UnstartedRuntime::UnstartedClassNewInstance( cons = nullptr; } if (cons != nullptr) { - Handle h_obj(hs.NewHandle(klass->AllocObject(self))); + Handle h_obj(hs.NewHandle(h_klass->AllocObject(self))); CHECK(h_obj != nullptr); // We don't expect OOM at compile-time. EnterInterpreterFromInvoke(self, cons, h_obj.Get(), nullptr, nullptr); if (!self->IsExceptionPending()) { @@ -323,8 +322,8 @@ void UnstartedRuntime::UnstartedClassGetDeclaredField( Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { // Special managed code cut-out to allow field lookup in a un-started runtime that'd fail // going the reflective Dex way. - mirror::Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); - mirror::String* name2 = shadow_frame->GetVRegReference(arg_offset + 1)->AsString(); + ObjPtr klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); + ObjPtr name2 = shadow_frame->GetVRegReference(arg_offset + 1)->AsString(); ArtField* found = nullptr; for (ArtField& field : klass->GetIFields()) { if (name2->Equals(field.GetName())) { @@ -376,13 +375,13 @@ void UnstartedRuntime::UnstartedClassGetDeclaredField( void UnstartedRuntime::UnstartedClassGetDeclaredMethod( Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { // Special managed code cut-out to allow method lookup in a un-started runtime. - mirror::Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); + ObjPtr klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); if (klass == nullptr) { ThrowNullPointerExceptionForMethodAccess(shadow_frame->GetMethod(), InvokeType::kVirtual); return; } - mirror::String* name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString(); - mirror::ObjectArray* args = + ObjPtr name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString(); + ObjPtr> args = shadow_frame->GetVRegReference(arg_offset + 2)->AsObjectArray(); Runtime* runtime = Runtime::Current(); bool transaction = runtime->IsActiveTransaction(); @@ -414,7 +413,7 @@ void UnstartedRuntime::UnstartedClassGetDeclaredMethod( // Special managed code cut-out to allow constructor lookup in a un-started runtime. void UnstartedRuntime::UnstartedClassGetDeclaredConstructor( Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { - mirror::Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); + ObjPtr klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); if (klass == nullptr) { ThrowNullPointerExceptionForMethodAccess(shadow_frame->GetMethod(), InvokeType::kVirtual); return; @@ -510,7 +509,7 @@ void UnstartedRuntime::UnstartedClassIsAnonymousClass( result->SetZ(false); return; } - mirror::String* class_name = nullptr; + ObjPtr class_name = nullptr; if (!annotations::GetInnerClass(klass, &class_name)) { result->SetZ(false); return; @@ -830,12 +829,12 @@ void UnstartedRuntime::UnstartedSystemArraycopy( } // Type checking. - mirror::Class* src_type = shadow_frame->GetVRegReference(arg_offset)->GetClass()-> + ObjPtr src_type = shadow_frame->GetVRegReference(arg_offset)->GetClass()-> GetComponentType(); if (!src_type->IsPrimitive()) { // Check that the second type is not primitive. - mirror::Class* trg_type = shadow_frame->GetVRegReference(arg_offset + 2)->GetClass()-> + ObjPtr trg_type = shadow_frame->GetVRegReference(arg_offset + 2)->GetClass()-> GetComponentType(); if (trg_type->IsPrimitiveInt()) { AbortTransactionOrFail(self, "Type mismatch in arraycopy: %s vs %s", @@ -1486,13 +1485,17 @@ void UnstartedRuntime::UnstartedUnsafeCompareAndSwapObject( bool success; // Check whether we're in a transaction, call accordingly. if (Runtime::Current()->IsActiveTransaction()) { - success = obj->CasFieldStrongSequentiallyConsistentObject(MemberOffset(offset), - expected_value, - newValue); + success = obj->CasFieldObject(MemberOffset(offset), + expected_value, + newValue, + CASMode::kStrong, + std::memory_order_seq_cst); } else { - success = obj->CasFieldStrongSequentiallyConsistentObject(MemberOffset(offset), - expected_value, - newValue); + success = obj->CasFieldObject(MemberOffset(offset), + expected_value, + newValue, + CASMode::kStrong, + std::memory_order_seq_cst); } result->SetZ(success ? 1 : 0); } @@ -1540,7 +1543,7 @@ void UnstartedRuntime::UnstartedUnsafePutOrderedObject( } int64_t offset = shadow_frame->GetVRegLong(arg_offset + 2); mirror::Object* newValue = shadow_frame->GetVRegReference(arg_offset + 4); - QuasiAtomic::ThreadFenceRelease(); + std::atomic_thread_fence(std::memory_order_release); if (Runtime::Current()->IsActiveTransaction()) { obj->SetFieldObject(MemberOffset(offset), newValue); } else { @@ -1692,7 +1695,7 @@ void UnstartedRuntime::UnstartedJNIVMRuntimeNewUnpaddedArray( ObjPtr element_class = reinterpret_cast(args[0])->AsClass(); Runtime* runtime = Runtime::Current(); ObjPtr array_class = - runtime->GetClassLinker()->FindArrayClass(self, &element_class); + runtime->GetClassLinker()->FindArrayClass(self, element_class); DCHECK(array_class != nullptr); gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentAllocator(); result->SetL(mirror::Array::Alloc(self, @@ -1819,13 +1822,13 @@ void UnstartedRuntime::UnstartedJNIArrayCreateObjectArray( ObjPtr element_class = reinterpret_cast(args[0])->AsClass(); Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); - ObjPtr array_class = class_linker->FindArrayClass(self, &element_class); + ObjPtr array_class = class_linker->FindArrayClass(self, element_class); if (UNLIKELY(array_class == nullptr)) { CHECK(self->IsExceptionPending()); return; } DCHECK(array_class->IsObjectArrayClass()); - mirror::Array* new_array = mirror::ObjectArray::Alloc( + ObjPtr new_array = mirror::ObjectArray::Alloc( self, array_class, length, runtime->GetHeap()->GetCurrentAllocator()); result->SetL(new_array); } @@ -1856,11 +1859,17 @@ void UnstartedRuntime::UnstartedJNIUnsafeCompareAndSwapInt( jint newValue = args[4]; bool success; if (Runtime::Current()->IsActiveTransaction()) { - success = obj->CasFieldStrongSequentiallyConsistent32(MemberOffset(offset), - expectedValue, newValue); + success = obj->CasField32(MemberOffset(offset), + expectedValue, + newValue, + CASMode::kStrong, + std::memory_order_seq_cst); } else { - success = obj->CasFieldStrongSequentiallyConsistent32(MemberOffset(offset), - expectedValue, newValue); + success = obj->CasField32(MemberOffset(offset), + expectedValue, + newValue, + CASMode::kStrong, + std::memory_order_seq_cst); } result->SetZ(success ? JNI_TRUE : JNI_FALSE); } @@ -1894,7 +1903,7 @@ void UnstartedRuntime::UnstartedJNIUnsafePutObject( void UnstartedRuntime::UnstartedJNIUnsafeGetArrayBaseOffsetForComponentType( Thread* self ATTRIBUTE_UNUSED, ArtMethod* method ATTRIBUTE_UNUSED, mirror::Object* receiver ATTRIBUTE_UNUSED, uint32_t* args, JValue* result) { - mirror::Class* component = reinterpret_cast(args[0])->AsClass(); + ObjPtr component = reinterpret_cast(args[0])->AsClass(); Primitive::Type primitive_type = component->GetPrimitiveType(); result->SetI(mirror::Array::DataOffset(Primitive::ComponentSize(primitive_type)).Int32Value()); } @@ -1902,7 +1911,7 @@ void UnstartedRuntime::UnstartedJNIUnsafeGetArrayBaseOffsetForComponentType( void UnstartedRuntime::UnstartedJNIUnsafeGetArrayIndexScaleForComponentType( Thread* self ATTRIBUTE_UNUSED, ArtMethod* method ATTRIBUTE_UNUSED, mirror::Object* receiver ATTRIBUTE_UNUSED, uint32_t* args, JValue* result) { - mirror::Class* component = reinterpret_cast(args[0])->AsClass(); + ObjPtr component = reinterpret_cast(args[0])->AsClass(); Primitive::Type primitive_type = component->GetPrimitiveType(); result->SetI(Primitive::ComponentSize(primitive_type)); } diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc index fd435627bf0522c0ac6d78c70afccfee2e13ea85..200fc5b3342c127953994ed0add86dff2569063c 100644 --- a/runtime/interpreter/unstarted_runtime_test.cc +++ b/runtime/interpreter/unstarted_runtime_test.cc @@ -23,6 +23,7 @@ #include "base/enums.h" #include "base/memory_tool.h" #include "class_linker.h" +#include "class_root.h" #include "common_runtime_test.h" #include "dex/descriptors_names.h" #include "dex/dex_instruction.h" @@ -35,12 +36,25 @@ #include "mirror/string-inl.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" +#include "shadow_frame-inl.h" #include "thread.h" #include "transaction.h" namespace art { namespace interpreter { +// Deleter to be used with ShadowFrame::CreateDeoptimizedFrame objects. +struct DeoptShadowFrameDelete { + // NOTE: Deleting a const object is valid but free() takes a non-const pointer. + void operator()(ShadowFrame* ptr) const { + if (ptr != nullptr) { + ShadowFrame::DeleteDeoptimizedFrame(ptr); + } + } +}; +// Alias for std::unique_ptr<> that uses the above deleter. +using UniqueDeoptShadowFramePtr = std::unique_ptr; + class UnstartedRuntimeTest : public CommonRuntimeTest { protected: // Re-expose all UnstartedRuntime implementations so we don't need to declare a million @@ -77,19 +91,27 @@ class UnstartedRuntimeTest : public CommonRuntimeTest { #undef UNSTARTED_RUNTIME_JNI_LIST #undef UNSTARTED_JNI + UniqueDeoptShadowFramePtr CreateShadowFrame(uint32_t num_vregs, + ShadowFrame* link, + ArtMethod* method, + uint32_t dex_pc) { + return UniqueDeoptShadowFramePtr( + ShadowFrame::CreateDeoptimizedFrame(num_vregs, link, method, dex_pc)); + } + // Helpers for ArrayCopy. // // Note: as we have to use handles, we use StackHandleScope to transfer data. Hardcode a size // of three everywhere. That is enough to test all cases. - static mirror::ObjectArray* CreateObjectArray( + static ObjPtr> CreateObjectArray( Thread* self, ObjPtr component_type, const StackHandleScope<3>& data) REQUIRES_SHARED(Locks::mutator_lock_) { Runtime* runtime = Runtime::Current(); ObjPtr array_type = - runtime->GetClassLinker()->FindArrayClass(self, &component_type); + runtime->GetClassLinker()->FindArrayClass(self, component_type); CHECK(array_type != nullptr); ObjPtr> result = mirror::ObjectArray::Alloc(self, array_type, 3); @@ -98,10 +120,10 @@ class UnstartedRuntimeTest : public CommonRuntimeTest { result->Set(static_cast(i), data.GetReference(i)); CHECK(!self->IsExceptionPending()); } - return result.Ptr(); + return result; } - static void CheckObjectArray(mirror::ObjectArray* array, + static void CheckObjectArray(ObjPtr> array, const StackHandleScope<3>& data) REQUIRES_SHARED(Locks::mutator_lock_) { CHECK_EQ(array->GetLength(), 3); @@ -114,9 +136,9 @@ class UnstartedRuntimeTest : public CommonRuntimeTest { void RunArrayCopy(Thread* self, ShadowFrame* tmp, bool expect_exception, - mirror::ObjectArray* src, + ObjPtr> src, int32_t src_pos, - mirror::ObjectArray* dst, + ObjPtr> dst, int32_t dst_pos, int32_t length) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -137,8 +159,8 @@ class UnstartedRuntimeTest : public CommonRuntimeTest { void RunArrayCopy(Thread* self, ShadowFrame* tmp, bool expect_exception, - mirror::Class* src_component_class, - mirror::Class* dst_component_class, + ObjPtr src_component_class, + ObjPtr dst_component_class, const StackHandleScope<3>& src_data, int32_t src_pos, const StackHandleScope<3>& dst_data, @@ -194,7 +216,7 @@ class UnstartedRuntimeTest : public CommonRuntimeTest { // Prepare for aborts. Aborts assume that the exception class is already resolved, as the // loading code doesn't work under transactions. void PrepareForAborts() REQUIRES_SHARED(Locks::mutator_lock_) { - mirror::Object* result = Runtime::Current()->GetClassLinker()->FindClass( + ObjPtr result = Runtime::Current()->GetClassLinker()->FindClass( Thread::Current(), Transaction::kAbortExceptionSignature, ScopedNullHandle()); @@ -211,17 +233,15 @@ TEST_F(UnstartedRuntimeTest, MemoryPeekByte) { const uint8_t* base_ptr = base_array; JValue result; - ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0); for (int32_t i = 0; i < kBaseLen; ++i) { tmp->SetVRegLong(0, static_cast(reinterpret_cast(base_ptr + i))); - UnstartedMemoryPeekByte(self, tmp, &result, 0); + UnstartedMemoryPeekByte(self, tmp.get(), &result, 0); EXPECT_EQ(result.GetB(), static_cast(base_array[i])); } - - ShadowFrame::DeleteDeoptimizedFrame(tmp); } TEST_F(UnstartedRuntimeTest, MemoryPeekShort) { @@ -233,20 +253,18 @@ TEST_F(UnstartedRuntimeTest, MemoryPeekShort) { const uint8_t* base_ptr = base_array; JValue result; - ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0); int32_t adjusted_length = kBaseLen - sizeof(int16_t); for (int32_t i = 0; i < adjusted_length; ++i) { tmp->SetVRegLong(0, static_cast(reinterpret_cast(base_ptr + i))); - UnstartedMemoryPeekShort(self, tmp, &result, 0); + UnstartedMemoryPeekShort(self, tmp.get(), &result, 0); typedef int16_t unaligned_short __attribute__ ((aligned (1))); const unaligned_short* short_ptr = reinterpret_cast(base_ptr + i); EXPECT_EQ(result.GetS(), *short_ptr); } - - ShadowFrame::DeleteDeoptimizedFrame(tmp); } TEST_F(UnstartedRuntimeTest, MemoryPeekInt) { @@ -258,20 +276,18 @@ TEST_F(UnstartedRuntimeTest, MemoryPeekInt) { const uint8_t* base_ptr = base_array; JValue result; - ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0); int32_t adjusted_length = kBaseLen - sizeof(int32_t); for (int32_t i = 0; i < adjusted_length; ++i) { tmp->SetVRegLong(0, static_cast(reinterpret_cast(base_ptr + i))); - UnstartedMemoryPeekInt(self, tmp, &result, 0); + UnstartedMemoryPeekInt(self, tmp.get(), &result, 0); typedef int32_t unaligned_int __attribute__ ((aligned (1))); const unaligned_int* int_ptr = reinterpret_cast(base_ptr + i); EXPECT_EQ(result.GetI(), *int_ptr); } - - ShadowFrame::DeleteDeoptimizedFrame(tmp); } TEST_F(UnstartedRuntimeTest, MemoryPeekLong) { @@ -283,20 +299,18 @@ TEST_F(UnstartedRuntimeTest, MemoryPeekLong) { const uint8_t* base_ptr = base_array; JValue result; - ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0); int32_t adjusted_length = kBaseLen - sizeof(int64_t); for (int32_t i = 0; i < adjusted_length; ++i) { tmp->SetVRegLong(0, static_cast(reinterpret_cast(base_ptr + i))); - UnstartedMemoryPeekLong(self, tmp, &result, 0); + UnstartedMemoryPeekLong(self, tmp.get(), &result, 0); typedef int64_t unaligned_long __attribute__ ((aligned (1))); const unaligned_long* long_ptr = reinterpret_cast(base_ptr + i); EXPECT_EQ(result.GetJ(), *long_ptr); } - - ShadowFrame::DeleteDeoptimizedFrame(tmp); } TEST_F(UnstartedRuntimeTest, StringGetCharsNoCheck) { @@ -315,7 +329,7 @@ TEST_F(UnstartedRuntimeTest, StringGetCharsNoCheck) { uint16_t buf[kBaseLen]; JValue result; - ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0); for (int32_t start_index = 0; start_index < kBaseLen; ++start_index) { for (int32_t count = 0; count <= kBaseLen; ++count) { @@ -331,7 +345,7 @@ TEST_F(UnstartedRuntimeTest, StringGetCharsNoCheck) { // Copy the char_array into buf. memcpy(buf, h_char_array->GetData(), kBaseLen * sizeof(uint16_t)); - UnstartedStringCharAt(self, tmp, &result, 0); + UnstartedStringCharAt(self, tmp.get(), &result, 0); uint16_t* data = h_char_array->GetData(); @@ -355,8 +369,6 @@ TEST_F(UnstartedRuntimeTest, StringGetCharsNoCheck) { } } } - - ShadowFrame::DeleteDeoptimizedFrame(tmp); } TEST_F(UnstartedRuntimeTest, StringCharAt) { @@ -366,27 +378,25 @@ TEST_F(UnstartedRuntimeTest, StringCharAt) { // TODO: Actual UTF. constexpr const char* base_string = "abcdefghijklmnop"; int32_t base_len = static_cast(strlen(base_string)); - mirror::String* test_string = mirror::String::AllocFromModifiedUtf8(self, base_string); + ObjPtr test_string = mirror::String::AllocFromModifiedUtf8(self, base_string); JValue result; - ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0); for (int32_t i = 0; i < base_len; ++i) { tmp->SetVRegReference(0, test_string); tmp->SetVReg(1, i); - UnstartedStringCharAt(self, tmp, &result, 0); + UnstartedStringCharAt(self, tmp.get(), &result, 0); EXPECT_EQ(result.GetI(), base_string[i]); } - - ShadowFrame::DeleteDeoptimizedFrame(tmp); } TEST_F(UnstartedRuntimeTest, StringInit) { Thread* self = Thread::Current(); ScopedObjectAccess soa(self); - mirror::Class* klass = mirror::String::GetJavaLangString(); + ObjPtr klass = GetClassRoot(); ArtMethod* method = klass->FindConstructor("(Ljava/lang/String;)V", Runtime::Current()->GetClassLinker()->GetImagePointerSize()); @@ -396,12 +406,15 @@ TEST_F(UnstartedRuntimeTest, StringInit) { uint16_t inst_data[3] = { 0x2070, 0x0000, 0x0010 }; JValue result; - ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, method, 0); + UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, method, 0); const char* base_string = "hello_world"; - mirror::String* string_arg = mirror::String::AllocFromModifiedUtf8(self, base_string); - mirror::String* reference_empty_string = mirror::String::AllocFromModifiedUtf8(self, ""); - shadow_frame->SetVRegReference(0, reference_empty_string); - shadow_frame->SetVRegReference(1, string_arg); + StackHandleScope<2> hs(self); + Handle string_arg = + hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, base_string)); + Handle reference_empty_string = + hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "")); + shadow_frame->SetVRegReference(0, reference_empty_string.Get()); + shadow_frame->SetVRegReference(1, string_arg.Get()); interpreter::DoCall(method, self, @@ -409,7 +422,7 @@ TEST_F(UnstartedRuntimeTest, StringInit) { Instruction::At(inst_data), inst_data[0], &result); - mirror::String* string_result = reinterpret_cast(result.GetL()); + ObjPtr string_result = down_cast(result.GetL()); EXPECT_EQ(string_arg->GetLength(), string_result->GetLength()); if (string_arg->IsCompressed() && string_result->IsCompressed()) { @@ -428,8 +441,6 @@ TEST_F(UnstartedRuntimeTest, StringInit) { } EXPECT_EQ(equal, true); } - - ShadowFrame::DeleteDeoptimizedFrame(shadow_frame); } // Tests the exceptions that should be checked before modifying the destination. @@ -438,13 +449,12 @@ TEST_F(UnstartedRuntimeTest, SystemArrayCopyObjectArrayTestExceptions) { Thread* self = Thread::Current(); ScopedObjectAccess soa(self); JValue result; - ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0); // Note: all tests are not GC safe. Assume there's no GC running here with the few objects we // allocate. - StackHandleScope<2> hs_misc(self); - Handle object_class( - hs_misc.NewHandle(mirror::Class::GetJavaLangClass()->GetSuperClass())); + StackHandleScope<3> hs_misc(self); + Handle object_class(hs_misc.NewHandle(GetClassRoot())); StackHandleScope<3> hs_data(self); hs_data.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "1")); @@ -454,30 +464,27 @@ TEST_F(UnstartedRuntimeTest, SystemArrayCopyObjectArrayTestExceptions) { Handle> array( hs_misc.NewHandle(CreateObjectArray(self, object_class.Get(), hs_data))); - RunArrayCopy(self, tmp, true, array.Get(), -1, array.Get(), 0, 0); - RunArrayCopy(self, tmp, true, array.Get(), 0, array.Get(), -1, 0); - RunArrayCopy(self, tmp, true, array.Get(), 0, array.Get(), 0, -1); - RunArrayCopy(self, tmp, true, array.Get(), 0, array.Get(), 0, 4); - RunArrayCopy(self, tmp, true, array.Get(), 0, array.Get(), 1, 3); - RunArrayCopy(self, tmp, true, array.Get(), 1, array.Get(), 0, 3); - - mirror::ObjectArray* class_as_array = - reinterpret_cast*>(object_class.Get()); - RunArrayCopy(self, tmp, true, class_as_array, 0, array.Get(), 0, 0); - RunArrayCopy(self, tmp, true, array.Get(), 0, class_as_array, 0, 0); - - ShadowFrame::DeleteDeoptimizedFrame(tmp); + RunArrayCopy(self, tmp.get(), true, array.Get(), -1, array.Get(), 0, 0); + RunArrayCopy(self, tmp.get(), true, array.Get(), 0, array.Get(), -1, 0); + RunArrayCopy(self, tmp.get(), true, array.Get(), 0, array.Get(), 0, -1); + RunArrayCopy(self, tmp.get(), true, array.Get(), 0, array.Get(), 0, 4); + RunArrayCopy(self, tmp.get(), true, array.Get(), 0, array.Get(), 1, 3); + RunArrayCopy(self, tmp.get(), true, array.Get(), 1, array.Get(), 0, 3); + + Handle> class_as_array = + hs_misc.NewHandle(reinterpret_cast*>(object_class.Get())); + RunArrayCopy(self, tmp.get(), true, class_as_array.Get(), 0, array.Get(), 0, 0); + RunArrayCopy(self, tmp.get(), true, array.Get(), 0, class_as_array.Get(), 0, 0); } TEST_F(UnstartedRuntimeTest, SystemArrayCopyObjectArrayTest) { Thread* self = Thread::Current(); ScopedObjectAccess soa(self); JValue result; - ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0); StackHandleScope<1> hs_object(self); - Handle object_class( - hs_object.NewHandle(mirror::Class::GetJavaLangClass()->GetSuperClass())); + Handle object_class(hs_object.NewHandle(GetClassRoot())); // Simple test: // [1,2,3]{1 @ 2} into [4,5,6] = [4,2,6] @@ -498,7 +505,7 @@ TEST_F(UnstartedRuntimeTest, SystemArrayCopyObjectArrayTest) { hs_expected.NewHandle(hs_src.GetReference(1)); RunArrayCopy(self, - tmp, + tmp.get(), false, object_class.Get(), object_class.Get(), @@ -529,10 +536,10 @@ TEST_F(UnstartedRuntimeTest, SystemArrayCopyObjectArrayTest) { hs_expected.NewHandle(hs_dst.GetReference(2)); RunArrayCopy(self, - tmp, + tmp.get(), false, object_class.Get(), - mirror::String::GetJavaLangString(), + GetClassRoot(), hs_src, 1, hs_dst, @@ -546,7 +553,7 @@ TEST_F(UnstartedRuntimeTest, SystemArrayCopyObjectArrayTest) { { StackHandleScope<3> hs_src(self); hs_src.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "1")); - hs_src.NewHandle(mirror::String::GetJavaLangString()); + hs_src.NewHandle(GetClassRoot()); hs_src.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "3")); StackHandleScope<3> hs_dst(self); @@ -560,10 +567,10 @@ TEST_F(UnstartedRuntimeTest, SystemArrayCopyObjectArrayTest) { hs_expected.NewHandle(hs_dst.GetReference(2)); RunArrayCopy(self, - tmp, + tmp.get(), true, object_class.Get(), - mirror::String::GetJavaLangString(), + GetClassRoot(), hs_src, 0, hs_dst, @@ -571,15 +578,13 @@ TEST_F(UnstartedRuntimeTest, SystemArrayCopyObjectArrayTest) { 3, hs_expected); } - - ShadowFrame::DeleteDeoptimizedFrame(tmp); } TEST_F(UnstartedRuntimeTest, IntegerParseIntTest) { Thread* self = Thread::Current(); ScopedObjectAccess soa(self); - ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0); // Test string. Should be valid, and between minimal values of LONG_MIN and LONG_MAX (for all // suffixes). @@ -613,13 +618,11 @@ TEST_F(UnstartedRuntimeTest, IntegerParseIntTest) { tmp->SetVRegReference(0, h_str.Get()); JValue result; - UnstartedIntegerParseInt(self, tmp, &result, 0); + UnstartedIntegerParseInt(self, tmp.get(), &result, 0); ASSERT_FALSE(self->IsExceptionPending()); EXPECT_EQ(result.GetI(), test_values[i]); } - - ShadowFrame::DeleteDeoptimizedFrame(tmp); } // Right now the same as Integer.Parse @@ -627,7 +630,7 @@ TEST_F(UnstartedRuntimeTest, LongParseLongTest) { Thread* self = Thread::Current(); ScopedObjectAccess soa(self); - ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0); // Test string. Should be valid, and between minimal values of LONG_MIN and LONG_MAX (for all // suffixes). @@ -661,20 +664,18 @@ TEST_F(UnstartedRuntimeTest, LongParseLongTest) { tmp->SetVRegReference(0, h_str.Get()); JValue result; - UnstartedLongParseLong(self, tmp, &result, 0); + UnstartedLongParseLong(self, tmp.get(), &result, 0); ASSERT_FALSE(self->IsExceptionPending()); EXPECT_EQ(result.GetJ(), test_values[i]); } - - ShadowFrame::DeleteDeoptimizedFrame(tmp); } TEST_F(UnstartedRuntimeTest, Ceil) { Thread* self = Thread::Current(); ScopedObjectAccess soa(self); - ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0); constexpr double nan = std::numeric_limits::quiet_NaN(); constexpr double inf = std::numeric_limits::infinity(); @@ -694,16 +695,14 @@ TEST_F(UnstartedRuntimeTest, Ceil) { { ld2, ld2 } }; - TestCeilFloor(true /* ceil */, self, tmp, test_pairs, arraysize(test_pairs)); - - ShadowFrame::DeleteDeoptimizedFrame(tmp); + TestCeilFloor(true /* ceil */, self, tmp.get(), test_pairs, arraysize(test_pairs)); } TEST_F(UnstartedRuntimeTest, Floor) { Thread* self = Thread::Current(); ScopedObjectAccess soa(self); - ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0); constexpr double nan = std::numeric_limits::quiet_NaN(); constexpr double inf = std::numeric_limits::infinity(); @@ -723,16 +722,14 @@ TEST_F(UnstartedRuntimeTest, Floor) { { ld2, ld2 } }; - TestCeilFloor(false /* floor */, self, tmp, test_pairs, arraysize(test_pairs)); - - ShadowFrame::DeleteDeoptimizedFrame(tmp); + TestCeilFloor(false /* floor */, self, tmp.get(), test_pairs, arraysize(test_pairs)); } TEST_F(UnstartedRuntimeTest, ToLowerUpper) { Thread* self = Thread::Current(); ScopedObjectAccess soa(self); - ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0); std::locale c_locale("C"); @@ -746,7 +743,7 @@ TEST_F(UnstartedRuntimeTest, ToLowerUpper) { { JValue result; tmp->SetVReg(0, static_cast(i)); - UnstartedCharacterToLowerCase(self, tmp, &result, 0); + UnstartedCharacterToLowerCase(self, tmp.get(), &result, 0); ASSERT_FALSE(self->IsExceptionPending()); uint32_t lower_result = static_cast(result.GetI()); if (c_lower) { @@ -763,7 +760,7 @@ TEST_F(UnstartedRuntimeTest, ToLowerUpper) { { JValue result2; tmp->SetVReg(0, static_cast(i)); - UnstartedCharacterToUpperCase(self, tmp, &result2, 0); + UnstartedCharacterToUpperCase(self, tmp.get(), &result2, 0); ASSERT_FALSE(self->IsExceptionPending()); uint32_t upper_result = static_cast(result2.GetI()); if (c_upper) { @@ -786,7 +783,7 @@ TEST_F(UnstartedRuntimeTest, ToLowerUpper) { JValue result; tmp->SetVReg(0, static_cast(i)); Runtime::Current()->EnterTransactionMode(); - UnstartedCharacterToLowerCase(self, tmp, &result, 0); + UnstartedCharacterToLowerCase(self, tmp.get(), &result, 0); ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(self->IsExceptionPending()); @@ -795,7 +792,7 @@ TEST_F(UnstartedRuntimeTest, ToLowerUpper) { JValue result; tmp->SetVReg(0, static_cast(i)); Runtime::Current()->EnterTransactionMode(); - UnstartedCharacterToUpperCase(self, tmp, &result, 0); + UnstartedCharacterToUpperCase(self, tmp.get(), &result, 0); ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(self->IsExceptionPending()); @@ -806,7 +803,7 @@ TEST_F(UnstartedRuntimeTest, ToLowerUpper) { JValue result; tmp->SetVReg(0, static_cast(i)); Runtime::Current()->EnterTransactionMode(); - UnstartedCharacterToLowerCase(self, tmp, &result, 0); + UnstartedCharacterToLowerCase(self, tmp.get(), &result, 0); ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(self->IsExceptionPending()); @@ -815,64 +812,53 @@ TEST_F(UnstartedRuntimeTest, ToLowerUpper) { JValue result; tmp->SetVReg(0, static_cast(i)); Runtime::Current()->EnterTransactionMode(); - UnstartedCharacterToUpperCase(self, tmp, &result, 0); + UnstartedCharacterToUpperCase(self, tmp.get(), &result, 0); ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(self->IsExceptionPending()); } } - - ShadowFrame::DeleteDeoptimizedFrame(tmp); } TEST_F(UnstartedRuntimeTest, Sin) { Thread* self = Thread::Current(); ScopedObjectAccess soa(self); - ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0); // Test an important value, PI/6. That's the one we see in practice. constexpr uint64_t lvalue = UINT64_C(0x3fe0c152382d7365); tmp->SetVRegLong(0, static_cast(lvalue)); JValue result; - UnstartedMathSin(self, tmp, &result, 0); + UnstartedMathSin(self, tmp.get(), &result, 0); const uint64_t lresult = static_cast(result.GetJ()); EXPECT_EQ(UINT64_C(0x3fdfffffffffffff), lresult); - - ShadowFrame::DeleteDeoptimizedFrame(tmp); } TEST_F(UnstartedRuntimeTest, Cos) { Thread* self = Thread::Current(); ScopedObjectAccess soa(self); - ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0); // Test an important value, PI/6. That's the one we see in practice. constexpr uint64_t lvalue = UINT64_C(0x3fe0c152382d7365); tmp->SetVRegLong(0, static_cast(lvalue)); JValue result; - UnstartedMathCos(self, tmp, &result, 0); + UnstartedMathCos(self, tmp.get(), &result, 0); const uint64_t lresult = static_cast(result.GetJ()); EXPECT_EQ(UINT64_C(0x3febb67ae8584cab), lresult); - - ShadowFrame::DeleteDeoptimizedFrame(tmp); } 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); - ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0); // Test an important pair. constexpr uint64_t lvalue1 = UINT64_C(0x4079000000000000); @@ -882,12 +868,10 @@ TEST_F(UnstartedRuntimeTest, Pow) { tmp->SetVRegLong(2, static_cast(lvalue2)); JValue result; - UnstartedMathPow(self, tmp, &result, 0); + UnstartedMathPow(self, tmp.get(), &result, 0); const uint64_t lresult = static_cast(result.GetJ()); EXPECT_EQ(UINT64_C(0x3f8c5c51326aa7ee), lresult); - - ShadowFrame::DeleteDeoptimizedFrame(tmp); } TEST_F(UnstartedRuntimeTest, IsAnonymousClass) { @@ -895,24 +879,22 @@ TEST_F(UnstartedRuntimeTest, IsAnonymousClass) { ScopedObjectAccess soa(self); JValue result; - ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0); - mirror::Class* class_klass = mirror::Class::GetJavaLangClass(); + ObjPtr class_klass = GetClassRoot(); shadow_frame->SetVRegReference(0, class_klass); - UnstartedClassIsAnonymousClass(self, shadow_frame, &result, 0); + UnstartedClassIsAnonymousClass(self, shadow_frame.get(), &result, 0); EXPECT_EQ(result.GetZ(), 0); jobject class_loader = LoadDex("Nested"); StackHandleScope<1> hs(soa.Self()); Handle loader( hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* c = class_linker_->FindClass(soa.Self(), "LNested$1;", loader); + ObjPtr c = class_linker_->FindClass(soa.Self(), "LNested$1;", loader); ASSERT_TRUE(c != nullptr); shadow_frame->SetVRegReference(0, c); - UnstartedClassIsAnonymousClass(self, shadow_frame, &result, 0); + UnstartedClassIsAnonymousClass(self, shadow_frame.get(), &result, 0); EXPECT_EQ(result.GetZ(), 1); - - ShadowFrame::DeleteDeoptimizedFrame(shadow_frame); } TEST_F(UnstartedRuntimeTest, GetDeclaringClass) { @@ -920,7 +902,7 @@ TEST_F(UnstartedRuntimeTest, GetDeclaringClass) { ScopedObjectAccess soa(self); JValue result; - ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0); jobject class_loader = LoadDex("Nested"); StackHandleScope<4> hs(self); @@ -935,18 +917,16 @@ TEST_F(UnstartedRuntimeTest, GetDeclaringClass) { class_linker_->FindClass(soa.Self(), "LNested$1;", loader))); shadow_frame->SetVRegReference(0, nested_klass.Get()); - UnstartedClassGetDeclaringClass(self, shadow_frame, &result, 0); + UnstartedClassGetDeclaringClass(self, shadow_frame.get(), &result, 0); EXPECT_EQ(result.GetL(), nullptr); shadow_frame->SetVRegReference(0, inner_klass.Get()); - UnstartedClassGetDeclaringClass(self, shadow_frame, &result, 0); + UnstartedClassGetDeclaringClass(self, shadow_frame.get(), &result, 0); EXPECT_EQ(result.GetL(), nested_klass.Get()); shadow_frame->SetVRegReference(0, anon_klass.Get()); - UnstartedClassGetDeclaringClass(self, shadow_frame, &result, 0); + UnstartedClassGetDeclaringClass(self, shadow_frame.get(), &result, 0); EXPECT_EQ(result.GetL(), nullptr); - - ShadowFrame::DeleteDeoptimizedFrame(shadow_frame); } TEST_F(UnstartedRuntimeTest, ThreadLocalGet) { @@ -954,7 +934,7 @@ TEST_F(UnstartedRuntimeTest, ThreadLocalGet) { ScopedObjectAccess soa(self); JValue result; - ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0); StackHandleScope<1> hs(self); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); @@ -976,14 +956,14 @@ TEST_F(UnstartedRuntimeTest, ThreadLocalGet) { ASSERT_TRUE(caller_method != nullptr); ASSERT_TRUE(caller_method->IsDirect()); ASSERT_TRUE(caller_method->GetDeclaringClass() == floating_decimal.Get()); - ShadowFrame* caller_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, caller_method, 0); - shadow_frame->SetLink(caller_frame); + UniqueDeoptShadowFramePtr caller_frame = CreateShadowFrame(10, nullptr, caller_method, 0); + shadow_frame->SetLink(caller_frame.get()); - UnstartedThreadLocalGet(self, shadow_frame, &result, 0); + UnstartedThreadLocalGet(self, shadow_frame.get(), &result, 0); EXPECT_TRUE(result.GetL() != nullptr); EXPECT_FALSE(self->IsExceptionPending()); - ShadowFrame::DeleteDeoptimizedFrame(caller_frame); + shadow_frame->SetLink(nullptr); } // Negative test. @@ -991,23 +971,21 @@ TEST_F(UnstartedRuntimeTest, ThreadLocalGet) { { // Just use a method in Class. - ObjPtr class_class = mirror::Class::GetJavaLangClass(); + ObjPtr class_class = GetClassRoot(); ArtMethod* caller_method = &*class_class->GetDeclaredMethods(class_linker->GetImagePointerSize()).begin(); - ShadowFrame* caller_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, caller_method, 0); - shadow_frame->SetLink(caller_frame); + UniqueDeoptShadowFramePtr caller_frame = CreateShadowFrame(10, nullptr, caller_method, 0); + shadow_frame->SetLink(caller_frame.get()); Runtime::Current()->EnterTransactionMode(); - UnstartedThreadLocalGet(self, shadow_frame, &result, 0); + UnstartedThreadLocalGet(self, shadow_frame.get(), &result, 0); ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(self->IsExceptionPending()); self->ClearException(); - ShadowFrame::DeleteDeoptimizedFrame(caller_frame); + shadow_frame->SetLink(nullptr); } - - ShadowFrame::DeleteDeoptimizedFrame(shadow_frame); } TEST_F(UnstartedRuntimeTest, FloatConversion) { @@ -1034,7 +1012,8 @@ TEST_F(UnstartedRuntimeTest, FloatConversion) { uint16_t inst_data[3] = { 0x2070, 0x0000, 0x0010 }; JValue result; - ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, method, 0); + UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, method, 0); + shadow_frame->SetVRegDouble(0, 1.23); interpreter::DoCall(method, self, @@ -1042,13 +1021,11 @@ TEST_F(UnstartedRuntimeTest, FloatConversion) { Instruction::At(inst_data), inst_data[0], &result); - ObjPtr string_result = reinterpret_cast(result.GetL()); + ObjPtr string_result = down_cast(result.GetL()); ASSERT_TRUE(string_result != nullptr); std::string mod_utf = string_result->ToModifiedUtf8(); EXPECT_EQ("1.23", mod_utf); - - ShadowFrame::DeleteDeoptimizedFrame(shadow_frame); } TEST_F(UnstartedRuntimeTest, ThreadCurrentThread) { @@ -1056,7 +1033,7 @@ TEST_F(UnstartedRuntimeTest, ThreadCurrentThread) { ScopedObjectAccess soa(self); JValue result; - ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0); StackHandleScope<1> hs(self); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); @@ -1071,14 +1048,12 @@ TEST_F(UnstartedRuntimeTest, ThreadCurrentThread) { { Runtime::Current()->EnterTransactionMode(); - UnstartedThreadCurrentThread(self, shadow_frame, &result, 0); + UnstartedThreadCurrentThread(self, shadow_frame.get(), &result, 0); ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(self->IsExceptionPending()); self->ClearException(); } - - ShadowFrame::DeleteDeoptimizedFrame(shadow_frame); } TEST_F(UnstartedRuntimeTest, LogManager) { @@ -1106,7 +1081,7 @@ class UnstartedClassForNameTest : public UnstartedRuntimeTest { { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); StackHandleScope<1> hs(self); - Handle h_class = hs.NewHandle(mirror::Class::GetJavaLangClass()); + Handle h_class = hs.NewHandle(GetClassRoot()); CHECK(class_linker->EnsureInitialized(self, h_class, true, true)); } @@ -1135,10 +1110,10 @@ class UnstartedClassForNameTest : public UnstartedRuntimeTest { } JValue result; - ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0); for (const char* name : kTestCases) { - mirror::String* name_string = mirror::String::AllocFromModifiedUtf8(self, name); + ObjPtr name_string = mirror::String::AllocFromModifiedUtf8(self, name); CHECK(name_string != nullptr); if (in_transaction) { @@ -1146,7 +1121,7 @@ class UnstartedClassForNameTest : public UnstartedRuntimeTest { } CHECK(!self->IsExceptionPending()); - runner(self, shadow_frame, name_string, &result); + runner(self, shadow_frame.get(), name_string, &result); if (should_succeed) { CHECK(!self->IsExceptionPending()) << name << " " << self->GetException()->Dump(); @@ -1163,8 +1138,6 @@ class UnstartedClassForNameTest : public UnstartedRuntimeTest { Runtime::Current()->ExitTransactionMode(); } } - - ShadowFrame::DeleteDeoptimizedFrame(shadow_frame); } mirror::ClassLoader* GetBootClassLoader() REQUIRES_SHARED(Locks::mutator_lock_) { @@ -1191,7 +1164,7 @@ class UnstartedClassForNameTest : public UnstartedRuntimeTest { CHECK(boot_cp_init != nullptr); JValue result; - ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, boot_cp_init, 0); + UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, boot_cp_init, 0); shadow_frame->SetVRegReference(0, boot_cp.Get()); // create instruction data for invoke-direct {v0} of method with fake index @@ -1204,8 +1177,6 @@ class UnstartedClassForNameTest : public UnstartedRuntimeTest { inst_data[0], &result); CHECK(!self->IsExceptionPending()); - - ShadowFrame::DeleteDeoptimizedFrame(shadow_frame); } return boot_cp.Get(); @@ -1213,8 +1184,10 @@ class UnstartedClassForNameTest : public UnstartedRuntimeTest { }; TEST_F(UnstartedClassForNameTest, ClassForName) { - auto runner = [](Thread* self, ShadowFrame* shadow_frame, mirror::String* name, JValue* result) - REQUIRES_SHARED(Locks::mutator_lock_) { + auto runner = [](Thread* self, + ShadowFrame* shadow_frame, + ObjPtr name, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { shadow_frame->SetVRegReference(0, name); UnstartedClassForName(self, shadow_frame, result, 0); }; @@ -1222,8 +1195,10 @@ TEST_F(UnstartedClassForNameTest, ClassForName) { } TEST_F(UnstartedClassForNameTest, ClassForNameLong) { - auto runner = [](Thread* self, ShadowFrame* shadow_frame, mirror::String* name, JValue* result) - REQUIRES_SHARED(Locks::mutator_lock_) { + auto runner = [](Thread* self, + ShadowFrame* shadow_frame, + ObjPtr name, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { shadow_frame->SetVRegReference(0, name); shadow_frame->SetVReg(1, 0); shadow_frame->SetVRegReference(2, nullptr); @@ -1239,8 +1214,10 @@ TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoader) { StackHandleScope<1> hs(self); Handle boot_cp = hs.NewHandle(GetBootClassLoader()); - auto runner = [&](Thread* th, ShadowFrame* shadow_frame, mirror::String* name, JValue* result) - REQUIRES_SHARED(Locks::mutator_lock_) { + auto runner = [&](Thread* th, + ShadowFrame* shadow_frame, + ObjPtr name, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { shadow_frame->SetVRegReference(0, name); shadow_frame->SetVReg(1, 0); shadow_frame->SetVRegReference(2, boot_cp.Get()); @@ -1256,7 +1233,10 @@ TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoaderTransaction) { StackHandleScope<1> hs(self); Handle boot_cp = hs.NewHandle(GetBootClassLoader()); - auto runner = [&](Thread* th, ShadowFrame* shadow_frame, mirror::String* name, JValue* result) + auto runner = [&](Thread* th, + ShadowFrame* shadow_frame, + ObjPtr name, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { shadow_frame->SetVRegReference(0, name); shadow_frame->SetVReg(1, 0); @@ -1277,8 +1257,10 @@ TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoaderFail) { Handle path_cp = hs.NewHandle( self->DecodeJObject(path_jobj)->AsClassLoader()); - auto runner = [&](Thread* th, ShadowFrame* shadow_frame, mirror::String* name, JValue* result) - REQUIRES_SHARED(Locks::mutator_lock_) { + auto runner = [&](Thread* th, + ShadowFrame* shadow_frame, + ObjPtr name, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { shadow_frame->SetVRegReference(0, name); shadow_frame->SetVReg(1, 0); shadow_frame->SetVRegReference(2, path_cp.Get()); @@ -1301,15 +1283,13 @@ TEST_F(UnstartedRuntimeTest, ClassGetSignatureAnnotation) { ASSERT_TRUE(class_linker->EnsureInitialized(self, list_class, true, true)); JValue result; - ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0); shadow_frame->SetVRegReference(0, list_class.Get()); - UnstartedClassGetSignatureAnnotation(self, shadow_frame, &result, 0); + UnstartedClassGetSignatureAnnotation(self, shadow_frame.get(), &result, 0); ASSERT_TRUE(result.GetL() != nullptr); ASSERT_FALSE(self->IsExceptionPending()); - ShadowFrame::DeleteDeoptimizedFrame(shadow_frame); - ASSERT_TRUE(result.GetL()->IsObjectArray()); ObjPtr> array = result.GetL()->AsObjectArray(); @@ -1332,7 +1312,7 @@ TEST_F(UnstartedRuntimeTest, ConstructorNewInstance0) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); // Get Throwable. - Handle throw_class = hs.NewHandle(mirror::Throwable::GetJavaLangThrowable()); + Handle throw_class = hs.NewHandle(GetClassRoot()); ASSERT_TRUE(class_linker->EnsureInitialized(self, throw_class, true, true)); // Get an input object. @@ -1355,51 +1335,47 @@ TEST_F(UnstartedRuntimeTest, ConstructorNewInstance0) { Handle> args = hs.NewHandle( mirror::ObjectArray::Alloc( - self, class_linker_->GetClassRoot(ClassLinker::ClassRoot::kObjectArrayClass), 1)); + self, GetClassRoot>(class_linker_), 1)); ASSERT_TRUE(args != nullptr); args->Set(0, input.Get()); // OK, we're ready now. JValue result; - ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0); shadow_frame->SetVRegReference(0, cons.Get()); shadow_frame->SetVRegReference(1, args.Get()); - UnstartedConstructorNewInstance0(self, shadow_frame, &result, 0); + UnstartedConstructorNewInstance0(self, shadow_frame.get(), &result, 0); ASSERT_TRUE(result.GetL() != nullptr); ASSERT_FALSE(self->IsExceptionPending()); // Should be a new object. ASSERT_NE(result.GetL(), input.Get()); - // Should be a String. - ASSERT_EQ(mirror::Throwable::GetJavaLangThrowable(), result.GetL()->GetClass()); + // Should be of type Throwable. + ASSERT_OBJ_PTR_EQ(GetClassRoot(), result.GetL()->GetClass()); // Should have the right string. ObjPtr result_msg = reinterpret_cast(result.GetL())->GetDetailMessage(); - EXPECT_EQ(input.Get(), result_msg.Ptr()); - - ShadowFrame::DeleteDeoptimizedFrame(shadow_frame); + EXPECT_OBJ_PTR_EQ(input.Get(), result_msg); } TEST_F(UnstartedRuntimeTest, IdentityHashCode) { Thread* self = Thread::Current(); ScopedObjectAccess soa(self); - ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0); JValue result; - UnstartedSystemIdentityHashCode(self, tmp, &result, 0); + UnstartedSystemIdentityHashCode(self, tmp.get(), &result, 0); EXPECT_EQ(0, result.GetI()); ASSERT_FALSE(self->IsExceptionPending()); ObjPtr str = mirror::String::AllocFromModifiedUtf8(self, "abd"); - tmp->SetVRegReference(0, str.Ptr()); - UnstartedSystemIdentityHashCode(self, tmp, &result, 0); + tmp->SetVRegReference(0, str); + UnstartedSystemIdentityHashCode(self, tmp.get(), &result, 0); EXPECT_NE(0, result.GetI()); EXPECT_EQ(str->IdentityHashCode(), result.GetI()); ASSERT_FALSE(self->IsExceptionPending()); - - ShadowFrame::DeleteDeoptimizedFrame(tmp); } } // namespace interpreter diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc index 291a983e75c16700654bfe6875859006e11ca860..1e61ba0f2d550ab9487dd688a19af4b9f36c4ea8 100644 --- a/runtime/jdwp/jdwp_handler.cc +++ b/runtime/jdwp/jdwp_handler.cc @@ -1625,7 +1625,7 @@ size_t JdwpState::ProcessRequest(Request* request, ExpandBuf* pReply, bool* skip * so waitForDebugger() doesn't return if we stall for a bit here. */ Dbg::GoActive(); - last_activity_time_ms_.StoreSequentiallyConsistent(0); + last_activity_time_ms_.store(0, std::memory_order_seq_cst); } /* @@ -1703,7 +1703,7 @@ size_t JdwpState::ProcessRequest(Request* request, ExpandBuf* pReply, bool* skip * the initial setup. Only update if this is a non-DDMS packet. */ if (request->GetCommandSet() != kJDWPDdmCmdSet) { - last_activity_time_ms_.StoreSequentiallyConsistent(MilliTime()); + last_activity_time_ms_.store(MilliTime(), std::memory_order_seq_cst); } return replyLength; diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc index 557b032154b025e596c17b9530db5aa2b984388f..447e3bf45ba44a4d4bd4eaf8288e03211ab51642 100644 --- a/runtime/jdwp/jdwp_main.cc +++ b/runtime/jdwp/jdwp_main.cc @@ -729,7 +729,7 @@ int64_t JdwpState::LastDebuggerActivity() { return -1; } - int64_t last = last_activity_time_ms_.LoadSequentiallyConsistent(); + int64_t last = last_activity_time_ms_.load(std::memory_order_seq_cst); /* initializing or in the middle of something? */ if (last == 0) { diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc index 510f5f00a69d564e3c78d36e8cdbe9c2bd8de709..df1eb2b561a2d5a51485a875c96623deeee911ac 100644 --- a/runtime/jdwp/object_registry.cc +++ b/runtime/jdwp/object_registry.cc @@ -17,7 +17,7 @@ #include "object_registry.h" #include "handle_scope-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/throwable.h" #include "obj_ptr-inl.h" diff --git a/runtime/jdwp_provider.h b/runtime/jdwp_provider.h index 698fdc086d4913043618253d86401e26a4d0ece7..c4f19899c95a97b256849c18a5c2e030d28bc28a 100644 --- a/runtime/jdwp_provider.h +++ b/runtime/jdwp_provider.h @@ -19,6 +19,7 @@ #include +#include "base/globals.h" #include "base/macros.h" #include "base/logging.h" @@ -26,13 +27,33 @@ namespace art { enum class JdwpProvider { kNone, + // Special value only used to denote that no explicit choice has been made by the user. This + // should not be used and one should always call CanonicalizeJdwpProvider which will remove this + // value before using a JdwpProvider value. + kUnset, kInternal, kAdbConnection, - // The current default provider + // The current default provider. Used if you run -XjdwpProvider:default kDefaultJdwpProvider = kAdbConnection, + + // What we should use as provider with no options and debuggable. On host we always want to be + // none since there is no adbd on host. + kUnsetDebuggable = kIsTargetBuild ? kDefaultJdwpProvider : kNone, + // What we should use as provider with no options and non-debuggable + kUnsetNonDebuggable = kNone, }; +inline JdwpProvider CanonicalizeJdwpProvider(JdwpProvider p, bool debuggable) { + if (p != JdwpProvider::kUnset) { + return p; + } + if (debuggable) { + return JdwpProvider::kUnsetDebuggable; + } + return JdwpProvider::kUnsetNonDebuggable; +} + std::ostream& operator<<(std::ostream& os, const JdwpProvider& rhs); } // namespace art diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 57d3b9f6c7e3c4650c1d2783c5430ffc96933044..86f06064ad00eb61ca18cadd7acf7b81281380ff 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -24,14 +24,17 @@ #include "base/memory_tool.h" #include "base/runtime_debug.h" #include "base/utils.h" +#include "class_root.h" #include "debugger.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "interpreter/interpreter.h" -#include "java_vm_ext.h" #include "jit_code_cache.h" +#include "jni/java_vm_ext.h" +#include "mirror/method_handle_impl.h" +#include "mirror/var_handle.h" #include "oat_file_manager.h" #include "oat_quick_method_header.h" -#include "profile_compilation_info.h" +#include "profile/profile_compilation_info.h" #include "profile_saver.h" #include "runtime.h" #include "runtime_options.h" @@ -44,8 +47,6 @@ namespace art { namespace jit { static constexpr bool kEnableOnStackReplacement = true; -// At what priority to schedule jit threads. 9 is the lowest foreground priority on device. -static constexpr int kJitPoolThreadPthreadPriority = 9; // Different compilation threshold constants. These can be overridden on the command line. static constexpr size_t kJitDefaultCompileThreshold = 10000; // Non-debug default. @@ -78,6 +79,8 @@ JitOptions* JitOptions::CreateFromRuntimeArguments(const RuntimeArgumentMap& opt options.Exists(RuntimeArgumentMap::DumpJITInfoOnShutdown); jit_options->profile_saver_options_ = options.GetOrDefault(RuntimeArgumentMap::ProfileSaverOpts); + jit_options->thread_pool_pthread_priority_ = + options.GetOrDefault(RuntimeArgumentMap::JITPoolThreadPthreadPriority); if (options.Exists(RuntimeArgumentMap::JITCompileThreshold)) { jit_options->compile_threshold_ = *options.Get(RuntimeArgumentMap::JITCompileThreshold); @@ -165,21 +168,14 @@ void Jit::AddTimingLogger(const TimingLogger& logger) { cumulative_timings_.AddLogger(logger); } -Jit::Jit() : dump_info_on_shutdown_(false), - cumulative_timings_("JIT timings"), - memory_use_("Memory used for compilation", 16), - lock_("JIT memory use lock"), - use_jit_compilation_(true), - hot_method_threshold_(0), - warm_method_threshold_(0), - osr_method_threshold_(0), - priority_thread_weight_(0), - invoke_transition_weight_(0) {} +Jit::Jit(JitOptions* options) : options_(options), + cumulative_timings_("JIT timings"), + memory_use_("Memory used for compilation", 16), + lock_("JIT memory use lock") {} Jit* Jit::Create(JitOptions* options, std::string* error_msg) { DCHECK(options->UseJitCompilation() || options->GetProfileSaverOptions().IsEnabled()); - std::unique_ptr jit(new Jit); - jit->dump_info_on_shutdown_ = options->DumpJitInfoOnShutdown(); + std::unique_ptr jit(new Jit(options)); if (jit_compiler_handle_ == nullptr && !LoadCompiler(error_msg)) { return nullptr; } @@ -193,8 +189,6 @@ Jit* Jit::Create(JitOptions* options, std::string* error_msg) { if (jit->GetCodeCache() == nullptr) { return nullptr; } - jit->use_jit_compilation_ = options->UseJitCompilation(); - jit->profile_saver_options_ = options->GetProfileSaverOptions(); VLOG(jit) << "JIT created with initial_capacity=" << PrettySize(options->GetCodeCacheInitialCapacity()) << ", max_capacity=" << PrettySize(options->GetCodeCacheMaxCapacity()) @@ -202,12 +196,6 @@ Jit* Jit::Create(JitOptions* options, std::string* error_msg) { << ", profile_saver_options=" << options->GetProfileSaverOptions(); - jit->hot_method_threshold_ = options->GetCompileThreshold(); - jit->warm_method_threshold_ = options->GetWarmupThreshold(); - jit->osr_method_threshold_ = options->GetOsrThreshold(); - jit->priority_thread_weight_ = options->GetPriorityThreadWeight(); - jit->invoke_transition_weight_ = options->GetInvokeTransitionWeight(); - jit->CreateThreadPool(); // Notify native debugger about the classes already loaded before the creation of the jit. @@ -328,7 +316,7 @@ void Jit::CreateThreadPool() { constexpr bool kJitPoolNeedsPeers = true; thread_pool_.reset(new ThreadPool("Jit thread pool", 1, kJitPoolNeedsPeers)); - thread_pool_->SetPthreadPriority(kJitPoolThreadPthreadPriority); + thread_pool_->SetPthreadPriority(options_->GetThreadPoolPthreadPriority()); Start(); } @@ -345,7 +333,7 @@ void Jit::DeleteThreadPool() { } // 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); } @@ -358,8 +346,8 @@ void Jit::DeleteThreadPool() { void Jit::StartProfileSaver(const std::string& filename, const std::vector& code_paths) { - if (profile_saver_options_.IsEnabled()) { - ProfileSaver::Start(profile_saver_options_, + if (options_->GetSaveProfilingInfo()) { + ProfileSaver::Start(options_->GetProfileSaverOptions(), filename, code_cache_.get(), code_paths); @@ -367,8 +355,8 @@ void Jit::StartProfileSaver(const std::string& filename, } void Jit::StopProfileSaver() { - if (profile_saver_options_.IsEnabled() && ProfileSaver::IsStarted()) { - ProfileSaver::Stop(dump_info_on_shutdown_); + if (options_->GetSaveProfilingInfo() && ProfileSaver::IsStarted()) { + ProfileSaver::Stop(options_->DumpJitInfoOnShutdown()); } } @@ -381,8 +369,8 @@ bool Jit::CanInvokeCompiledCode(ArtMethod* method) { } Jit::~Jit() { - DCHECK(!profile_saver_options_.IsEnabled() || !ProfileSaver::IsStarted()); - if (dump_info_on_shutdown_) { + DCHECK(!options_->GetSaveProfilingInfo() || !ProfileSaver::IsStarted()); + if (options_->DumpJitInfoOnShutdown()) { DumpInfo(LOG_STREAM(INFO)); Runtime::Current()->DumpDeoptimizations(LOG_STREAM(INFO)); } @@ -486,11 +474,10 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, return false; } - CodeInfo code_info = osr_method->GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); + CodeInfo code_info(osr_method); // Find stack map starting at the target dex_pc. - StackMap stack_map = code_info.GetOsrStackMapForDexPc(dex_pc + dex_pc_offset, encoding); + StackMap stack_map = code_info.GetOsrStackMapForDexPc(dex_pc + dex_pc_offset); if (!stack_map.IsValid()) { // There is no OSR stack map for this dex pc offset. Just return to the interpreter in the // hope that the next branch has one. @@ -506,8 +493,7 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, // We found a stack map, now fill the frame with dex register values from the interpreter's // shadow frame. - DexRegisterMap vreg_map = - code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_vregs); + DexRegisterMap vreg_map = code_info.GetDexRegisterMapOf(stack_map); frame_size = osr_method->GetFrameSizeInBytes(); @@ -523,13 +509,13 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, memory[0] = method; shadow_frame = thread->PopShadowFrame(); - if (!vreg_map.IsValid()) { + if (vreg_map.empty()) { // If we don't have a dex register map, then there are no live dex registers at // this dex pc. } else { + DCHECK_EQ(vreg_map.size(), number_of_vregs); for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) { - DexRegisterLocation::Kind location = - vreg_map.GetLocationKind(vreg, number_of_vregs, code_info, encoding); + DexRegisterLocation::Kind location = vreg_map[vreg].GetKind(); if (location == DexRegisterLocation::Kind::kNone) { // Dex register is dead or uninitialized. continue; @@ -543,17 +529,14 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, DCHECK_EQ(location, DexRegisterLocation::Kind::kInStack); int32_t vreg_value = shadow_frame->GetVReg(vreg); - int32_t slot_offset = vreg_map.GetStackOffsetInBytes(vreg, - number_of_vregs, - code_info, - encoding); + int32_t slot_offset = vreg_map[vreg].GetStackOffsetInBytes(); DCHECK_LT(slot_offset, static_cast(frame_size)); DCHECK_GT(slot_offset, 0); (reinterpret_cast(memory))[slot_offset / sizeof(int32_t)] = vreg_value; } } - native_pc = stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA) + + native_pc = stack_map.GetNativePcOffset(kRuntimeISA) + osr_method->GetEntryPoint(); VLOG(jit) << "Jumping to " << method_name @@ -640,36 +623,55 @@ class JitCompileTask FINAL : public Task { DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask); }; +static bool IgnoreSamplesForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { + if (method->IsClassInitializer() || !method->IsCompilable()) { + // We do not want to compile such methods. + return true; + } + if (method->IsNative()) { + ObjPtr klass = method->GetDeclaringClass(); + if (klass == GetClassRoot() || + klass == GetClassRoot()) { + // MethodHandle and VarHandle invocation methods are required to throw an + // UnsupportedOperationException if invoked reflectively. We achieve this by having native + // implementations that arise the exception. We need to disable JIT compilation of these JNI + // methods as it can lead to transitioning between JIT compiled JNI stubs and generic JNI + // stubs. Since these stubs have different stack representations we can then crash in stack + // walking (b/78151261). + return true; + } + } + return false; +} + void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_backedges) { if (thread_pool_ == nullptr) { // Should only see this when shutting down. DCHECK(Runtime::Current()->IsShuttingDown(self)); return; } - - if (method->IsClassInitializer() || !method->IsCompilable()) { - // We do not want to compile such methods. + if (IgnoreSamplesForMethod(method)) { return; } - if (hot_method_threshold_ == 0) { + if (HotMethodThreshold() == 0) { // Tests might request JIT on first use (compiled synchronously in the interpreter). return; } DCHECK(thread_pool_ != nullptr); - DCHECK_GT(warm_method_threshold_, 0); - DCHECK_GT(hot_method_threshold_, warm_method_threshold_); - DCHECK_GT(osr_method_threshold_, hot_method_threshold_); - DCHECK_GE(priority_thread_weight_, 1); - DCHECK_LE(priority_thread_weight_, hot_method_threshold_); + DCHECK_GT(WarmMethodThreshold(), 0); + DCHECK_GT(HotMethodThreshold(), WarmMethodThreshold()); + DCHECK_GT(OSRMethodThreshold(), HotMethodThreshold()); + DCHECK_GE(PriorityThreadWeight(), 1); + DCHECK_LE(PriorityThreadWeight(), HotMethodThreshold()); - int32_t starting_count = method->GetCounter(); + uint16_t starting_count = method->GetCounter(); if (Jit::ShouldUsePriorityThreadWeight(self)) { - count *= priority_thread_weight_; + count *= PriorityThreadWeight(); } - int32_t new_count = starting_count + count; // int32 here to avoid wrap-around; + uint32_t new_count = starting_count + count; // Note: Native method have no "warm" state or profiling info. - if (LIKELY(!method->IsNative()) && starting_count < warm_method_threshold_) { - if ((new_count >= warm_method_threshold_) && + if (LIKELY(!method->IsNative()) && starting_count < WarmMethodThreshold()) { + if ((new_count >= WarmMethodThreshold()) && (method->GetProfilingInfo(kRuntimePointerSize) == nullptr)) { bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false); if (success) { @@ -690,23 +692,23 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ } } // Avoid jumping more than one state at a time. - new_count = std::min(new_count, hot_method_threshold_ - 1); - } else if (use_jit_compilation_) { - if (starting_count < hot_method_threshold_) { - if ((new_count >= hot_method_threshold_) && + new_count = std::min(new_count, static_cast(HotMethodThreshold() - 1)); + } else if (UseJitCompilation()) { + if (starting_count < HotMethodThreshold()) { + if ((new_count >= HotMethodThreshold()) && !code_cache_->ContainsPc(method->GetEntryPointFromQuickCompiledCode())) { DCHECK(thread_pool_ != nullptr); thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile)); } // Avoid jumping more than one state at a time. - new_count = std::min(new_count, osr_method_threshold_ - 1); - } else if (starting_count < osr_method_threshold_) { + new_count = std::min(new_count, static_cast(OSRMethodThreshold() - 1)); + } else if (starting_count < OSRMethodThreshold()) { if (!with_backedges) { // If the samples don't contain any back edge, we don't increment the hotness. return; } DCHECK(!method->IsNative()); // No back edges reported for native methods. - if ((new_count >= osr_method_threshold_) && !code_cache_->IsOsrCompiled(method)) { + if ((new_count >= OSRMethodThreshold()) && !code_cache_->IsOsrCompiled(method)) { DCHECK(thread_pool_ != nullptr); thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr)); } @@ -719,19 +721,25 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ void Jit::MethodEntered(Thread* thread, ArtMethod* method) { Runtime* runtime = Runtime::Current(); if (UNLIKELY(runtime->UseJitCompilation() && runtime->GetJit()->JitAtFirstUse())) { - // The compiler requires a ProfilingInfo object. - ProfilingInfo::Create(thread, - method->GetInterfaceMethodIfProxy(kRuntimePointerSize), - /* retry_allocation */ true); - JitCompileTask compile_task(method, JitCompileTask::kCompile); - compile_task.Run(thread); + ArtMethod* np_method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize); + if (np_method->IsCompilable()) { + if (!np_method->IsNative()) { + // The compiler requires a ProfilingInfo object for non-native methods. + ProfilingInfo::Create(thread, np_method, /* retry_allocation */ true); + } + JitCompileTask compile_task(method, JitCompileTask::kCompile); + compile_task.Run(thread); + } return; } ProfilingInfo* profiling_info = method->GetProfilingInfo(kRuntimePointerSize); // Update the entrypoint if the ProfilingInfo has one. The interpreter will call it - // instead of interpreting the method. - if ((profiling_info != nullptr) && (profiling_info->GetSavedEntryPoint() != nullptr)) { + // instead of interpreting the method. We don't update it for instrumentation as the entrypoint + // must remain the instrumentation entrypoint. + if ((profiling_info != nullptr) && + (profiling_info->GetSavedEntryPoint() != nullptr) && + (method->GetEntryPointFromQuickCompiledCode() != GetQuickInstrumentationEntryPoint())) { Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( method, profiling_info->GetSavedEntryPoint()); } else { @@ -760,13 +768,20 @@ void Jit::WaitForCompilationToFinish(Thread* self) { void Jit::Stop() { Thread* self = Thread::Current(); // TODO(ngeoffray): change API to not require calling WaitForCompilationToFinish twice. + // During shutdown and startup the thread-pool can be null. + if (GetThreadPool() == nullptr) { + return; + } WaitForCompilationToFinish(self); GetThreadPool()->StopWorkers(self); WaitForCompilationToFinish(self); } void Jit::Start() { - GetThreadPool()->StartWorkers(Thread::Current()); + // During shutdown and startup the thread-pool can be null. + if (GetThreadPool() != nullptr) { + GetThreadPool()->StartWorkers(Thread::Current()); + } } ScopedJitSuspend::ScopedJitSuspend() { diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index 4b8b8919d11aed167dab1a91da26a40e6b956ff6..edaf348cc455f43f543c04e967d9ccd8f452135b 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -44,6 +44,110 @@ class JitOptions; static constexpr int16_t kJitCheckForOSR = -1; static constexpr int16_t kJitHotnessDisabled = -2; +// At what priority to schedule jit threads. 9 is the lowest foreground priority on device. +// See android/os/Process.java. +static constexpr int kJitPoolThreadPthreadDefaultPriority = 9; + +class JitOptions { + public: + static JitOptions* CreateFromRuntimeArguments(const RuntimeArgumentMap& options); + + uint16_t GetCompileThreshold() const { + return compile_threshold_; + } + + uint16_t GetWarmupThreshold() const { + return warmup_threshold_; + } + + uint16_t GetOsrThreshold() const { + return osr_threshold_; + } + + uint16_t GetPriorityThreadWeight() const { + return priority_thread_weight_; + } + + uint16_t GetInvokeTransitionWeight() const { + return invoke_transition_weight_; + } + + size_t GetCodeCacheInitialCapacity() const { + return code_cache_initial_capacity_; + } + + size_t GetCodeCacheMaxCapacity() const { + return code_cache_max_capacity_; + } + + bool DumpJitInfoOnShutdown() const { + return dump_info_on_shutdown_; + } + + const ProfileSaverOptions& GetProfileSaverOptions() const { + return profile_saver_options_; + } + + bool GetSaveProfilingInfo() const { + return profile_saver_options_.IsEnabled(); + } + + int GetThreadPoolPthreadPriority() const { + return thread_pool_pthread_priority_; + } + + bool UseJitCompilation() const { + return use_jit_compilation_; + } + + void SetUseJitCompilation(bool b) { + use_jit_compilation_ = b; + } + + void SetSaveProfilingInfo(bool save_profiling_info) { + profile_saver_options_.SetEnabled(save_profiling_info); + } + + void SetWaitForJitNotificationsToSaveProfile(bool value) { + profile_saver_options_.SetWaitForJitNotificationsToSave(value); + } + + void SetProfileAOTCode(bool value) { + profile_saver_options_.SetProfileAOTCode(value); + } + + void SetJitAtFirstUse() { + use_jit_compilation_ = true; + compile_threshold_ = 0; + } + + private: + bool use_jit_compilation_; + size_t code_cache_initial_capacity_; + size_t code_cache_max_capacity_; + uint16_t compile_threshold_; + uint16_t warmup_threshold_; + uint16_t osr_threshold_; + uint16_t priority_thread_weight_; + uint16_t invoke_transition_weight_; + bool dump_info_on_shutdown_; + int thread_pool_pthread_priority_; + ProfileSaverOptions profile_saver_options_; + + JitOptions() + : use_jit_compilation_(false), + code_cache_initial_capacity_(0), + code_cache_max_capacity_(0), + compile_threshold_(0), + warmup_threshold_(0), + osr_threshold_(0), + priority_thread_weight_(0), + invoke_transition_weight_(0), + dump_info_on_shutdown_(false), + thread_pool_pthread_priority_(kJitPoolThreadPthreadDefaultPriority) {} + + DISALLOW_COPY_AND_ASSIGN(JitOptions); +}; class Jit { public: @@ -77,29 +181,29 @@ class Jit { REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); - size_t OSRMethodThreshold() const { - return osr_method_threshold_; + uint16_t OSRMethodThreshold() const { + return options_->GetOsrThreshold(); } - size_t HotMethodThreshold() const { - return hot_method_threshold_; + uint16_t HotMethodThreshold() const { + return options_->GetCompileThreshold(); } - size_t WarmMethodThreshold() const { - return warm_method_threshold_; + uint16_t WarmMethodThreshold() const { + return options_->GetWarmupThreshold(); } uint16_t PriorityThreadWeight() const { - return priority_thread_weight_; + return options_->GetPriorityThreadWeight(); } // Returns false if we only need to save profile information and not compile methods. bool UseJitCompilation() const { - return use_jit_compilation_; + return options_->UseJitCompilation(); } bool GetSaveProfilingInfo() const { - return profile_saver_options_.IsEnabled(); + return options_->GetSaveProfilingInfo(); } // Wait until there is no more pending compilation tasks. @@ -120,12 +224,12 @@ class Jit { void NotifyInterpreterToCompiledCodeTransition(Thread* self, ArtMethod* caller) REQUIRES_SHARED(Locks::mutator_lock_) { - AddSamples(self, caller, invoke_transition_weight_, false); + AddSamples(self, caller, options_->GetInvokeTransitionWeight(), false); } void NotifyCompiledCodeToInterpreterTransition(Thread* self, ArtMethod* callee) REQUIRES_SHARED(Locks::mutator_lock_) { - AddSamples(self, callee, invoke_transition_weight_, false); + AddSamples(self, callee, options_->GetInvokeTransitionWeight(), false); } // Starts the profile saver if the config options allow profile recording. @@ -177,7 +281,7 @@ class Jit { void Start(); private: - Jit(); + explicit Jit(JitOptions* options); static bool LoadCompiler(std::string* error_msg); @@ -189,107 +293,22 @@ class Jit { static bool (*jit_compile_method_)(void*, ArtMethod*, Thread*, bool); static void (*jit_types_loaded_)(void*, mirror::Class**, size_t count); + // We make this static to simplify the interaction with libart-compiler.so. + static bool generate_debug_info_; + + const JitOptions* const options_; + + std::unique_ptr code_cache_; + std::unique_ptr thread_pool_; + // Performance monitoring. - bool dump_info_on_shutdown_; CumulativeLogger cumulative_timings_; Histogram memory_use_ GUARDED_BY(lock_); Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - std::unique_ptr code_cache_; - - bool use_jit_compilation_; - ProfileSaverOptions profile_saver_options_; - static bool generate_debug_info_; - uint16_t hot_method_threshold_; - uint16_t warm_method_threshold_; - uint16_t osr_method_threshold_; - uint16_t priority_thread_weight_; - uint16_t invoke_transition_weight_; - std::unique_ptr thread_pool_; - DISALLOW_COPY_AND_ASSIGN(Jit); }; -class JitOptions { - public: - static JitOptions* CreateFromRuntimeArguments(const RuntimeArgumentMap& options); - size_t GetCompileThreshold() const { - return compile_threshold_; - } - size_t GetWarmupThreshold() const { - return warmup_threshold_; - } - size_t GetOsrThreshold() const { - return osr_threshold_; - } - uint16_t GetPriorityThreadWeight() const { - return priority_thread_weight_; - } - size_t GetInvokeTransitionWeight() const { - return invoke_transition_weight_; - } - size_t GetCodeCacheInitialCapacity() const { - return code_cache_initial_capacity_; - } - size_t GetCodeCacheMaxCapacity() const { - return code_cache_max_capacity_; - } - bool DumpJitInfoOnShutdown() const { - return dump_info_on_shutdown_; - } - const ProfileSaverOptions& GetProfileSaverOptions() const { - return profile_saver_options_; - } - bool GetSaveProfilingInfo() const { - return profile_saver_options_.IsEnabled(); - } - bool UseJitCompilation() const { - return use_jit_compilation_; - } - void SetUseJitCompilation(bool b) { - use_jit_compilation_ = b; - } - void SetSaveProfilingInfo(bool save_profiling_info) { - profile_saver_options_.SetEnabled(save_profiling_info); - } - void SetWaitForJitNotificationsToSaveProfile(bool value) { - profile_saver_options_.SetWaitForJitNotificationsToSave(value); - } - void SetProfileAOTCode(bool value) { - profile_saver_options_.SetProfileAOTCode(value); - } - - void SetJitAtFirstUse() { - use_jit_compilation_ = true; - compile_threshold_ = 0; - } - - private: - bool use_jit_compilation_; - size_t code_cache_initial_capacity_; - size_t code_cache_max_capacity_; - size_t compile_threshold_; - size_t warmup_threshold_; - size_t osr_threshold_; - uint16_t priority_thread_weight_; - size_t invoke_transition_weight_; - bool dump_info_on_shutdown_; - ProfileSaverOptions profile_saver_options_; - - JitOptions() - : use_jit_compilation_(false), - code_cache_initial_capacity_(0), - code_cache_max_capacity_(0), - compile_threshold_(0), - warmup_threshold_(0), - osr_threshold_(0), - priority_thread_weight_(0), - invoke_transition_weight_(0), - dump_info_on_shutdown_(false) {} - - DISALLOW_COPY_AND_ASSIGN(JitOptions); -}; - // Helper class to stop the JIT for a given scope. This will wait for the JIT to quiesce. class ScopedJitSuspend { public: diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 1048c8093f2de09819055fb9ff8b0c9b685cd013..70a717154b483cc1b706ef5272da3ff0f461bda3 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -21,7 +21,9 @@ #include "arch/context.h" #include "art_method-inl.h" #include "base/enums.h" +#include "base/histogram-inl.h" #include "base/logging.h" // For VLOG. +#include "base/mem_map.h" #include "base/quasi_atomic.h" #include "base/stl_util.h" #include "base/systrace.h" @@ -29,19 +31,20 @@ #include "cha.h" #include "debugger_interface.h" #include "dex/dex_file_loader.h" +#include "dex/method_reference.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/bitmap-inl.h" #include "gc/scoped_gc_critical_section.h" #include "handle.h" +#include "instrumentation.h" #include "intern_table.h" #include "jit/jit.h" #include "jit/profiling_info.h" #include "linear_alloc.h" -#include "mem_map.h" #include "oat_file-inl.h" #include "oat_quick_method_header.h" #include "object_callbacks.h" -#include "profile_compilation_info.h" +#include "profile/profile_compilation_info.h" #include "scoped_thread_state_change-inl.h" #include "stack.h" #include "thread-current-inl.h" @@ -167,11 +170,14 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity, // Generating debug information is for using the Linux perf tool on // host which does not work with ashmem. - // Also, target linux does not support ashmem. - bool use_ashmem = !generate_debug_info && !kIsTargetLinux; + // Also, targets linux and fuchsia do not support ashmem. + bool use_ashmem = !generate_debug_info && !kIsTargetLinux && !kIsTargetFuchsia; // With 'perf', we want a 1-1 mapping between an address and a method. - bool garbage_collect_code = !generate_debug_info; + // We aren't able to keep method pointers live during the instrumentation method entry trampoline + // so we will just disable jit-gc if we are doing that. + bool garbage_collect_code = !generate_debug_info && + !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled(); // We need to have 32 bit offsets from method headers in code cache which point to things // in the data cache. If the maps are more than 4G apart, having multiple maps wouldn't work. @@ -267,7 +273,6 @@ JitCodeCache::JitCodeCache(MemMap* code_map, code_end_(initial_code_capacity), data_end_(initial_data_capacity), last_collection_increased_code_cache_(false), - last_update_time_ns_(0), garbage_collect_code_(garbage_collect_code), used_memory_for_data_(0), used_memory_for_code_(0), @@ -314,6 +319,17 @@ bool JitCodeCache::ContainsPc(const void* ptr) const { return code_map_->Begin() <= ptr && ptr < code_map_->End(); } +bool JitCodeCache::WillExecuteJitCode(ArtMethod* method) { + ScopedObjectAccess soa(art::Thread::Current()); + ScopedAssertNoThreadSuspension sants(__FUNCTION__); + if (ContainsPc(method->GetEntryPointFromQuickCompiledCode())) { + return true; + } else if (method->GetEntryPointFromQuickCompiledCode() == GetQuickInstrumentationEntryPoint()) { + return FindCompiledCodeForInstrumentation(method) != nullptr; + } + return false; +} + bool JitCodeCache::ContainsMethod(ArtMethod* method) { MutexLock mu(Thread::Current(), lock_); if (UNLIKELY(method->IsNative())) { @@ -346,19 +362,48 @@ const void* JitCodeCache::GetJniStubCode(ArtMethod* method) { return nullptr; } +void JitCodeCache::ClearAllCompiledDexCode() { + MutexLock mu(Thread::Current(), lock_); + // Get rid of OSR code waiting to be put on a thread. + osr_code_map_.clear(); + + // We don't clear out or even touch method_code_map_ since that is what we use to go the other + // way, move from code currently-running to the method it's from. Getting rid of it would break + // the jit-gc, stack-walking and signal handling. Since we never look through it to go the other + // way (from method -> code) everything is fine. + + for (ProfilingInfo* p : profiling_infos_) { + p->SetSavedEntryPoint(nullptr); + } +} + +const void* JitCodeCache::FindCompiledCodeForInstrumentation(ArtMethod* method) { + // If jit-gc is still on we use the SavedEntryPoint field for doing that and so cannot use it to + // find the instrumentation entrypoint. + if (LIKELY(GetGarbageCollectCode())) { + return nullptr; + } + ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); + if (info == nullptr) { + return nullptr; + } + // When GC is disabled for trampoline tracing we will use SavedEntrypoint to hold the actual + // jit-compiled version of the method. If jit-gc is disabled for other reasons this will just be + // nullptr. + return info->GetSavedEntryPoint(); +} + class ScopedCodeCacheWrite : ScopedTrace { public: - explicit ScopedCodeCacheWrite(const JitCodeCache* const code_cache, - bool only_for_tlb_shootdown = false) + explicit ScopedCodeCacheWrite(const JitCodeCache* const code_cache) : ScopedTrace("ScopedCodeCacheWrite"), - code_cache_(code_cache), - only_for_tlb_shootdown_(only_for_tlb_shootdown) { + code_cache_(code_cache) { ScopedTrace trace("mprotect all"); CheckedCall( mprotect, "make code writable", code_cache_->code_map_->Begin(), - only_for_tlb_shootdown_ ? kPageSize : code_cache_->code_map_->Size(), + code_cache_->code_map_->Size(), code_cache_->memmap_flags_prot_code_ | PROT_WRITE); } @@ -368,17 +413,13 @@ class ScopedCodeCacheWrite : ScopedTrace { mprotect, "make code protected", code_cache_->code_map_->Begin(), - only_for_tlb_shootdown_ ? kPageSize : code_cache_->code_map_->Size(), + code_cache_->code_map_->Size(), code_cache_->memmap_flags_prot_code_); } private: const JitCodeCache* const code_cache_; - // If we're using ScopedCacheWrite only for TLB shootdown, we limit the scope of mprotect to - // one page. - const bool only_for_tlb_shootdown_; - DISALLOW_COPY_AND_ASSIGN(ScopedCodeCacheWrite); }; @@ -468,21 +509,31 @@ static const uint8_t* FromStackMapToRoots(const uint8_t* stack_map_data) { return stack_map_data - ComputeRootTableSize(GetNumberOfRoots(stack_map_data)); } -static void FillRootTable(uint8_t* roots_data, Handle> roots) - REQUIRES_SHARED(Locks::mutator_lock_) { - GcRoot* gc_roots = reinterpret_cast*>(roots_data); +static void DCheckRootsAreValid(Handle> roots) + REQUIRES(!Locks::intern_table_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { + if (!kIsDebugBuild) { + return; + } const uint32_t length = roots->GetLength(); // Put all roots in `roots_data`. for (uint32_t i = 0; i < length; ++i) { ObjPtr object = roots->Get(i); - if (kIsDebugBuild) { - // Ensure the string is strongly interned. b/32995596 - if (object->IsString()) { - ObjPtr str = reinterpret_cast(object.Ptr()); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - CHECK(class_linker->GetInternTable()->LookupStrong(Thread::Current(), str) != nullptr); - } + // Ensure the string is strongly interned. b/32995596 + if (object->IsString()) { + ObjPtr str = ObjPtr::DownCast(object); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + CHECK(class_linker->GetInternTable()->LookupStrong(Thread::Current(), str) != nullptr); } + } +} + +void JitCodeCache::FillRootTable(uint8_t* roots_data, + Handle> roots) { + GcRoot* gc_roots = reinterpret_cast*>(roots_data); + const uint32_t length = roots->GetLength(); + // Put all roots in `roots_data`. + for (uint32_t i = 0; i < length; ++i) { + ObjPtr object = roots->Get(i); gc_roots[i] = GcRoot(object); } } @@ -570,7 +621,7 @@ void JitCodeCache::SweepRootTables(IsMarkedVisitor* visitor) { } } -void JitCodeCache::FreeCode(const void* code_ptr) { +void JitCodeCache::FreeCodeAndData(const void* code_ptr) { uintptr_t allocation = FromCodeToAllocation(code_ptr); // Notify native debugger that we are about to remove the code. // It does nothing if we are not using native debugger. @@ -597,7 +648,7 @@ void JitCodeCache::FreeAllMethodHeaders( MutexLock mu(Thread::Current(), lock_); ScopedCodeCacheWrite scc(this); for (const OatQuickMethodHeader* method_header : method_headers) { - FreeCode(method_header->GetCode()); + FreeCodeAndData(method_header->GetCode()); } } @@ -660,7 +711,7 @@ void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) { bool JitCodeCache::IsWeakAccessEnabled(Thread* self) const { return kUseReadBarrier ? self->GetWeakRefAccessEnabled() - : is_weak_access_enabled_.LoadSequentiallyConsistent(); + : is_weak_access_enabled_.load(std::memory_order_seq_cst); } void JitCodeCache::WaitUntilInlineCacheAccessible(Thread* self) { @@ -682,13 +733,13 @@ void JitCodeCache::BroadcastForInlineCacheAccess() { void JitCodeCache::AllowInlineCacheAccess() { DCHECK(!kUseReadBarrier); - is_weak_access_enabled_.StoreSequentiallyConsistent(true); + is_weak_access_enabled_.store(true, std::memory_order_seq_cst); BroadcastForInlineCacheAccess(); } void JitCodeCache::DisallowInlineCacheAccess() { DCHECK(!kUseReadBarrier); - is_weak_access_enabled_.StoreSequentiallyConsistent(false); + is_weak_access_enabled_.store(false, std::memory_order_seq_cst); } void JitCodeCache::CopyInlineCacheInto(const InlineCache& ic, @@ -738,7 +789,6 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, bool has_should_deoptimize_flag, const ArenaSet& cha_single_implementation_list) { - DCHECK_NE(stack_map != nullptr, method->IsNative()); DCHECK(!method->IsNative() || !osr); size_t alignment = GetInstructionSetAlignment(kRuntimeISA); // Ensure the header ends up at expected instruction alignment. @@ -815,13 +865,17 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, single_impl, method, method_header); } + if (!method->IsNative()) { + // We need to do this before grabbing the lock_ because it needs to be able to see the string + // InternTable. Native methods do not have roots. + DCheckRootsAreValid(roots); + } + // The following needs to be guarded by cha_lock_ also. Otherwise it's // possible that the compiled code is considered invalidated by some class linking, // but below we still make the compiled code valid for the method. MutexLock mu(self, lock_); if (UNLIKELY(method->IsNative())) { - DCHECK(stack_map == nullptr); - DCHECK(roots_data == nullptr); auto it = jni_stubs_map_.find(JniStubKey(method)); DCHECK(it != jni_stubs_map_.end()) << "Entry inserted in NotifyCompilationOf() should be alive."; @@ -840,8 +894,6 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, FillRootTable(roots_data, roots); { // Flush data cache, as compiled code references literals in it. - // We also need a TLB shootdown to act as memory barrier across cores. - ScopedCodeCacheWrite ccw(this, /* only_for_tlb_shootdown */ true); FlushDataCache(reinterpret_cast(roots_data), reinterpret_cast(roots_data + data_size)); } @@ -859,7 +911,6 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, // code. GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(code_ptr)); } - last_update_time_ns_.StoreRelease(NanoTime()); VLOG(jit) << "JIT added (osr=" << std::boolalpha << osr << std::noboolalpha << ") " << ArtMethod::PrettyMethod(method) << "@" << method @@ -926,7 +977,7 @@ bool JitCodeCache::RemoveMethodLocked(ArtMethod* method, bool release_memory) { in_cache = true; if (it->second.GetMethods().empty()) { if (release_memory) { - FreeCode(it->second.GetCode()); + FreeCodeAndData(it->second.GetCode()); } jni_stubs_map_.erase(it); } else { @@ -938,7 +989,7 @@ bool JitCodeCache::RemoveMethodLocked(ArtMethod* method, bool release_memory) { if (it->second == method) { in_cache = true; if (release_memory) { - FreeCode(it->first); + FreeCodeAndData(it->first); } it = method_code_map_.erase(it); } else { @@ -988,6 +1039,8 @@ void JitCodeCache::MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_meth // checks should always pass. DCHECK(!info->IsInUseByCompiler()); new_method->SetProfilingInfo(info); + // Get rid of the old saved entrypoint if it is there. + info->SetSavedEntryPoint(nullptr); info->method_ = new_method; } // Update method_code_map_ to point to the new method. @@ -1331,7 +1384,8 @@ void JitCodeCache::RemoveUnmarkedCode(Thread* self) { if (GetLiveBitmap()->Test(allocation)) { ++it; } else { - method_headers.insert(OatQuickMethodHeader::FromCodePointer(code_ptr)); + OatQuickMethodHeader* header = OatQuickMethodHeader::FromCodePointer(code_ptr); + method_headers.insert(header); it = method_code_map_.erase(it); } } @@ -1580,7 +1634,7 @@ ProfilingInfo* JitCodeCache::AddProfilingInfoInternal(Thread* self ATTRIBUTE_UNU // Make sure other threads see the data in the profiling info object before the // store in the ArtMethod's ProfilingInfo pointer. - QuasiAtomic::ThreadFenceRelease(); + std::atomic_thread_fence(std::memory_order_release); method->SetProfilingInfo(info); profiling_infos_.push_back(info); @@ -1685,10 +1739,6 @@ void JitCodeCache::GetProfiledMethods(const std::set& dex_base_loca } } -uint64_t JitCodeCache::GetLastUpdateTimeNs() const { - return last_update_time_ns_.LoadAcquire(); -} - bool JitCodeCache::IsOsrCompiled(ArtMethod* method) { MutexLock mu(Thread::Current(), lock_); return osr_code_map_.find(method) != osr_code_map_.end(); @@ -1800,13 +1850,18 @@ void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method, const OatQuickMethodHeader* header) { DCHECK(!method->IsNative()); ProfilingInfo* profiling_info = method->GetProfilingInfo(kRuntimePointerSize); + const void* method_entrypoint = method->GetEntryPointFromQuickCompiledCode(); if ((profiling_info != nullptr) && (profiling_info->GetSavedEntryPoint() == header->GetEntryPoint())) { + // When instrumentation is set, the actual entrypoint is the one in the profiling info. + method_entrypoint = profiling_info->GetSavedEntryPoint(); // Prevent future uses of the compiled code. profiling_info->SetSavedEntryPoint(nullptr); } - if (method->GetEntryPointFromQuickCompiledCode() == header->GetEntryPoint()) { + // Clear the method counter if we are running jitted code since we might want to jit this again in + // the future. + if (method_entrypoint == header->GetEntryPoint()) { // The entrypoint is the one to invalidate, so we just update it to the interpreter entry point // and clear the counter to get the method Jitted again. Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 92deb97873b7b49ce2f9cadb69f49766d990a9ff..ee6111a4303e2fbcef06fd8a6114951bd9c9f898 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -17,16 +17,19 @@ #ifndef ART_RUNTIME_JIT_JIT_CODE_CACHE_H_ #define ART_RUNTIME_JIT_JIT_CODE_CACHE_H_ -#include "instrumentation.h" +#include +#include +#include +#include +#include +#include #include "base/arena_containers.h" #include "base/atomic.h" -#include "base/histogram-inl.h" +#include "base/histogram.h" #include "base/macros.h" #include "base/mutex.h" #include "base/safe_map.h" -#include "dex/method_reference.h" -#include "gc_root.h" namespace art { @@ -36,6 +39,7 @@ class LinearAlloc; class InlineCache; class IsMarkedVisitor; class JitJniStubTestHelper; +class MemMap; class OatQuickMethodHeader; struct ProfileMethodInfo; class ProfilingInfo; @@ -150,6 +154,10 @@ class JitCodeCache { // Return true if the code cache contains this pc. bool ContainsPc(const void* pc) const; + // Returns true if either the method's entrypoint is JIT compiled code or it is the + // instrumentation entrypoint and we can jump to jit code for this method. For testing use only. + bool WillExecuteJitCode(ArtMethod* method) REQUIRES(!lock_); + // Return true if the code cache contains this method. bool ContainsMethod(ArtMethod* method) REQUIRES(!lock_); @@ -207,6 +215,8 @@ class JitCodeCache { REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); + void ClearAllCompiledDexCode() REQUIRES(!lock_, Locks::mutator_lock_); + void CopyInlineCacheInto(const InlineCache& ic, Handle> array) REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); @@ -265,6 +275,16 @@ class JitCodeCache { garbage_collect_code_ = value; } + bool GetGarbageCollectCode() const { + return garbage_collect_code_; + } + + // If Jit-gc has been disabled (and instrumentation has been enabled) this will return the + // jit-compiled entrypoint for this method. Otherwise it will return null. + const void* FindCompiledCodeForInstrumentation(ArtMethod* method) + REQUIRES(!lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + private: // Take ownership of maps. JitCodeCache(MemMap* code_map, @@ -295,6 +315,11 @@ class JitCodeCache { REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); + // Adds the given roots to the roots_data. Only a member for annotalysis. + void FillRootTable(uint8_t* roots_data, Handle> roots) + REQUIRES(lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + ProfilingInfo* AddProfilingInfoInternal(Thread* self, ArtMethod* method, const std::vector& entries) @@ -317,8 +342,8 @@ class JitCodeCache { REQUIRES(lock_) REQUIRES(Locks::mutator_lock_); - // Free in the mspace allocations for `code_ptr`. - void FreeCode(const void* code_ptr) REQUIRES(lock_); + // Free code and data allocations for `code_ptr`. + void FreeCodeAndData(const void* code_ptr) REQUIRES(lock_); // Number of bytes allocated in the code cache. size_t CodeCacheSizeLocked() REQUIRES(lock_); @@ -357,10 +382,10 @@ class JitCodeCache { REQUIRES(lock_) REQUIRES_SHARED(Locks::mutator_lock_); - void FreeCode(uint8_t* code) REQUIRES(lock_); uint8_t* AllocateCode(size_t code_size) REQUIRES(lock_); - void FreeData(uint8_t* data) REQUIRES(lock_); + void FreeCode(uint8_t* code) REQUIRES(lock_); uint8_t* AllocateData(size_t data_size) REQUIRES(lock_); + void FreeData(uint8_t* data) REQUIRES(lock_); bool IsWeakAccessEnabled(Thread* self) const; void WaitUntilInlineCacheAccessible(Thread* self) @@ -371,7 +396,7 @@ class JitCodeCache { class JniStubData; // Lock for guarding allocations, collections, and the method_code_map_. - Mutex lock_; + Mutex lock_ BOTTOM_MUTEX_ACQUIRED_AFTER; // Condition to wait on during collection. ConditionVariable lock_cond_ GUARDED_BY(lock_); // Whether there is a code cache collection in progress. @@ -410,10 +435,6 @@ class JitCodeCache { // Whether the last collection round increased the code cache. bool last_collection_increased_code_cache_ GUARDED_BY(lock_); - // Last time the the code_cache was updated. - // It is atomic to avoid locking when reading it. - Atomic last_update_time_ns_; - // Whether we can do garbage collection. Not 'const' as tests may override this. bool garbage_collect_code_; diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index 5ca5a7cf19d4718cc14bc1e3eb6602a36bfea3b7..6ccda8b0bbb2608609808cdd8224190c176c6800 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -37,8 +37,9 @@ #include "gc/collector_type.h" #include "gc/gc_cause.h" #include "gc/scoped_gc_critical_section.h" -#include "jit/profile_compilation_info.h" +#include "jit/profiling_info.h" #include "oat_file_manager.h" +#include "profile/profile_compilation_info.h" #include "scoped_thread_state_change-inl.h" namespace art { @@ -46,6 +47,10 @@ namespace art { ProfileSaver* ProfileSaver::instance_ = nullptr; pthread_t ProfileSaver::profiler_pthread_ = 0U; +static_assert(ProfileCompilationInfo::kIndividualInlineCacheSize == + InlineCache::kIndividualCacheSize, + "InlineCache and ProfileCompilationInfo do not agree on kIndividualCacheSize"); + // At what priority to schedule the saver threads. 9 is the lowest foreground priority on device. static constexpr int kProfileSaverPthreadPriority = 9; diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h index afbb3c122d74df426cae45ad1e5a01778a0b1d9a..02c8cd14749d900f283ed8e64e6b7d6caf6cba29 100644 --- a/runtime/jit/profile_saver.h +++ b/runtime/jit/profile_saver.h @@ -21,7 +21,7 @@ #include "base/safe_map.h" #include "dex/method_reference.h" #include "jit_code_cache.h" -#include "profile_compilation_info.h" +#include "profile/profile_compilation_info.h" #include "profile_saver_options.h" namespace art { diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc index 9126bea7d0d30ad9a1191b2546d446a54fccb2e0..2cb569c61a73dd65b05569bfd6fca36f60d404d3 100644 --- a/runtime/jit/profiling_info.cc +++ b/runtime/jit/profiling_info.cc @@ -26,12 +26,12 @@ namespace art { ProfilingInfo::ProfilingInfo(ArtMethod* method, const std::vector& entries) - : number_of_inline_caches_(entries.size()), - method_(method), - is_method_being_compiled_(false), - is_osr_method_being_compiled_(false), + : method_(method), + saved_entry_point_(nullptr), + number_of_inline_caches_(entries.size()), current_inline_uses_(0), - saved_entry_point_(nullptr) { + is_method_being_compiled_(false), + is_osr_method_being_compiled_(false) { memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache)); for (size_t i = 0; i < number_of_inline_caches_; ++i) { cache_[i].dex_pc_ = entries[i]; diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h index 788fa1f92bc86c1b57a096e6dad09dfcda1fe8df..a3dae8330a333f03658c18b4793479be12b8a9a4 100644 --- a/runtime/jit/profiling_info.h +++ b/runtime/jit/profiling_info.h @@ -132,27 +132,27 @@ class ProfilingInfo { private: ProfilingInfo(ArtMethod* method, const std::vector& entries); - // Number of instructions we are profiling in the ArtMethod. - const uint32_t number_of_inline_caches_; - // Method this profiling info is for. // Not 'const' as JVMTI introduces obsolete methods that we implement by creating new ArtMethods. // See JitCodeCache::MoveObsoleteMethod. ArtMethod* method_; - // Whether the ArtMethod is currently being compiled. This flag - // is implicitly guarded by the JIT code cache lock. - // TODO: Make the JIT code cache lock global. - bool is_method_being_compiled_; - bool is_osr_method_being_compiled_; + // Entry point of the corresponding ArtMethod, while the JIT code cache + // is poking for the liveness of compiled code. + const void* saved_entry_point_; + + // Number of instructions we are profiling in the ArtMethod. + const uint32_t number_of_inline_caches_; // When the compiler inlines the method associated to this ProfilingInfo, // it updates this counter so that the GC does not try to clear the inline caches. uint16_t current_inline_uses_; - // Entry point of the corresponding ArtMethod, while the JIT code cache - // is poking for the liveness of compiled code. - const void* saved_entry_point_; + // Whether the ArtMethod is currently being compiled. This flag + // is implicitly guarded by the JIT code cache lock. + // TODO: Make the JIT code cache lock global. + bool is_method_being_compiled_; + bool is_osr_method_being_compiled_; // Dynamically allocated array of size `number_of_inline_caches_`. InlineCache cache_[0]; diff --git a/runtime/jit/profiling_info_test.cc b/runtime/jit/profiling_info_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..8424610cf82c6b6f2ce6fddf3d77699238d826b2 --- /dev/null +++ b/runtime/jit/profiling_info_test.cc @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "art_method-inl.h" +#include "base/unix_file/fd_file.h" +#include "class_linker-inl.h" +#include "common_runtime_test.h" +#include "dex/dex_file.h" +#include "dex/dex_file_loader.h" +#include "dex/method_reference.h" +#include "dex/type_reference.h" +#include "handle_scope-inl.h" +#include "linear_alloc.h" +#include "mirror/class-inl.h" +#include "mirror/class_loader.h" +#include "profile/profile_compilation_info.h" +#include "scoped_thread_state_change-inl.h" + +namespace art { + +using Hotness = ProfileCompilationInfo::MethodHotness; + +static constexpr size_t kMaxMethodIds = 65535; + +class ProfileCompilationInfoTest : public CommonRuntimeTest { + public: + void PostRuntimeCreate() OVERRIDE { + allocator_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool())); + } + + protected: + std::vector GetVirtualMethods(jobject class_loader, + const std::string& clazz) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + StackHandleScope<1> hs(self); + Handle h_loader( + hs.NewHandle(self->DecodeJObject(class_loader)->AsClassLoader())); + ObjPtr klass = class_linker->FindClass(self, clazz.c_str(), h_loader); + + const auto pointer_size = class_linker->GetImagePointerSize(); + std::vector methods; + for (auto& m : klass->GetVirtualMethods(pointer_size)) { + methods.push_back(&m); + } + return methods; + } + + bool AddMethod(const std::string& dex_location, + uint32_t checksum, + uint16_t method_index, + ProfileCompilationInfo* info) { + return info->AddMethodIndex(Hotness::kFlagHot, + dex_location, + checksum, + method_index, + kMaxMethodIds); + } + + bool AddMethod(const std::string& dex_location, + uint32_t checksum, + uint16_t method_index, + const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi, + ProfileCompilationInfo* info) { + return info->AddMethod( + dex_location, checksum, method_index, kMaxMethodIds, pmi, Hotness::kFlagPostStartup); + } + + bool AddClass(const std::string& dex_location, + uint32_t checksum, + dex::TypeIndex type_index, + ProfileCompilationInfo* info) { + DexCacheResolvedClasses classes(dex_location, dex_location, checksum, kMaxMethodIds); + classes.AddClass(type_index); + return info->AddClasses({classes}); + } + + uint32_t GetFd(const ScratchFile& file) { + return static_cast(file.GetFd()); + } + + bool SaveProfilingInfo( + const std::string& filename, + const std::vector& methods, + const std::set& resolved_classes, + Hotness::Flag flags) { + ProfileCompilationInfo info; + std::vector profile_methods; + ScopedObjectAccess soa(Thread::Current()); + for (ArtMethod* method : methods) { + profile_methods.emplace_back( + MethodReference(method->GetDexFile(), method->GetDexMethodIndex())); + } + if (!info.AddMethods(profile_methods, flags) || !info.AddClasses(resolved_classes)) { + return false; + } + if (info.GetNumberOfMethods() != profile_methods.size()) { + return false; + } + ProfileCompilationInfo file_profile; + if (!file_profile.Load(filename, false)) { + return false; + } + if (!info.MergeWith(file_profile)) { + return false; + } + + return info.Save(filename, nullptr); + } + + // Saves the given art methods to a profile backed by 'filename' and adds + // some fake inline caches to it. The added inline caches are returned in + // the out map `profile_methods_map`. + bool SaveProfilingInfoWithFakeInlineCaches( + const std::string& filename, + const std::vector& methods, + Hotness::Flag flags, + /*out*/ SafeMap* profile_methods_map) { + ProfileCompilationInfo info; + std::vector profile_methods; + ScopedObjectAccess soa(Thread::Current()); + for (ArtMethod* method : methods) { + std::vector caches; + // Monomorphic + for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) { + std::vector classes; + classes.emplace_back(method->GetDexFile(), dex::TypeIndex(0)); + caches.emplace_back(dex_pc, /*is_missing_types*/false, classes); + } + // Polymorphic + for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) { + std::vector classes; + for (uint16_t k = 0; k < InlineCache::kIndividualCacheSize / 2; k++) { + classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k)); + } + caches.emplace_back(dex_pc, /*is_missing_types*/false, classes); + } + // Megamorphic + for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) { + std::vector classes; + for (uint16_t k = 0; k < 2 * InlineCache::kIndividualCacheSize; k++) { + classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k)); + } + caches.emplace_back(dex_pc, /*is_missing_types*/false, classes); + } + // Missing types + for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) { + std::vector classes; + caches.emplace_back(dex_pc, /*is_missing_types*/true, classes); + } + ProfileMethodInfo pmi(MethodReference(method->GetDexFile(), + method->GetDexMethodIndex()), + caches); + profile_methods.push_back(pmi); + profile_methods_map->Put(method, pmi); + } + + if (!info.AddMethods(profile_methods, flags) + || info.GetNumberOfMethods() != profile_methods.size()) { + return false; + } + return info.Save(filename, nullptr); + } + + // Creates an inline cache which will be destructed at the end of the test. + ProfileCompilationInfo::InlineCacheMap* CreateInlineCacheMap() { + used_inline_caches.emplace_back(new ProfileCompilationInfo::InlineCacheMap( + std::less(), allocator_->Adapter(kArenaAllocProfile))); + return used_inline_caches.back().get(); + } + + ProfileCompilationInfo::OfflineProfileMethodInfo ConvertProfileMethodInfo( + const ProfileMethodInfo& pmi) { + ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap(); + ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi(ic_map); + SafeMap dex_map; // dex files to profile index + for (const auto& inline_cache : pmi.inline_caches) { + ProfileCompilationInfo::DexPcData& dex_pc_data = + ic_map->FindOrAdd( + inline_cache.dex_pc, ProfileCompilationInfo::DexPcData(allocator_.get()))->second; + if (inline_cache.is_missing_types) { + dex_pc_data.SetIsMissingTypes(); + } + for (const auto& class_ref : inline_cache.classes) { + uint8_t dex_profile_index = dex_map.FindOrAdd(const_cast(class_ref.dex_file), + static_cast(dex_map.size()))->second; + dex_pc_data.AddClass(dex_profile_index, class_ref.TypeIndex()); + if (dex_profile_index >= offline_pmi.dex_references.size()) { + // This is a new dex. + const std::string& dex_key = ProfileCompilationInfo::GetProfileDexFileKey( + class_ref.dex_file->GetLocation()); + offline_pmi.dex_references.emplace_back(dex_key, + class_ref.dex_file->GetLocationChecksum(), + class_ref.dex_file->NumMethodIds()); + } + } + } + return offline_pmi; + } + + // Cannot sizeof the actual arrays so hard code the values here. + // They should not change anyway. + static constexpr int kProfileMagicSize = 4; + static constexpr int kProfileVersionSize = 4; + + std::unique_ptr allocator_; + + // Cache of inline caches generated during tests. + // This makes it easier to pass data between different utilities and ensure that + // caches are destructed at the end of the test. + std::vector> used_inline_caches; +}; + +TEST_F(ProfileCompilationInfoTest, SaveArtMethods) { + ScratchFile profile; + + Thread* self = Thread::Current(); + jobject class_loader; + { + ScopedObjectAccess soa(self); + class_loader = LoadDex("ProfileTestMultiDex"); + } + ASSERT_NE(class_loader, nullptr); + + // Save virtual methods from Main. + std::set resolved_classes; + std::vector main_methods = GetVirtualMethods(class_loader, "LMain;"); + ASSERT_TRUE(SaveProfilingInfo( + profile.GetFilename(), main_methods, resolved_classes, Hotness::kFlagPostStartup)); + + // Check that what we saved is in the profile. + ProfileCompilationInfo info1; + ASSERT_TRUE(info1.Load(GetFd(profile))); + ASSERT_EQ(info1.GetNumberOfMethods(), main_methods.size()); + { + ScopedObjectAccess soa(self); + for (ArtMethod* m : main_methods) { + Hotness h = info1.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())); + ASSERT_TRUE(h.IsHot()); + ASSERT_TRUE(h.IsPostStartup()); + } + } + + // Save virtual methods from Second. + std::vector second_methods = GetVirtualMethods(class_loader, "LSecond;"); + ASSERT_TRUE(SaveProfilingInfo( + profile.GetFilename(), second_methods, resolved_classes, Hotness::kFlagStartup)); + + // Check that what we saved is in the profile (methods form Main and Second). + ProfileCompilationInfo info2; + ASSERT_TRUE(profile.GetFile()->ResetOffset()); + ASSERT_TRUE(info2.Load(GetFd(profile))); + ASSERT_EQ(info2.GetNumberOfMethods(), main_methods.size() + second_methods.size()); + { + ScopedObjectAccess soa(self); + for (ArtMethod* m : main_methods) { + Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())); + ASSERT_TRUE(h.IsHot()); + ASSERT_TRUE(h.IsPostStartup()); + } + for (ArtMethod* m : second_methods) { + Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())); + ASSERT_TRUE(h.IsHot()); + ASSERT_TRUE(h.IsStartup()); + } + } +} + +TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) { + ScratchFile profile; + + Thread* self = Thread::Current(); + jobject class_loader; + { + ScopedObjectAccess soa(self); + class_loader = LoadDex("ProfileTestMultiDex"); + } + ASSERT_NE(class_loader, nullptr); + + // Save virtual methods from Main. + std::set resolved_classes; + std::vector main_methods = GetVirtualMethods(class_loader, "LMain;"); + + SafeMap profile_methods_map; + ASSERT_TRUE(SaveProfilingInfoWithFakeInlineCaches( + profile.GetFilename(), main_methods, Hotness::kFlagStartup, &profile_methods_map)); + + // Check that what we saved is in the profile. + ProfileCompilationInfo info; + ASSERT_TRUE(info.Load(GetFd(profile))); + ASSERT_EQ(info.GetNumberOfMethods(), main_methods.size()); + { + ScopedObjectAccess soa(self); + for (ArtMethod* m : main_methods) { + Hotness h = info.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())); + ASSERT_TRUE(h.IsHot()); + ASSERT_TRUE(h.IsStartup()); + const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second; + std::unique_ptr offline_pmi = + info.GetMethod(m->GetDexFile()->GetLocation(), + m->GetDexFile()->GetLocationChecksum(), + m->GetDexMethodIndex()); + ASSERT_TRUE(offline_pmi != nullptr); + ProfileCompilationInfo::OfflineProfileMethodInfo converted_pmi = + ConvertProfileMethodInfo(pmi); + ASSERT_EQ(converted_pmi, *offline_pmi); + } + } +} + +} // namespace art diff --git a/runtime/check_jni.cc b/runtime/jni/check_jni.cc similarity index 99% rename from runtime/check_jni.cc rename to runtime/jni/check_jni.cc index b1ededf2bc86da74c916fc204d44bc02599c8a7b..7919c32737b87944d58e3ec639a8f752ad3217ce 100644 --- a/runtime/check_jni.cc +++ b/runtime/jni/check_jni.cc @@ -31,12 +31,15 @@ #include "base/time_utils.h" #include "class_linker-inl.h" #include "class_linker.h" +#include "class_root.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "gc/space/space.h" #include "java_vm_ext.h" #include "jni_internal.h" #include "mirror/class-inl.h" +#include "mirror/field.h" +#include "mirror/method.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/string-inl.h" @@ -636,9 +639,11 @@ class ScopedCheck { AbortF("expected non-null method"); return false; } - mirror::Class* c = method->GetClass(); - if (soa.Decode(WellKnownClasses::java_lang_reflect_Method) != c && - soa.Decode(WellKnownClasses::java_lang_reflect_Constructor) != c) { + ObjPtr> class_roots = + Runtime::Current()->GetClassLinker()->GetClassRoots(); + ObjPtr c = method->GetClass(); + if (c != GetClassRoot(class_roots) && + c != GetClassRoot(class_roots)) { AbortF("expected java.lang.reflect.Method or " "java.lang.reflect.Constructor but got object of type %s: %p", method->PrettyTypeOf().c_str(), jmethod); @@ -667,8 +672,8 @@ class ScopedCheck { AbortF("expected non-null java.lang.reflect.Field"); return false; } - mirror::Class* c = field->GetClass(); - if (soa.Decode(WellKnownClasses::java_lang_reflect_Field) != c) { + ObjPtr c = field->GetClass(); + if (GetClassRoot() != c) { AbortF("expected java.lang.reflect.Field but got object of type %s: %p", field->PrettyTypeOf().c_str(), jfield); return false; @@ -2168,7 +2173,7 @@ class CheckJNI { return result; } - static jobject NewObjectA(JNIEnv* env, jclass c, jmethodID mid, jvalue* vargs) { + static jobject NewObjectA(JNIEnv* env, jclass c, jmethodID mid, const jvalue* vargs) { CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); @@ -2263,16 +2268,16 @@ class CheckJNI { FIELD_ACCESSORS(jdouble, Double, Primitive::kPrimDouble, D, D) #undef FIELD_ACCESSORS - static void CallVoidMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* vargs) { + static void CallVoidMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* vargs) { CallMethodA(__FUNCTION__, env, obj, nullptr, mid, vargs, Primitive::kPrimVoid, kVirtual); } static void CallNonvirtualVoidMethodA(JNIEnv* env, jobject obj, jclass c, jmethodID mid, - jvalue* vargs) { + const jvalue* vargs) { CallMethodA(__FUNCTION__, env, obj, c, mid, vargs, Primitive::kPrimVoid, kDirect); } - static void CallStaticVoidMethodA(JNIEnv* env, jclass c, jmethodID mid, jvalue* vargs) { + static void CallStaticVoidMethodA(JNIEnv* env, jclass c, jmethodID mid, const jvalue* vargs) { CallMethodA(__FUNCTION__, env, nullptr, c, mid, vargs, Primitive::kPrimVoid, kStatic); } @@ -2311,16 +2316,16 @@ class CheckJNI { } #define CALL(rtype, name, ptype, shorty) \ - static rtype Call##name##MethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* vargs) { \ + static rtype Call##name##MethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* vargs) { \ return CallMethodA(__FUNCTION__, env, obj, nullptr, mid, vargs, ptype, kVirtual).shorty; \ } \ \ static rtype CallNonvirtual##name##MethodA(JNIEnv* env, jobject obj, jclass c, jmethodID mid, \ - jvalue* vargs) { \ + const jvalue* vargs) { \ return CallMethodA(__FUNCTION__, env, obj, c, mid, vargs, ptype, kDirect).shorty; \ } \ \ - static rtype CallStatic##name##MethodA(JNIEnv* env, jclass c, jmethodID mid, jvalue* vargs) { \ + static rtype CallStatic##name##MethodA(JNIEnv* env, jclass c, jmethodID mid, const jvalue* vargs) { \ return CallMethodA(__FUNCTION__, env, nullptr, c, mid, vargs, ptype, kStatic).shorty; \ } \ \ @@ -3065,7 +3070,7 @@ class CheckJNI { } static JniValueType CallMethodA(const char* function_name, JNIEnv* env, jobject obj, jclass c, - jmethodID mid, jvalue* vargs, Primitive::Type type, + jmethodID mid, const jvalue* vargs, Primitive::Type type, InvokeType invoke) { CHECK_ATTACHED_THREAD(function_name, JniValueType()); ScopedObjectAccess soa(env); diff --git a/runtime/check_jni.h b/runtime/jni/check_jni.h similarity index 87% rename from runtime/check_jni.h rename to runtime/jni/check_jni.h index f41abf81ce8b912fef7735054fb3637e2f00ad3c..10fdfe859d468123bf5a85dcdd923ceb8f2962da 100644 --- a/runtime/check_jni.h +++ b/runtime/jni/check_jni.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_CHECK_JNI_H_ -#define ART_RUNTIME_CHECK_JNI_H_ +#ifndef ART_RUNTIME_JNI_CHECK_JNI_H_ +#define ART_RUNTIME_JNI_CHECK_JNI_H_ #include @@ -26,4 +26,4 @@ const JNIInvokeInterface* GetCheckJniInvokeInterface(); } // namespace art -#endif // ART_RUNTIME_CHECK_JNI_H_ +#endif // ART_RUNTIME_JNI_CHECK_JNI_H_ diff --git a/runtime/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc similarity index 98% rename from runtime/java_vm_ext.cc rename to runtime/jni/java_vm_ext.cc index da4c4b2fa48ae28d09e5217b6207a512aa178499..44679a5afae38243874b4c7fd78ef369117ee6ce 100644 --- a/runtime/java_vm_ext.cc +++ b/runtime/jni/java_vm_ext.cc @@ -736,14 +736,14 @@ void JavaVMExt::DisallowNewWeakGlobals() { // mutator lock exclusively held so that we don't have any threads in the middle of // DecodeWeakGlobal. Locks::mutator_lock_->AssertExclusiveHeld(self); - allow_accessing_weak_globals_.StoreSequentiallyConsistent(false); + allow_accessing_weak_globals_.store(false, std::memory_order_seq_cst); } void JavaVMExt::AllowNewWeakGlobals() { CHECK(!kUseReadBarrier); Thread* self = Thread::Current(); MutexLock mu(self, *Locks::jni_weak_globals_lock_); - allow_accessing_weak_globals_.StoreSequentiallyConsistent(true); + allow_accessing_weak_globals_.store(true, std::memory_order_seq_cst); weak_globals_add_condition_.Broadcast(self); } @@ -770,7 +770,7 @@ inline bool JavaVMExt::MayAccessWeakGlobalsUnlocked(Thread* self) const { DCHECK(self != nullptr); return kUseReadBarrier ? self->GetWeakRefAccessEnabled() : - allow_accessing_weak_globals_.LoadSequentiallyConsistent(); + allow_accessing_weak_globals_.load(std::memory_order_seq_cst); } ObjPtr JavaVMExt::DecodeWeakGlobal(Thread* self, IndirectRef ref) { @@ -809,7 +809,7 @@ ObjPtr JavaVMExt::DecodeWeakGlobalDuringShutdown(Thread* self, I } // self can be null during a runtime shutdown. ~Runtime()->~ClassLinker()->DecodeWeakGlobal(). if (!kUseReadBarrier) { - DCHECK(allow_accessing_weak_globals_.LoadSequentiallyConsistent()); + DCHECK(allow_accessing_weak_globals_.load(std::memory_order_seq_cst)); } return weak_globals_.SynchronizedGet(ref); } @@ -912,7 +912,11 @@ bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, return utf.c_str(); } } - env->ExceptionClear(); + if (env->ExceptionCheck()) { + // We can't do much better logging, really. So leave it with a Describe. + env->ExceptionDescribe(); + env->ExceptionClear(); + } return "(Error calling toString)"; } return "null"; diff --git a/runtime/java_vm_ext.h b/runtime/jni/java_vm_ext.h similarity index 98% rename from runtime/java_vm_ext.h rename to runtime/jni/java_vm_ext.h index ac20afecd4aa4f153697d7dac8d058e6cd620b34..408d3542ed6fe695cddce4b9f73d5514a01b3d88 100644 --- a/runtime/java_vm_ext.h +++ b/runtime/jni/java_vm_ext.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_JAVA_VM_EXT_H_ -#define ART_RUNTIME_JAVA_VM_EXT_H_ +#ifndef ART_RUNTIME_JNI_JAVA_VM_EXT_H_ +#define ART_RUNTIME_JNI_JAVA_VM_EXT_H_ #include "jni.h" @@ -262,4 +262,4 @@ class JavaVMExt : public JavaVM { } // namespace art -#endif // ART_RUNTIME_JAVA_VM_EXT_H_ +#endif // ART_RUNTIME_JNI_JAVA_VM_EXT_H_ diff --git a/runtime/java_vm_ext_test.cc b/runtime/jni/java_vm_ext_test.cc similarity index 99% rename from runtime/java_vm_ext_test.cc rename to runtime/jni/java_vm_ext_test.cc index a15ec562749058d94d57a192ff8e8aa52ff37a0f..74e4a30905c5aeadc07fabcd503ffd2a07ddfad9 100644 --- a/runtime/java_vm_ext_test.cc +++ b/runtime/jni/java_vm_ext_test.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "jni_internal.h" +#include "jni/jni_internal.h" #include diff --git a/runtime/jni_env_ext-inl.h b/runtime/jni/jni_env_ext-inl.h similarity index 92% rename from runtime/jni_env_ext-inl.h rename to runtime/jni/jni_env_ext-inl.h index 14f708b18dfc09542934081169b1ac6deadd1919..7609a9e01a28631a16ffd1e24bbcd46ab30ce72f 100644 --- a/runtime/jni_env_ext-inl.h +++ b/runtime/jni/jni_env_ext-inl.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_JNI_ENV_EXT_INL_H_ -#define ART_RUNTIME_JNI_ENV_EXT_INL_H_ +#ifndef ART_RUNTIME_JNI_JNI_ENV_EXT_INL_H_ +#define ART_RUNTIME_JNI_JNI_ENV_EXT_INL_H_ #include "jni_env_ext.h" @@ -51,4 +51,4 @@ inline T JNIEnvExt::AddLocalReference(ObjPtr obj) { } // namespace art -#endif // ART_RUNTIME_JNI_ENV_EXT_INL_H_ +#endif // ART_RUNTIME_JNI_JNI_ENV_EXT_INL_H_ diff --git a/runtime/jni_env_ext.cc b/runtime/jni/jni_env_ext.cc similarity index 100% rename from runtime/jni_env_ext.cc rename to runtime/jni/jni_env_ext.cc diff --git a/runtime/jni_env_ext.h b/runtime/jni/jni_env_ext.h similarity index 98% rename from runtime/jni_env_ext.h rename to runtime/jni/jni_env_ext.h index 291ac48e8672fb2c3acc611163b82dbaaf38ea39..3a007adcf002d1dced6c2be651c863793dc1f708 100644 --- a/runtime/jni_env_ext.h +++ b/runtime/jni/jni_env_ext.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_JNI_ENV_EXT_H_ -#define ART_RUNTIME_JNI_ENV_EXT_H_ +#ifndef ART_RUNTIME_JNI_JNI_ENV_EXT_H_ +#define ART_RUNTIME_JNI_JNI_ENV_EXT_H_ #include @@ -229,4 +229,4 @@ class ScopedJniEnvLocalRefState { } // namespace art -#endif // ART_RUNTIME_JNI_ENV_EXT_H_ +#endif // ART_RUNTIME_JNI_JNI_ENV_EXT_H_ diff --git a/runtime/jni_internal.cc b/runtime/jni/jni_internal.cc similarity index 96% rename from runtime/jni_internal.cc rename to runtime/jni/jni_internal.cc index cd66a60376ecc89d2652440238724f1fa2a2ff9d..a02e76ae547b54c75df220c7b3b8b1c3c6c0e437 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni/jni_internal.cc @@ -33,6 +33,7 @@ #include "base/safe_map.h" #include "base/stl_util.h" #include "class_linker-inl.h" +#include "class_root.h" #include "dex/dex_file-inl.h" #include "dex/utf.h" #include "fault_handler.h" @@ -305,7 +306,7 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con return nullptr; } ArtField* field = nullptr; - mirror::Class* field_type; + ObjPtr field_type; ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); if (sig[1] != '\0') { Handle class_loader(hs.NewHandle(c->GetClassLoader())); @@ -346,8 +347,11 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con return jni::EncodeArtField(field); } -static void ThrowAIOOBE(ScopedObjectAccess& soa, mirror::Array* array, jsize start, - jsize length, const char* identifier) +static void ThrowAIOOBE(ScopedObjectAccess& soa, + ObjPtr array, + jsize start, + jsize length, + const char* identifier) REQUIRES_SHARED(Locks::mutator_lock_) { std::string type(array->PrettyTypeOf()); soa.Self()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;", @@ -434,7 +438,9 @@ static JavaVMExt* JavaVmExtFromEnv(JNIEnv* env) { } template -static ArtMethod* FindMethod(mirror::Class* c, const StringPiece& name, const StringPiece& sig) +static ArtMethod* FindMethod(ObjPtr c, + const StringPiece& name, + const StringPiece& sig) REQUIRES_SHARED(Locks::mutator_lock_) { auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); for (auto& method : c->GetMethods(pointer_size)) { @@ -462,7 +468,7 @@ class JNI { ClassLinker* class_linker = runtime->GetClassLinker(); std::string descriptor(NormalizeJniClassDescriptor(name)); ScopedObjectAccess soa(env); - mirror::Class* c = nullptr; + ObjPtr c = nullptr; if (runtime->IsStarted()) { StackHandleScope<1> hs(soa.Self()); Handle class_loader(hs.NewHandle(GetClassLoader(soa))); @@ -483,7 +489,7 @@ class JNI { CHECK_NON_NULL_ARGUMENT(jlr_field); ScopedObjectAccess soa(env); ObjPtr obj_field = soa.Decode(jlr_field); - if (obj_field->GetClass() != mirror::Field::StaticClass()) { + if (obj_field->GetClass() != GetClassRoot()) { // Not even a java.lang.reflect.Field, return null. TODO, is this check necessary? return nullptr; } @@ -608,7 +614,7 @@ class JNI { static jthrowable ExceptionOccurred(JNIEnv* env) { ScopedObjectAccess soa(env); - mirror::Object* exception = soa.Self()->GetException(); + ObjPtr exception = soa.Self()->GetException(); return soa.AddLocalReference(exception); } @@ -755,7 +761,7 @@ class JNI { return local_result; } - static jobject NewObjectA(JNIEnv* env, jclass java_class, jmethodID mid, jvalue* args) { + static jobject NewObjectA(JNIEnv* env, jclass java_class, jmethodID mid, const jvalue* args) { CHECK_NON_NULL_ARGUMENT(java_class); CHECK_NON_NULL_ARGUMENT(mid); ScopedObjectAccess soa(env); @@ -818,7 +824,7 @@ class JNI { return soa.AddLocalReference(result.GetL()); } - static jobject CallObjectMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) { + static jobject CallObjectMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* args) { CHECK_NON_NULL_ARGUMENT(obj); CHECK_NON_NULL_ARGUMENT(mid); ScopedObjectAccess soa(env); @@ -844,7 +850,7 @@ class JNI { return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetZ(); } - static jboolean CallBooleanMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) { + static jboolean CallBooleanMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); @@ -869,7 +875,7 @@ class JNI { return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetB(); } - static jbyte CallByteMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) { + static jbyte CallByteMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); @@ -894,7 +900,7 @@ class JNI { return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetC(); } - static jchar CallCharMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) { + static jchar CallCharMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); @@ -919,7 +925,7 @@ class JNI { return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetD(); } - static jdouble CallDoubleMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) { + static jdouble CallDoubleMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); @@ -944,7 +950,7 @@ class JNI { return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetF(); } - static jfloat CallFloatMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) { + static jfloat CallFloatMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); @@ -969,7 +975,7 @@ class JNI { return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetI(); } - static jint CallIntMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) { + static jint CallIntMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); @@ -994,7 +1000,7 @@ class JNI { return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetJ(); } - static jlong CallLongMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) { + static jlong CallLongMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); @@ -1019,7 +1025,7 @@ class JNI { return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetS(); } - static jshort CallShortMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) { + static jshort CallShortMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); @@ -1043,7 +1049,7 @@ class JNI { InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args); } - static void CallVoidMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) { + static void CallVoidMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_VOID(obj); CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid); ScopedObjectAccess soa(env); @@ -1072,7 +1078,7 @@ class JNI { } static jobject CallNonvirtualObjectMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, - jvalue* args) { + const jvalue* args) { CHECK_NON_NULL_ARGUMENT(obj); CHECK_NON_NULL_ARGUMENT(mid); ScopedObjectAccess soa(env); @@ -1101,7 +1107,7 @@ class JNI { } static jboolean CallNonvirtualBooleanMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, - jvalue* args) { + const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); @@ -1128,7 +1134,7 @@ class JNI { } static jbyte CallNonvirtualByteMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, - jvalue* args) { + const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); @@ -1155,7 +1161,7 @@ class JNI { } static jchar CallNonvirtualCharMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, - jvalue* args) { + const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); @@ -1182,7 +1188,7 @@ class JNI { } static jshort CallNonvirtualShortMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, - jvalue* args) { + const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); @@ -1209,7 +1215,7 @@ class JNI { } static jint CallNonvirtualIntMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, - jvalue* args) { + const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); @@ -1236,7 +1242,7 @@ class JNI { } static jlong CallNonvirtualLongMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, - jvalue* args) { + const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); @@ -1263,7 +1269,7 @@ class JNI { } static jfloat CallNonvirtualFloatMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, - jvalue* args) { + const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); @@ -1290,7 +1296,7 @@ class JNI { } static jdouble CallNonvirtualDoubleMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, - jvalue* args) { + const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); @@ -1316,7 +1322,7 @@ class JNI { } static void CallNonvirtualVoidMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, - jvalue* args) { + const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_VOID(obj); CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid); ScopedObjectAccess soa(env); @@ -1556,7 +1562,7 @@ class JNI { return soa.AddLocalReference(result.GetL()); } - static jobject CallStaticObjectMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) { + static jobject CallStaticObjectMethodA(JNIEnv* env, jclass, jmethodID mid, const jvalue* args) { CHECK_NON_NULL_ARGUMENT(mid); ScopedObjectAccess soa(env); JValue result(InvokeWithJValues(soa, nullptr, mid, args)); @@ -1579,7 +1585,7 @@ class JNI { return InvokeWithVarArgs(soa, nullptr, mid, args).GetZ(); } - static jboolean CallStaticBooleanMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) { + static jboolean CallStaticBooleanMethodA(JNIEnv* env, jclass, jmethodID mid, const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, nullptr, mid, args).GetZ(); @@ -1601,7 +1607,7 @@ class JNI { return InvokeWithVarArgs(soa, nullptr, mid, args).GetB(); } - static jbyte CallStaticByteMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) { + static jbyte CallStaticByteMethodA(JNIEnv* env, jclass, jmethodID mid, const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, nullptr, mid, args).GetB(); @@ -1623,7 +1629,7 @@ class JNI { return InvokeWithVarArgs(soa, nullptr, mid, args).GetC(); } - static jchar CallStaticCharMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) { + static jchar CallStaticCharMethodA(JNIEnv* env, jclass, jmethodID mid, const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, nullptr, mid, args).GetC(); @@ -1645,7 +1651,7 @@ class JNI { return InvokeWithVarArgs(soa, nullptr, mid, args).GetS(); } - static jshort CallStaticShortMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) { + static jshort CallStaticShortMethodA(JNIEnv* env, jclass, jmethodID mid, const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, nullptr, mid, args).GetS(); @@ -1667,7 +1673,7 @@ class JNI { return InvokeWithVarArgs(soa, nullptr, mid, args).GetI(); } - static jint CallStaticIntMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) { + static jint CallStaticIntMethodA(JNIEnv* env, jclass, jmethodID mid, const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, nullptr, mid, args).GetI(); @@ -1689,7 +1695,7 @@ class JNI { return InvokeWithVarArgs(soa, nullptr, mid, args).GetJ(); } - static jlong CallStaticLongMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) { + static jlong CallStaticLongMethodA(JNIEnv* env, jclass, jmethodID mid, const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, nullptr, mid, args).GetJ(); @@ -1711,7 +1717,7 @@ class JNI { return InvokeWithVarArgs(soa, nullptr, mid, args).GetF(); } - static jfloat CallStaticFloatMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) { + static jfloat CallStaticFloatMethodA(JNIEnv* env, jclass, jmethodID mid, const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, nullptr, mid, args).GetF(); @@ -1733,7 +1739,7 @@ class JNI { return InvokeWithVarArgs(soa, nullptr, mid, args).GetD(); } - static jdouble CallStaticDoubleMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) { + static jdouble CallStaticDoubleMethodA(JNIEnv* env, jclass, jmethodID mid, const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); ScopedObjectAccess soa(env); return InvokeWithJValues(soa, nullptr, mid, args).GetD(); @@ -1754,7 +1760,7 @@ class JNI { InvokeWithVarArgs(soa, nullptr, mid, args); } - static void CallStaticVoidMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) { + static void CallStaticVoidMethodA(JNIEnv* env, jclass, jmethodID mid, const jvalue* args) { CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid); ScopedObjectAccess soa(env); InvokeWithJValues(soa, nullptr, mid, args); @@ -1979,7 +1985,7 @@ class JNI { ObjPtr> array = soa.Decode>(java_array); ObjPtr value = soa.Decode(java_value); - array->Set(index, value.Ptr()); + array->Set(index, value); } static jbooleanArray NewBooleanArray(JNIEnv* env, jsize length) { @@ -2022,7 +2028,7 @@ class JNI { ScopedObjectAccess soa(env); ObjPtr array_class; { - ObjPtr element_class = soa.Decode(element_jclass).Ptr(); + ObjPtr element_class = soa.Decode(element_jclass); if (UNLIKELY(element_class->IsPrimitive())) { soa.Vm()->JniAbortF("NewObjectArray", "not an object type: %s", @@ -2030,19 +2036,19 @@ class JNI { return nullptr; } ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - array_class = class_linker->FindArrayClass(soa.Self(), &element_class); + array_class = class_linker->FindArrayClass(soa.Self(), element_class); if (UNLIKELY(array_class == nullptr)) { return nullptr; } } // Allocate and initialize if necessary. - mirror::ObjectArray* result = + ObjPtr> result = mirror::ObjectArray::Alloc(soa.Self(), array_class, length); if (result != nullptr && initial_element != nullptr) { ObjPtr initial_object = soa.Decode(initial_element); if (initial_object != nullptr) { - mirror::Class* element_class = result->GetClass()->GetComponentType(); + ObjPtr element_class = result->GetClass()->GetComponentType(); if (UNLIKELY(!element_class->IsAssignableFrom(initial_object->GetClass()))) { soa.Vm()->JniAbortF("NewObjectArray", "cannot assign object of type '%s' to array with " "element type of '%s'", @@ -2051,7 +2057,7 @@ class JNI { return nullptr; } else { for (jsize i = 0; i < length; ++i) { - result->SetWithoutChecks(i, initial_object.Ptr()); + result->SetWithoutChecks(i, initial_object); } } } @@ -2101,7 +2107,7 @@ class JNI { return; } const size_t component_size = array->GetClass()->GetComponentSize(); - ReleasePrimitiveArray(soa, array.Ptr(), component_size, elements, mode); + ReleasePrimitiveArray(soa, array, component_size, elements, mode); } static jboolean* GetBooleanArrayElements(JNIEnv* env, jbooleanArray array, jboolean* is_copy) { @@ -2338,13 +2344,13 @@ class JNI { current_class != nullptr; current_class = current_class->GetSuperClass()) { // Search first only comparing methods which are native. - m = FindMethod(current_class.Ptr(), name, sig); + m = FindMethod(current_class, name, sig); if (m != nullptr) { break; } // Search again comparing to all methods, to find non-native methods that match. - m = FindMethod(current_class.Ptr(), name, sig); + m = FindMethod(current_class, name, sig); if (m != nullptr) { break; } @@ -2542,35 +2548,37 @@ class JNI { soa.Vm()->JniAbortF("NewPrimitiveArray", "negative array length: %d", length); return nullptr; } - ArtT* result = ArtT::Alloc(soa.Self(), length); + ObjPtr result = ArtT::Alloc(soa.Self(), length); return soa.AddLocalReference(result); } template - static ArtArrayT* DecodeAndCheckArrayType(ScopedObjectAccess& soa, JArrayT java_array, - const char* fn_name, const char* operation) + static ObjPtr DecodeAndCheckArrayType(ScopedObjectAccess& soa, + JArrayT java_array, + const char* fn_name, + const char* operation) REQUIRES_SHARED(Locks::mutator_lock_) { ObjPtr array = soa.Decode(java_array); - if (UNLIKELY(ArtArrayT::GetArrayClass() != array->GetClass())) { + ObjPtr expected_array_class = GetClassRoot(); + if (UNLIKELY(expected_array_class != array->GetClass())) { soa.Vm()->JniAbortF(fn_name, "attempt to %s %s primitive array elements with an object of type %s", operation, mirror::Class::PrettyDescriptor( - ArtArrayT::GetArrayClass()->GetComponentType()).c_str(), + expected_array_class->GetComponentType()).c_str(), mirror::Class::PrettyDescriptor(array->GetClass()).c_str()); return nullptr; } DCHECK_EQ(sizeof(ElementT), array->GetClass()->GetComponentSize()); - return array.Ptr(); + return array; } template static ElementT* GetPrimitiveArray(JNIEnv* env, ArrayT java_array, jboolean* is_copy) { CHECK_NON_NULL_ARGUMENT(java_array); ScopedObjectAccess soa(env); - ArtArrayT* array = DecodeAndCheckArrayType(soa, java_array, - "GetArrayElements", - "get"); + ObjPtr array = DecodeAndCheckArrayType( + soa, java_array, "GetArrayElements", "get"); if (UNLIKELY(array == nullptr)) { return nullptr; } @@ -2596,17 +2604,19 @@ class JNI { static void ReleasePrimitiveArray(JNIEnv* env, ArrayT java_array, ElementT* elements, jint mode) { CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_array); ScopedObjectAccess soa(env); - ArtArrayT* array = DecodeAndCheckArrayType(soa, java_array, - "ReleaseArrayElements", - "release"); + ObjPtr array = DecodeAndCheckArrayType( + soa, java_array, "ReleaseArrayElements", "release"); if (array == nullptr) { return; } ReleasePrimitiveArray(soa, array, sizeof(ElementT), elements, mode); } - static void ReleasePrimitiveArray(ScopedObjectAccess& soa, mirror::Array* array, - size_t component_size, void* elements, jint mode) + static void ReleasePrimitiveArray(ScopedObjectAccess& soa, + ObjPtr array, + size_t component_size, + void* elements, + jint mode) REQUIRES_SHARED(Locks::mutator_lock_) { void* array_data = array->GetRawData(component_size, 0); gc::Heap* heap = Runtime::Current()->GetHeap(); @@ -2649,10 +2659,8 @@ class JNI { jsize start, jsize length, ElementT* buf) { CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_array); ScopedObjectAccess soa(env); - ArtArrayT* array = - DecodeAndCheckArrayType(soa, java_array, - "GetPrimitiveArrayRegion", - "get region of"); + ObjPtr array = DecodeAndCheckArrayType( + soa, java_array, "GetPrimitiveArrayRegion", "get region of"); if (array != nullptr) { if (start < 0 || length < 0 || length > array->GetLength() - start) { ThrowAIOOBE(soa, array, start, length, "src"); @@ -2669,10 +2677,8 @@ class JNI { jsize start, jsize length, const ElementT* buf) { CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_array); ScopedObjectAccess soa(env); - ArtArrayT* array = - DecodeAndCheckArrayType(soa, java_array, - "SetPrimitiveArrayRegion", - "set region of"); + ObjPtr array = DecodeAndCheckArrayType( + soa, java_array, "SetPrimitiveArrayRegion", "set region of"); if (array != nullptr) { if (start < 0 || length < 0 || length > array->GetLength() - start) { ThrowAIOOBE(soa, array, start, length, "dst"); diff --git a/runtime/jni_internal.h b/runtime/jni/jni_internal.h similarity index 92% rename from runtime/jni_internal.h rename to runtime/jni/jni_internal.h index 2c90b3ba789e55f4fb3518bda6012bad9da5f70e..d0426617ebd8c385a34998492fa6ec4bd4c43ad0 100644 --- a/runtime/jni_internal.h +++ b/runtime/jni/jni_internal.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_JNI_INTERNAL_H_ -#define ART_RUNTIME_JNI_INTERNAL_H_ +#ifndef ART_RUNTIME_JNI_JNI_INTERNAL_H_ +#define ART_RUNTIME_JNI_JNI_INTERNAL_H_ #include #include @@ -59,4 +59,4 @@ static inline ArtMethod* DecodeArtMethod(jmethodID method_id) { std::ostream& operator<<(std::ostream& os, const jobjectRefType& rhs); -#endif // ART_RUNTIME_JNI_INTERNAL_H_ +#endif // ART_RUNTIME_JNI_JNI_INTERNAL_H_ diff --git a/runtime/jni_internal_test.cc b/runtime/jni/jni_internal_test.cc similarity index 99% rename from runtime/jni_internal_test.cc rename to runtime/jni/jni_internal_test.cc index 5d74181cef44ec19d898517c17f0913cebe81f5b..a25049e6811d33621da557c23d7acc20ebaccd85 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni/jni_internal_test.cc @@ -91,7 +91,7 @@ class JniInternalTest : public CommonCompilerTest { jclass GetPrimitiveClass(char descriptor) { ScopedObjectAccess soa(env_); - mirror::Class* c = class_linker_->FindPrimitiveClass(descriptor); + ObjPtr c = class_linker_->FindPrimitiveClass(descriptor); CHECK(c != nullptr); return soa.AddLocalReference(c); } @@ -624,7 +624,7 @@ class JniInternalTest : public CommonCompilerTest { StackHandleScope<1> hs(soa.Self()); Handle loader( hs.NewHandle(soa.Decode(class_loader_))); - mirror::Class* c = class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader); + ObjPtr c = class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader); const auto pointer_size = class_linker_->GetImagePointerSize(); ArtMethod* method = c->FindClassMethod(method_name, method_sig, pointer_size); ASSERT_TRUE(method != nullptr) << method_name << " " << method_sig; diff --git a/runtime/jobject_comparator.cc b/runtime/jobject_comparator.cc deleted file mode 100644 index 4c45e3839bb4abd14707c83487a594b363b58a5b..0000000000000000000000000000000000000000 --- a/runtime/jobject_comparator.cc +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "jobject_comparator.h" - -#include "mirror/array-inl.h" -#include "mirror/class-inl.h" -#include "mirror/object-inl.h" -#include "scoped_thread_state_change-inl.h" - -namespace art { - -bool JobjectComparator::operator()(jobject jobj1, jobject jobj2) const { - // Ensure null references and cleared jweaks appear at the end. - if (jobj1 == nullptr) { - return true; - } else if (jobj2 == nullptr) { - return false; - } - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); - Handle obj1(hs.NewHandle(soa.Decode(jobj1))); - Handle obj2(hs.NewHandle(soa.Decode(jobj2))); - if (obj1 == nullptr) { - return true; - } else if (obj2 == nullptr) { - return false; - } - // Sort by class... - if (obj1->GetClass() != obj2->GetClass()) { - return obj1->GetClass()->IdentityHashCode() < obj2->GetClass()->IdentityHashCode(); - } - // ...then by size... - const size_t count1 = obj1->SizeOf(); - const size_t count2 = obj2->SizeOf(); - if (count1 != count2) { - return count1 < count2; - } - // ...and finally by identity hash code. - return obj1->IdentityHashCode() < obj2->IdentityHashCode(); -} - -} // namespace art diff --git a/runtime/jvalue-inl.h b/runtime/jvalue-inl.h index 25e34b2a74e4c826f2969e4c0bb2b6e3fdf77dcb..5bd4f176f6ed88cf8d2c7b38a1ba5019894b6acc 100644 --- a/runtime/jvalue-inl.h +++ b/runtime/jvalue-inl.h @@ -19,7 +19,7 @@ #include "jvalue.h" -#include "obj_ptr.h" +#include "obj_ptr-inl.h" namespace art { diff --git a/runtime/jvalue.h b/runtime/jvalue.h index 266abcf399d915da4a85dd7da12812baccfa5975..b42d995d5ccd007cb598d0c949de7bd5980a5b0d 100644 --- a/runtime/jvalue.h +++ b/runtime/jvalue.h @@ -33,7 +33,7 @@ union PACKED(alignof(mirror::Object*)) JValue { // We default initialize JValue instances to all-zeros. JValue() : j(0) {} - template static JValue FromPrimitive(T v); + template ALWAYS_INLINE static JValue FromPrimitive(T v); int8_t GetB() const { return b; } void SetB(int8_t new_b) { @@ -62,6 +62,7 @@ union PACKED(alignof(mirror::Object*)) JValue { mirror::Object* GetL() const REQUIRES_SHARED(Locks::mutator_lock_) { return l; } + ALWAYS_INLINE void SetL(ObjPtr new_l) REQUIRES_SHARED(Locks::mutator_lock_); int16_t GetS() const { return s; } diff --git a/runtime/linear_alloc.h b/runtime/linear_alloc.h index 384b2e3243495024752530fac623b2a9a008ffd9..87086f29d440ea595d8439e8a45b425ad5f824b4 100644 --- a/runtime/linear_alloc.h +++ b/runtime/linear_alloc.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_LINEAR_ALLOC_H_ #include "base/arena_allocator.h" +#include "base/mutex.h" namespace art { diff --git a/runtime/lock_word.h b/runtime/lock_word.h index e89beb6d41bc19c926b7db9b0a0ecf1f099f3110..ce7fe34b2270c05d6ac89321ab6d2a1081b0e288 100644 --- a/runtime/lock_word.h +++ b/runtime/lock_word.h @@ -32,9 +32,9 @@ class Object; class Monitor; -/* The lock value itself as stored in mirror::Object::monitor_. The two most significant bits of - * the state. The four possible states are fat locked, thin/unlocked, hash code, and forwarding - * address. +/* The lock value itself as stored in mirror::Object::monitor_. The two most significant bits + * encode the state. The four possible states are fat locked, thin/unlocked, hash code, and + * forwarding address. * * When the lock word is in the "thin" state and its bits are formatted as follows: * @@ -75,16 +75,18 @@ class LockWord { // Remaining bits are the recursive lock count. kThinLockCountSize = 32 - kThinLockOwnerSize - kStateSize - kReadBarrierStateSize - kMarkBitStateSize, - // Thin lock bits. Owner in lowest bits. + // Thin lock bits. Owner in lowest bits. kThinLockOwnerShift = 0, kThinLockOwnerMask = (1 << kThinLockOwnerSize) - 1, + kThinLockOwnerMaskShifted = kThinLockOwnerMask << kThinLockOwnerShift, kThinLockMaxOwner = kThinLockOwnerMask, // Count in higher bits. kThinLockCountShift = kThinLockOwnerSize + kThinLockOwnerShift, kThinLockCountMask = (1 << kThinLockCountSize) - 1, kThinLockMaxCount = kThinLockCountMask, kThinLockCountOne = 1 << kThinLockCountShift, // == 65536 (0x10000) + kThinLockCountMaskShifted = kThinLockCountMask << kThinLockCountShift, // State in the highest bits. kStateShift = kReadBarrierStateSize + kThinLockCountSize + kThinLockCountShift + diff --git a/runtime/memory_region.cc b/runtime/memory_region.cc deleted file mode 100644 index 862ff736396d5f3250584ff1a41ab1182e544598..0000000000000000000000000000000000000000 --- a/runtime/memory_region.cc +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "memory_region.h" - -#include -#include - -namespace art { - -void MemoryRegion::CopyFrom(size_t offset, const MemoryRegion& from) const { - CHECK(from.pointer() != nullptr); - CHECK_GT(from.size(), 0U); - CHECK_GE(this->size(), from.size()); - CHECK_LE(offset, this->size() - from.size()); - memmove(reinterpret_cast(begin() + offset), from.pointer(), from.size()); -} - -void MemoryRegion::StoreBits(uintptr_t bit_offset, uint32_t value, size_t length) { - DCHECK_LE(value, MaxInt(length)); - DCHECK_LE(length, BitSizeOf()); - DCHECK_LE(bit_offset + length, size_in_bits()); - if (length == 0) { - return; - } - // Bits are stored in this order {7 6 5 4 3 2 1 0}. - // How many remaining bits in current byte is (bit_offset % kBitsPerByte) + 1. - uint8_t* out = ComputeInternalPointer(bit_offset >> kBitsPerByteLog2); - size_t orig_len = length; - uint32_t orig_value = value; - uintptr_t bit_remainder = bit_offset % kBitsPerByte; - while (true) { - const uintptr_t remaining_bits = kBitsPerByte - bit_remainder; - if (length <= remaining_bits) { - // Length is smaller than all of remainder bits. - size_t mask = ((1 << length) - 1) << bit_remainder; - *out = (*out & ~mask) | (value << bit_remainder); - break; - } - // Copy remaining bits in current byte. - size_t value_mask = (1 << remaining_bits) - 1; - *out = (*out & ~(value_mask << bit_remainder)) | ((value & value_mask) << bit_remainder); - value >>= remaining_bits; - bit_remainder = 0; - length -= remaining_bits; - ++out; - } - DCHECK_EQ(LoadBits(bit_offset, orig_len), orig_value) << bit_offset << " " << orig_len; -} - -} // namespace art diff --git a/runtime/method_handles-inl.h b/runtime/method_handles-inl.h index 41c8384e0d6a8248a797a41068e35f33abe1a772..c4ac982576b73533b34ba9a6fac4928f964e250d 100644 --- a/runtime/method_handles-inl.h +++ b/runtime/method_handles-inl.h @@ -22,7 +22,8 @@ #include "common_throws.h" #include "dex/dex_instruction.h" #include "interpreter/interpreter_common.h" -#include "jvalue.h" +#include "interpreter/shadow_frame-inl.h" +#include "jvalue-inl.h" #include "mirror/class.h" #include "mirror/method_type.h" #include "mirror/object.h" @@ -31,6 +32,79 @@ namespace art { +// A convenience class that allows for iteration through a list of +// input argument registers. This is used to iterate over input +// arguments while performing standard argument conversions. +class ShadowFrameGetter { + public: + ShadowFrameGetter(const ShadowFrame& shadow_frame, + const InstructionOperands* const operands, + size_t operand_index = 0u) + : shadow_frame_(shadow_frame), operands_(operands), operand_index_(operand_index) {} + + ALWAYS_INLINE uint32_t Get() REQUIRES_SHARED(Locks::mutator_lock_) { + return shadow_frame_.GetVReg(Next()); + } + + ALWAYS_INLINE int64_t GetLong() REQUIRES_SHARED(Locks::mutator_lock_) { + return shadow_frame_.GetVRegLong(NextLong()); + } + + ALWAYS_INLINE ObjPtr GetReference() REQUIRES_SHARED(Locks::mutator_lock_) { + return shadow_frame_.GetVRegReference(Next()); + } + + private: + uint32_t Next() { + const uint32_t next = operands_->GetOperand(operand_index_); + operand_index_ += 1; + return next; + } + + uint32_t NextLong() { + const uint32_t next = operands_->GetOperand(operand_index_); + operand_index_ += 2; + return next; + } + + const ShadowFrame& shadow_frame_; + const InstructionOperands* const operands_; // the set of register operands to read + size_t operand_index_; // the next register operand to read from frame +}; + +// A convenience class that allows values to be written to a given shadow frame, +// starting at location |first_dst_reg|. +class ShadowFrameSetter { + public: + ShadowFrameSetter(ShadowFrame* shadow_frame, size_t first_dst_reg) + : shadow_frame_(shadow_frame), arg_index_(first_dst_reg) {} + + ALWAYS_INLINE void Set(uint32_t value) REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_LT(arg_index_, shadow_frame_->NumberOfVRegs()); + shadow_frame_->SetVReg(arg_index_++, value); + } + + ALWAYS_INLINE void SetReference(ObjPtr value) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_LT(arg_index_, shadow_frame_->NumberOfVRegs()); + shadow_frame_->SetVRegReference(arg_index_++, value); + } + + ALWAYS_INLINE void SetLong(int64_t value) REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_LT(arg_index_, shadow_frame_->NumberOfVRegs()); + shadow_frame_->SetVRegLong(arg_index_, value); + arg_index_ += 2; + } + + ALWAYS_INLINE bool Done() const { + return arg_index_ == shadow_frame_->NumberOfVRegs(); + } + + private: + ShadowFrame* shadow_frame_; + size_t arg_index_; +}; + inline bool ConvertArgumentValue(Handle callsite_type, Handle callee_type, ObjPtr from_class, diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index 64ab78997f951937ab277f1a435b6f3a308c7c9c..01a32a2288fc4038ddfe5ca02163405c4f73b2fb 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -18,9 +18,10 @@ #include "android-base/stringprintf.h" +#include "class_root.h" #include "common_dex_operations.h" +#include "interpreter/shadow_frame-inl.h" #include "jvalue-inl.h" -#include "jvalue.h" #include "mirror/emulated_stack_frame.h" #include "mirror/method_handle_impl-inl.h" #include "mirror/method_type.h" @@ -267,7 +268,7 @@ bool ConvertJValueCommon( // Then perform the actual boxing, and then set the reference. ObjPtr boxed = BoxPrimitive(type, src_value); - value->SetL(boxed.Ptr()); + value->SetL(boxed); return true; } else { // The source type is a reference and the target type is a primitive, so we must unbox. @@ -322,7 +323,7 @@ inline void CopyArgumentsFromCallerFrame(const ShadowFrame& caller_frame, // Note: As an optimization, non-moving collectors leave a stale reference value // in the references array even after the original vreg was overwritten to a non-reference. if (src_value == reinterpret_cast(o.Ptr())) { - callee_frame->SetVRegReference(dst_reg, o.Ptr()); + callee_frame->SetVRegReference(dst_reg, o); } else { callee_frame->SetVReg(dst_reg, src_value); } @@ -1022,7 +1023,7 @@ bool DoVarHandleInvokeTranslation(Thread* self, // Check that the first parameter is a VarHandle if (callsite_ptypes->GetLength() < 1 || !mh_ptypes->Get(0)->IsAssignableFrom(callsite_ptypes->Get(0)) || - mh_ptypes->Get(0) != mirror::VarHandle::StaticClass()) { + mh_ptypes->Get(0) != GetClassRoot()) { ThrowWrongMethodTypeException(method_handle->GetMethodType(), callsite_type.Get()); return false; } @@ -1036,7 +1037,7 @@ bool DoVarHandleInvokeTranslation(Thread* self, // Cast to VarHandle instance Handle vh(hs.NewHandle(down_cast(receiver))); - DCHECK(mirror::VarHandle::StaticClass()->IsAssignableFrom(vh->GetClass())); + DCHECK(GetClassRoot()->IsAssignableFrom(vh->GetClass())); // Determine the accessor kind to dispatch ArtMethod* target_method = method_handle->GetTargetMethod(); diff --git a/runtime/method_handles.h b/runtime/method_handles.h index 7e60a5c170d9e1bb618b72284b828558f1f51d18..b6e31345e11403812fc68d8657dcd0e234668586 100644 --- a/runtime/method_handles.h +++ b/runtime/method_handles.h @@ -21,12 +21,13 @@ #include "dex/dex_instruction.h" #include "handle.h" -#include "interpreter/shadow_frame.h" #include "jvalue.h" #include "mirror/class.h" namespace art { +class ShadowFrame; + namespace mirror { class MethodHandle; class MethodType; @@ -126,72 +127,6 @@ bool PerformConversions(Thread* self, int32_t start_index, int32_t end_index) REQUIRES_SHARED(Locks::mutator_lock_); -// A convenience class that allows for iteration through a list of -// input argument registers. This is used to iterate over input -// arguments while performing standard argument conversions. -class ShadowFrameGetter { - public: - ShadowFrameGetter(const ShadowFrame& shadow_frame, - const InstructionOperands* const operands, - size_t operand_index = 0u) - : shadow_frame_(shadow_frame), operands_(operands), operand_index_(operand_index) {} - - ALWAYS_INLINE uint32_t Get() REQUIRES_SHARED(Locks::mutator_lock_) { - return shadow_frame_.GetVReg(Next()); - } - - ALWAYS_INLINE int64_t GetLong() REQUIRES_SHARED(Locks::mutator_lock_) { - return shadow_frame_.GetVRegLong(NextLong()); - } - - ALWAYS_INLINE ObjPtr GetReference() REQUIRES_SHARED(Locks::mutator_lock_) { - return shadow_frame_.GetVRegReference(Next()); - } - - private: - uint32_t Next() { - const uint32_t next = operands_->GetOperand(operand_index_); - operand_index_ += 1; - return next; - } - - uint32_t NextLong() { - const uint32_t next = operands_->GetOperand(operand_index_); - operand_index_ += 2; - return next; - } - - const ShadowFrame& shadow_frame_; - const InstructionOperands* const operands_; // the set of register operands to read - size_t operand_index_; // the next register operand to read from frame -}; - -// A convenience class that allows values to be written to a given shadow frame, -// starting at location |first_dst_reg|. -class ShadowFrameSetter { - public: - ShadowFrameSetter(ShadowFrame* shadow_frame, size_t first_dst_reg) - : shadow_frame_(shadow_frame), arg_index_(first_dst_reg) {} - - ALWAYS_INLINE void Set(uint32_t value) REQUIRES_SHARED(Locks::mutator_lock_) { - shadow_frame_->SetVReg(arg_index_++, value); - } - - ALWAYS_INLINE void SetReference(ObjPtr value) - REQUIRES_SHARED(Locks::mutator_lock_) { - shadow_frame_->SetVRegReference(arg_index_++, value.Ptr()); - } - - ALWAYS_INLINE void SetLong(int64_t value) REQUIRES_SHARED(Locks::mutator_lock_) { - shadow_frame_->SetVRegLong(arg_index_, value); - arg_index_ += 2; - } - - private: - ShadowFrame* shadow_frame_; - size_t arg_index_; -}; - bool MethodHandleInvoke(Thread* self, ShadowFrame& shadow_frame, Handle method_handle, diff --git a/runtime/method_handles_test.cc b/runtime/method_handles_test.cc index a9473421cb96891c5cda6316d97a268b0bf7857f..d123754e47005aa52496a7071767bc7cc649d9a5 100644 --- a/runtime/method_handles_test.cc +++ b/runtime/method_handles_test.cc @@ -17,6 +17,7 @@ #include "method_handles.h" #include "class_linker-inl.h" +#include "class_root.h" #include "common_runtime_test.h" #include "handle_scope-inl.h" #include "jvalue-inl.h" @@ -49,12 +50,11 @@ namespace { REQUIRES_SHARED(Locks::mutator_lock_) { ClassLinker* cl = Runtime::Current()->GetClassLinker(); StackHandleScope<2> hs(self); - ObjPtr class_type = mirror::Class::GetJavaLangClass(); - ObjPtr class_array_type = cl->FindArrayClass(self, &class_type); + ObjPtr class_array_type = GetClassRoot>(cl); auto parameter_types = hs.NewHandle( mirror::ObjectArray::Alloc(self, class_array_type, 1)); parameter_types->Set(0, parameter_type.Get()); - Handle void_class = hs.NewHandle(cl->FindPrimitiveClass('V')); + Handle void_class = hs.NewHandle(GetClassRoot(ClassRoot::kPrimitiveVoid, cl)); return mirror::MethodType::Create(self, void_class, parameter_types); } @@ -179,7 +179,7 @@ TEST_F(MethodHandlesTest, SupportedReferenceCast) { StackHandleScope<3> hs(soa.Self()); static const int32_t kInitialValue = 101; JValue value = JValue::FromPrimitive(kInitialValue); - Handle boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value).Ptr()); + Handle boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value)); Handle from = hs.NewHandle(boxed_value->GetClass()); Handle to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Number;")); value.SetL(boxed_value.Get()); @@ -195,8 +195,7 @@ TEST_F(MethodHandlesTest, UnsupportedReferenceCast) { ClassLinker* cl = Runtime::Current()->GetClassLinker(); StackHandleScope<3> hs(soa.Self()); JValue value = JValue::FromPrimitive(3.733e2); - Handle boxed_value = - hs.NewHandle(BoxPrimitive(Primitive::kPrimDouble, value).Ptr()); + Handle boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimDouble, value)); Handle from = hs.NewHandle(boxed_value->GetClass()); Handle to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;")); value.SetL(boxed_value.Get()); @@ -293,7 +292,7 @@ TEST_F(MethodHandlesTest, SupportedBoxedToPrimitiveConversion) { StackHandleScope<3> hs(soa.Self()); const int32_t kInitialValue = 101; JValue value = JValue::FromPrimitive(kInitialValue); - Handle boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value).Ptr()); + Handle boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value)); Handle from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;")); Handle to = hs.NewHandle(cl->FindPrimitiveClass('I')); value.SetL(boxed_value.Get()); @@ -308,7 +307,7 @@ TEST_F(MethodHandlesTest, SupportedBoxedToWiderPrimitiveConversion) { StackHandleScope<3> hs(soa.Self()); static const int32_t kInitialValue = 101; JValue value = JValue::FromPrimitive(kInitialValue); - Handle boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value).Ptr()); + Handle boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value)); Handle from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;")); Handle to = hs.NewHandle(cl->FindPrimitiveClass('J')); value.SetL(boxed_value.Get()); @@ -352,7 +351,7 @@ TEST_F(MethodHandlesTest, UnsupportedBoxedToNarrowerPrimitiveConversionNoCast) { StackHandleScope<3> hs(soa.Self()); static const int32_t kInitialValue = 101; JValue value = JValue::FromPrimitive(kInitialValue); - Handle boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value).Ptr()); + Handle boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value)); Handle from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;")); Handle to = hs.NewHandle(cl->FindPrimitiveClass('S')); value.SetL(boxed_value.Get()); @@ -368,8 +367,7 @@ TEST_F(MethodHandlesTest, UnsupportedBoxedToNarrowerPrimitiveConversionWithCast) StackHandleScope<3> hs(soa.Self()); static const double kInitialValue = 1e77; JValue value = JValue::FromPrimitive(kInitialValue); - Handle boxed_value = - hs.NewHandle(BoxPrimitive(Primitive::kPrimDouble, value).Ptr()); + Handle boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimDouble, value)); Handle from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Number;")); Handle to = hs.NewHandle(cl->FindPrimitiveClass('F')); value.SetL(boxed_value.Get()); diff --git a/runtime/method_info.h b/runtime/method_info.h index fe062564f14016287633985d93cf13acf17f0c1e..6f74678e427b231a1a1516e462c6e679753c7465 100644 --- a/runtime/method_info.h +++ b/runtime/method_info.h @@ -21,7 +21,7 @@ #include "base/leb128.h" #include "base/macros.h" -#include "memory_region.h" +#include "base/bit_memory_region.h" namespace art { @@ -35,8 +35,8 @@ class MethodInfo { explicit MethodInfo(const uint8_t* ptr) { if (ptr != nullptr) { num_method_indices_ = DecodeUnsignedLeb128(&ptr); - region_ = MemoryRegion(const_cast(ptr), - num_method_indices_ * sizeof(MethodIndexType)); + region_ = BitMemoryRegion( + MemoryRegion(const_cast(ptr), num_method_indices_ * sizeof(MethodIndexType))); } } @@ -44,7 +44,7 @@ class MethodInfo { MethodInfo(uint8_t* ptr, size_t num_method_indices) : num_method_indices_(num_method_indices) { DCHECK(ptr != nullptr); ptr = EncodeUnsignedLeb128(ptr, num_method_indices_); - region_ = MemoryRegion(ptr, num_method_indices_ * sizeof(MethodIndexType)); + region_ = BitMemoryRegion(MemoryRegion(ptr, num_method_indices_ * sizeof(MethodIndexType))); } static size_t ComputeSize(size_t num_method_indices) { @@ -71,7 +71,7 @@ class MethodInfo { private: size_t num_method_indices_ = 0u; - MemoryRegion region_; + BitMemoryRegion region_; }; } // namespace art diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index 636c84c7597e6cdb569531d137bba247de2c5165..2e395302aff935cdb89f03555df3a1adc0289931 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -151,11 +151,11 @@ class SetLengthToUsableSizeVisitor { }; template -inline Array* Array::Alloc(Thread* self, - ObjPtr array_class, - int32_t component_count, - size_t component_size_shift, - gc::AllocatorType allocator_type) { +inline ObjPtr Array::Alloc(Thread* self, + ObjPtr array_class, + int32_t component_count, + size_t component_size_shift, + gc::AllocatorType allocator_type) { DCHECK(allocator_type != gc::kAllocatorTypeLOS); DCHECK(array_class != nullptr); DCHECK(array_class->IsArrayClass()); @@ -175,19 +175,19 @@ inline Array* Array::Alloc(Thread* self, } #endif gc::Heap* heap = Runtime::Current()->GetHeap(); - Array* result; + ObjPtr result; if (!kFillUsable) { SetLengthVisitor visitor(component_count); - result = down_cast( + result = ObjPtr::DownCast(MakeObjPtr( heap->AllocObjectWithAllocator(self, array_class, size, - allocator_type, visitor)); + allocator_type, visitor))); } else { SetLengthToUsableSizeVisitor visitor(component_count, DataOffset(1U << component_size_shift).SizeValue(), component_size_shift); - result = down_cast( + result = ObjPtr::DownCast(MakeObjPtr( heap->AllocObjectWithAllocator(self, array_class, size, - allocator_type, visitor)); + allocator_type, visitor))); } if (kIsDebugBuild && result != nullptr && Runtime::Current()->IsStarted()) { array_class = result->GetClass(); // In case the array class moved. @@ -201,15 +201,10 @@ inline Array* Array::Alloc(Thread* self, return result; } -template -inline void PrimitiveArray::VisitRoots(RootVisitor* visitor) { - array_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - template -inline PrimitiveArray* PrimitiveArray::AllocateAndFill(Thread* self, - const T* data, - size_t length) { +inline ObjPtr> PrimitiveArray::AllocateAndFill(Thread* self, + const T* data, + size_t length) { StackHandleScope<1> hs(self); Handle> arr(hs.NewHandle(PrimitiveArray::Alloc(self, length))); if (!arr.IsNull()) { @@ -219,16 +214,6 @@ inline PrimitiveArray* PrimitiveArray::AllocateAndFill(Thread* self, return arr.Get(); } -template -inline PrimitiveArray* PrimitiveArray::Alloc(Thread* self, size_t length) { - Array* raw_array = Array::Alloc(self, - GetArrayClass(), - length, - ComponentSizeShiftWidth(sizeof(T)), - Runtime::Current()->GetHeap()->GetCurrentAllocator()); - return down_cast*>(raw_array); -} - template inline T PrimitiveArray::Get(int32_t i) { if (!CheckIsValidIndex(i)) { @@ -461,13 +446,6 @@ void PointerArray::Memcpy(int32_t dst_pos, } } -template -inline void PrimitiveArray::SetArrayClass(ObjPtr array_class) { - CHECK(array_class_.IsNull()); - CHECK(array_class != nullptr); - array_class_ = GcRoot(array_class); -} - } // namespace mirror } // namespace art diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc index ea202e766f14748e177eecfe7595562583219622..66ec368935b453bb2a5eba9dffc34413b1d3ecbb 100644 --- a/runtime/mirror/array.cc +++ b/runtime/mirror/array.cc @@ -20,6 +20,7 @@ #include "class-inl.h" #include "class.h" #include "class_linker-inl.h" +#include "class_root.h" #include "common_throws.h" #include "dex/dex_file-inl.h" #include "gc/accounting/card_table-inl.h" @@ -41,17 +42,18 @@ using android::base::StringPrintf; // piece and work our way in. // Recursively create an array with multiple dimensions. Elements may be // Objects or primitive types. -static Array* RecursiveCreateMultiArray(Thread* self, - Handle array_class, int current_dimension, - Handle dimensions) +static ObjPtr RecursiveCreateMultiArray(Thread* self, + Handle array_class, + int current_dimension, + Handle dimensions) REQUIRES_SHARED(Locks::mutator_lock_) { int32_t array_length = dimensions->Get(current_dimension); - StackHandleScope<1> hs(self); - Handle new_array( - hs.NewHandle( - Array::Alloc(self, array_class.Get(), array_length, - array_class->GetComponentSizeShift(), - Runtime::Current()->GetHeap()->GetCurrentAllocator()))); + StackHandleScope<2> hs(self); + Handle h_component_type(hs.NewHandle(array_class->GetComponentType())); + size_t component_size_shift = h_component_type->GetPrimitiveTypeSizeShift(); + gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); + Handle new_array(hs.NewHandle(Array::Alloc( + self, array_class.Get(), array_length, component_size_shift, allocator_type))); if (UNLIKELY(new_array == nullptr)) { CHECK(self->IsExceptionPending()); return nullptr; @@ -59,10 +61,8 @@ static Array* RecursiveCreateMultiArray(Thread* self, if (current_dimension + 1 < dimensions->GetLength()) { // Create a new sub-array in every element of the array. for (int32_t i = 0; i < array_length; i++) { - StackHandleScope<1> hs2(self); - Handle h_component_type(hs2.NewHandle(array_class->GetComponentType())); - ObjPtr sub_array = RecursiveCreateMultiArray(self, h_component_type, - current_dimension + 1, dimensions); + ObjPtr sub_array = + RecursiveCreateMultiArray(self, h_component_type, current_dimension + 1, dimensions); if (UNLIKELY(sub_array == nullptr)) { CHECK(self->IsExceptionPending()); return nullptr; @@ -74,8 +74,9 @@ static Array* RecursiveCreateMultiArray(Thread* self, return new_array.Get(); } -Array* Array::CreateMultiArray(Thread* self, Handle element_class, - Handle dimensions) { +ObjPtr Array::CreateMultiArray(Thread* self, + Handle element_class, + Handle dimensions) { // Verify dimensions. // // The caller is responsible for verifying that "dimArray" is non-null @@ -94,17 +95,15 @@ Array* Array::CreateMultiArray(Thread* self, Handle element_class, // Find/generate the array class. ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - ObjPtr element_class_ptr = element_class.Get(); StackHandleScope<1> hs(self); MutableHandle array_class( - hs.NewHandle(class_linker->FindArrayClass(self, &element_class_ptr))); + hs.NewHandle(class_linker->FindArrayClass(self, element_class.Get()))); if (UNLIKELY(array_class == nullptr)) { CHECK(self->IsExceptionPending()); return nullptr; } for (int32_t i = 1; i < dimensions->GetLength(); ++i) { - ObjPtr array_class_ptr = array_class.Get(); - array_class.Assign(class_linker->FindArrayClass(self, &array_class_ptr)); + array_class.Assign(class_linker->FindArrayClass(self, array_class.Get())); if (UNLIKELY(array_class == nullptr)) { CHECK(self->IsExceptionPending()); return nullptr; @@ -118,6 +117,17 @@ Array* Array::CreateMultiArray(Thread* self, Handle element_class, return new_array.Ptr(); } +template +ObjPtr> PrimitiveArray::Alloc(Thread* self, size_t length) { + gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); + ObjPtr raw_array = Array::Alloc(self, + GetClassRoot>(), + length, + ComponentSizeShiftWidth(sizeof(T)), + allocator_type); + return ObjPtr>::DownCast(raw_array); +} + void Array::ThrowArrayIndexOutOfBoundsException(int32_t index) { art::ThrowArrayIndexOutOfBoundsException(index, GetLength()); } @@ -126,7 +136,7 @@ void Array::ThrowArrayStoreException(ObjPtr object) { art::ThrowArrayStoreException(object->GetClass(), this->GetClass()); } -Array* Array::CopyOf(Thread* self, int32_t new_length) { +ObjPtr Array::CopyOf(Thread* self, int32_t new_length) { CHECK(GetClass()->GetComponentType()->IsPrimitive()) << "Will miss write barriers"; DCHECK_GE(new_length, 0); // We may get copied by a compacting GC. @@ -137,7 +147,8 @@ Array* Array::CopyOf(Thread* self, int32_t new_length) { heap->GetCurrentNonMovingAllocator(); const auto component_size = GetClass()->GetComponentSize(); const auto component_shift = GetClass()->GetComponentSizeShift(); - ObjPtr new_array = Alloc(self, GetClass(), new_length, component_shift, allocator_type); + ObjPtr new_array = + Alloc(self, GetClass(), new_length, component_shift, allocator_type); if (LIKELY(new_array != nullptr)) { memcpy(new_array->GetRawData(component_size, 0), h_this->GetRawData(component_size, 0), @@ -146,9 +157,6 @@ Array* Array::CopyOf(Thread* self, int32_t new_length) { return new_array.Ptr(); } - -template GcRoot PrimitiveArray::array_class_; - // Explicitly instantiate all the primitive array types. template class PrimitiveArray; // BooleanArray template class PrimitiveArray; // ByteArray diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h index 11128bb5a2ba71fd09befffb0c9872f6e4cb6951..8bdd561ec4938501b94aa1e705df00964acc815f 100644 --- a/runtime/mirror/array.h +++ b/runtime/mirror/array.h @@ -19,7 +19,6 @@ #include "base/enums.h" #include "gc/allocator_type.h" -#include "gc_root.h" #include "obj_ptr.h" #include "object.h" @@ -38,17 +37,17 @@ class MANAGED Array : public Object { // least component_count size, however, if there's usable space at the end of the allocation the // array will fill it. template - ALWAYS_INLINE static Array* Alloc(Thread* self, - ObjPtr array_class, - int32_t component_count, - size_t component_size_shift, - gc::AllocatorType allocator_type) + ALWAYS_INLINE static ObjPtr Alloc(Thread* self, + ObjPtr array_class, + int32_t component_count, + size_t component_size_shift, + gc::AllocatorType allocator_type) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - static Array* CreateMultiArray(Thread* self, - Handle element_class, - Handle dimensions) + static ObjPtr CreateMultiArray(Thread* self, + Handle element_class, + Handle dimensions) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); @@ -91,7 +90,7 @@ class MANAGED Array : public Object { template ALWAYS_INLINE bool CheckIsValidIndex(int32_t index) REQUIRES_SHARED(Locks::mutator_lock_); - Array* CopyOf(Thread* self, int32_t new_length) REQUIRES_SHARED(Locks::mutator_lock_) + ObjPtr CopyOf(Thread* self, int32_t new_length) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); protected: @@ -115,10 +114,10 @@ class MANAGED PrimitiveArray : public Array { public: typedef T ElementType; - static PrimitiveArray* Alloc(Thread* self, size_t length) + static ObjPtr> Alloc(Thread* self, size_t length) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - static PrimitiveArray* AllocateAndFill(Thread* self, const T* data, size_t length) + static ObjPtr> AllocateAndFill(Thread* self, const T* data, size_t length) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); @@ -167,24 +166,7 @@ class MANAGED PrimitiveArray : public Array { void Memcpy(int32_t dst_pos, ObjPtr> src, int32_t src_pos, int32_t count) REQUIRES_SHARED(Locks::mutator_lock_); - static void SetArrayClass(ObjPtr array_class); - - template - static Class* GetArrayClass() REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(!array_class_.IsNull()); - return array_class_.Read(); - } - - static void ResetArrayClass() { - CHECK(!array_class_.IsNull()); - array_class_ = GcRoot(nullptr); - } - - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: - static GcRoot array_class_; - DISALLOW_IMPLICIT_CONSTRUCTORS(PrimitiveArray); }; diff --git a/runtime/mirror/call_site.cc b/runtime/mirror/call_site.cc index eb613df4c60df520c819bf95db96801a694c8968..738106c0e4280b68e9cf10055a0ecd9c97d1db3e 100644 --- a/runtime/mirror/call_site.cc +++ b/runtime/mirror/call_site.cc @@ -17,35 +17,18 @@ #include "call_site.h" #include "class-inl.h" -#include "gc_root-inl.h" +#include "class_root.h" +#include "obj_ptr-inl.h" namespace art { namespace mirror { -GcRoot CallSite::static_class_; - mirror::CallSite* CallSite::Create(Thread* const self, Handle target) { - StackHandleScope<1> hs(self); - Handle cs( - hs.NewHandle(ObjPtr::DownCast(StaticClass()->AllocObject(self)))); + ObjPtr cs = + ObjPtr::DownCast(GetClassRoot()->AllocObject(self)); CHECK(!Runtime::Current()->IsActiveTransaction()); cs->SetFieldObject(TargetOffset(), target.Get()); - return cs.Get(); -} - -void CallSite::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void CallSite::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void CallSite::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); + return cs.Ptr(); } } // namespace mirror diff --git a/runtime/mirror/call_site.h b/runtime/mirror/call_site.h index 93f274808c1ff90c29d4e3d4d480c16c9fd60aec..9b6afca3aa6a8ccaaad0778d81fe9f9c709fd2dc 100644 --- a/runtime/mirror/call_site.h +++ b/runtime/mirror/call_site.h @@ -33,18 +33,10 @@ class MANAGED CallSite : public Object { Handle method_handle) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); - } - MethodHandle* GetTarget() REQUIRES_SHARED(Locks::mutator_lock_) { return GetFieldObject(TargetOffset()); } - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: static inline MemberOffset TargetOffset() { return MemberOffset(OFFSETOF_MEMBER(CallSite, target_)); @@ -52,8 +44,6 @@ class MANAGED CallSite : public Object { HeapReference target_; - static GcRoot static_class_; // java.lang.invoke.CallSite.class - friend struct art::CallSiteOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(CallSite); }; diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 72b31790f008afd39cfb6236751b9b650d2e9e36..fffd7f306208bd16792ca09708b1830d5b2bedbc 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -36,7 +36,6 @@ #include "object-inl.h" #include "object_array.h" #include "read_barrier-inl.h" -#include "reference-inl.h" #include "runtime.h" #include "string.h" @@ -146,6 +145,7 @@ inline ArraySlice Class::GetDeclaredMethodsSliceUnchecked(PointerSize GetDirectMethodsStartOffset(), GetCopiedMethodsStartOffset()); } + template inline ArraySlice Class::GetDeclaredVirtualMethodsSlice(PointerSize pointer_size) { DCHECK(IsLoaded() || IsErroneous()); @@ -282,8 +282,7 @@ inline ArtMethod* Class::GetVirtualMethodUnchecked(size_t i, PointerSize pointer return &GetVirtualMethodsSliceUnchecked(pointer_size)[i]; } -template +template inline PointerArray* Class::GetVTable() { DCHECK(IsLoaded() || IsErroneous()); return GetFieldObject( @@ -295,7 +294,7 @@ inline PointerArray* Class::GetVTableDuringLinking() { return GetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, vtable_)); } -inline void Class::SetVTable(PointerArray* new_vtable) { +inline void Class::SetVTable(ObjPtr new_vtable) { SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, vtable_), new_vtable); } @@ -303,8 +302,7 @@ inline bool Class::HasVTable() { return GetVTable() != nullptr || ShouldHaveEmbeddedVTable(); } - template +template inline int32_t Class::GetVTableLength() { if (ShouldHaveEmbeddedVTable()) { return GetEmbeddedVTableLength(); @@ -313,15 +311,15 @@ inline int32_t Class::GetVTableLength() { GetVTable()->GetLength() : 0; } - template +template inline ArtMethod* Class::GetVTableEntry(uint32_t i, PointerSize pointer_size) { if (ShouldHaveEmbeddedVTable()) { return GetEmbeddedVTableEntry(i, pointer_size); } auto* vtable = GetVTable(); DCHECK(vtable != nullptr); - return vtable->template GetElementPtrSize(i, pointer_size); + return vtable->template GetElementPtrSize( + i, pointer_size); } inline int32_t Class::GetEmbeddedVTableLength() { @@ -411,7 +409,7 @@ inline void Class::SetObjectSize(uint32_t new_object_size) { // Object[] = int[] --> false // inline bool Class::IsArrayAssignableFromArray(ObjPtr src) { - DCHECK(IsArrayClass()) << PrettyClass(); + DCHECK(IsArrayClass()) << PrettyClass(); DCHECK(src->IsArrayClass()) << src->PrettyClass(); return GetComponentType()->IsAssignableFrom(src->GetComponentType()); } @@ -488,7 +486,7 @@ inline bool Class::ResolvedMethodAccessTest(ObjPtr access_to, if (UNLIKELY(!this->CanAccess(dex_access_to))) { if (throw_on_failure) { ThrowIllegalAccessErrorClassForMethodDispatch(this, - dex_access_to.Ptr(), + dex_access_to, method, throw_invoke_type); } @@ -623,16 +621,14 @@ inline ArtMethod* Class::FindVirtualMethodForVirtualOrInterface(ArtMethod* metho return FindVirtualMethodForVirtual(method, pointer_size); } -template +template inline IfTable* Class::GetIfTable() { ObjPtr ret = GetFieldObject(IfTableOffset()); DCHECK(ret != nullptr) << PrettyClass(this); return ret.Ptr(); } -template +template inline int32_t Class::GetIfTableCount() { return GetIfTable()->Count(); } @@ -735,7 +731,7 @@ inline String* Class::GetName() { } inline void Class::SetName(ObjPtr name) { - SetFieldObjectTransaction(OFFSET_OF_OBJECT_MEMBER(Class, name_), name); + SetFieldObjectTransaction(OFFSET_OF_OBJECT_MEMBER(Class, name_), name); } template @@ -801,7 +797,7 @@ inline ObjPtr Class::Alloc(Thread* self, gc::AllocatorType allocator_typ obj = nullptr; } } - return obj.Ptr(); + return obj; } inline ObjPtr Class::AllocObject(Thread* self) { @@ -857,11 +853,6 @@ inline uint32_t Class::ComputeClassSize(bool has_embedded_vtable, return size; } -template -inline bool Class::IsReferenceClass() const { - return this == Reference::GetJavaLangRefReference(); -} - template inline bool Class::IsClassClass() { ObjPtr java_lang_Class = GetClass()-> @@ -893,8 +884,8 @@ inline bool Class::DescriptorEquals(const char* match) { inline void Class::AssertInitializedOrInitializingInThread(Thread* self) { if (kIsDebugBuild && !IsInitialized()) { CHECK(IsInitializing()) << PrettyClass() << " is not initializing: " << GetStatus(); - CHECK_EQ(GetClinitThreadId(), self->GetTid()) << PrettyClass() - << " is initializing in a different thread"; + CHECK_EQ(GetClinitThreadId(), self->GetTid()) + << PrettyClass() << " is initializing in a different thread"; } } @@ -916,30 +907,6 @@ inline ObjectArray>* Class::GetProxyThrows() { return GetFieldObject>>(field_offset); } -inline MemberOffset Class::GetDisableIntrinsicFlagOffset() { - CHECK(IsReferenceClass()); - // First static field - auto* field = GetStaticField(0); - DCHECK_STREQ(field->GetName(), "disableIntrinsic"); - return field->GetOffset(); -} - -inline MemberOffset Class::GetSlowPathFlagOffset() { - CHECK(IsReferenceClass()); - // Second static field - auto* field = GetStaticField(1); - DCHECK_STREQ(field->GetName(), "slowPathEnabled"); - return field->GetOffset(); -} - -inline bool Class::GetSlowPathEnabled() { - return GetFieldBoolean(GetSlowPathFlagOffset()); -} - -inline void Class::SetSlowPath(bool enabled) { - SetFieldBoolean(GetSlowPathFlagOffset(), enabled); -} - inline void Class::InitializeClassVisitor::operator()(ObjPtr obj, size_t usable_size) const { DCHECK_LE(class_size_, usable_size); @@ -994,18 +961,15 @@ inline ArraySlice Class::GetDirectMethods(PointerSize pointer_size) { return GetDirectMethodsSliceUnchecked(pointer_size); } -inline ArraySlice Class::GetDeclaredMethods( - PointerSize pointer_size) { +inline ArraySlice Class::GetDeclaredMethods(PointerSize pointer_size) { return GetDeclaredMethodsSliceUnchecked(pointer_size); } -inline ArraySlice Class::GetDeclaredVirtualMethods( - PointerSize pointer_size) { +inline ArraySlice Class::GetDeclaredVirtualMethods(PointerSize pointer_size) { return GetDeclaredVirtualMethodsSliceUnchecked(pointer_size); } -inline ArraySlice Class::GetVirtualMethods( - PointerSize pointer_size) { +inline ArraySlice Class::GetVirtualMethods(PointerSize pointer_size) { CheckPointerSize(pointer_size); return GetVirtualMethodsSliceUnchecked(pointer_size); } diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 3f4e841f866a5d556e54963f0eaa0da3ce5f66f2..26dba024c640a6f9f94c0fd58844aa0114d90930 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -26,6 +26,7 @@ #include "class_ext.h" #include "class_linker-inl.h" #include "class_loader.h" +#include "class_root.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_annotations.h" @@ -38,6 +39,7 @@ #include "object-refvisitor-inl.h" #include "object_array-inl.h" #include "object_lock.h" +#include "string-inl.h" #include "runtime.h" #include "thread.h" #include "throwable.h" @@ -53,48 +55,28 @@ namespace mirror { using android::base::StringPrintf; -GcRoot Class::java_lang_Class_; - -void Class::SetClassClass(ObjPtr java_lang_Class) { - CHECK(java_lang_Class_.IsNull()) - << java_lang_Class_.Read() - << " " << java_lang_Class; - CHECK(java_lang_Class != nullptr); - java_lang_Class->SetClassFlags(kClassFlagClass); - java_lang_Class_ = GcRoot(java_lang_Class); -} - -void Class::ResetClass() { - CHECK(!java_lang_Class_.IsNull()); - java_lang_Class_ = GcRoot(nullptr); -} - -void Class::VisitRoots(RootVisitor* visitor) { - java_lang_Class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - ObjPtr Class::GetPrimitiveClass(ObjPtr name) { const char* expected_name = nullptr; - ClassLinker::ClassRoot class_root = ClassLinker::kJavaLangObject; // Invalid. + ClassRoot class_root = ClassRoot::kJavaLangObject; // Invalid. if (name != nullptr && name->GetLength() >= 2) { // Perfect hash for the expected values: from the second letters of the primitive types, // only 'y' has the bit 0x10 set, so use it to change 'b' to 'B'. char hash = name->CharAt(0) ^ ((name->CharAt(1) & 0x10) << 1); switch (hash) { - case 'b': expected_name = "boolean"; class_root = ClassLinker::kPrimitiveBoolean; break; - case 'B': expected_name = "byte"; class_root = ClassLinker::kPrimitiveByte; break; - case 'c': expected_name = "char"; class_root = ClassLinker::kPrimitiveChar; break; - case 'd': expected_name = "double"; class_root = ClassLinker::kPrimitiveDouble; break; - case 'f': expected_name = "float"; class_root = ClassLinker::kPrimitiveFloat; break; - case 'i': expected_name = "int"; class_root = ClassLinker::kPrimitiveInt; break; - case 'l': expected_name = "long"; class_root = ClassLinker::kPrimitiveLong; break; - case 's': expected_name = "short"; class_root = ClassLinker::kPrimitiveShort; break; - case 'v': expected_name = "void"; class_root = ClassLinker::kPrimitiveVoid; break; + case 'b': expected_name = "boolean"; class_root = ClassRoot::kPrimitiveBoolean; break; + case 'B': expected_name = "byte"; class_root = ClassRoot::kPrimitiveByte; break; + case 'c': expected_name = "char"; class_root = ClassRoot::kPrimitiveChar; break; + case 'd': expected_name = "double"; class_root = ClassRoot::kPrimitiveDouble; break; + case 'f': expected_name = "float"; class_root = ClassRoot::kPrimitiveFloat; break; + case 'i': expected_name = "int"; class_root = ClassRoot::kPrimitiveInt; break; + case 'l': expected_name = "long"; class_root = ClassRoot::kPrimitiveLong; break; + case 's': expected_name = "short"; class_root = ClassRoot::kPrimitiveShort; break; + case 'v': expected_name = "void"; class_root = ClassRoot::kPrimitiveVoid; break; default: break; } } if (expected_name != nullptr && name->Equals(expected_name)) { - ObjPtr klass = Runtime::Current()->GetClassLinker()->GetClassRoot(class_root); + ObjPtr klass = GetClassRoot(class_root); DCHECK(klass != nullptr); return klass; } else { @@ -132,13 +114,17 @@ ClassExt* Class::EnsureExtDataPresent(Thread* self) { bool set; // Set the ext_data_ field using CAS semantics. if (Runtime::Current()->IsActiveTransaction()) { - set = h_this->CasFieldStrongSequentiallyConsistentObject(ext_offset, - ObjPtr(nullptr), - new_ext.Get()); + set = h_this->CasFieldObject(ext_offset, + nullptr, + new_ext.Get(), + CASMode::kStrong, + std::memory_order_seq_cst); } else { - set = h_this->CasFieldStrongSequentiallyConsistentObject(ext_offset, - ObjPtr(nullptr), - new_ext.Get()); + set = h_this->CasFieldObject(ext_offset, + nullptr, + new_ext.Get(), + CASMode::kStrong, + std::memory_order_seq_cst); } ObjPtr ret(set ? new_ext.Get() : h_this->GetExtData()); DCHECK(!set || h_this->GetExtData() == new_ext.Get()); @@ -437,7 +423,7 @@ bool Class::IsInSamePackage(ObjPtr that) { } bool Class::IsThrowableClass() { - return WellKnownClasses::ToClass(WellKnownClasses::java_lang_Throwable)->IsAssignableFrom(this); + return GetClassRoot()->IsAssignableFrom(this); } void Class::SetClassLoader(ObjPtr new_class_loader) { @@ -1209,13 +1195,15 @@ Class* Class::CopyOf(Thread* self, int32_t new_length, ImTable* imt, PointerSize // We may get copied by a compacting GC. StackHandleScope<1> hs(self); Handle h_this(hs.NewHandle(this)); - gc::Heap* heap = Runtime::Current()->GetHeap(); + Runtime* runtime = Runtime::Current(); + gc::Heap* heap = runtime->GetHeap(); // The num_bytes (3rd param) is sizeof(Class) as opposed to SizeOf() // to skip copying the tail part that we will overwrite here. CopyClassVisitor visitor(self, &h_this, new_length, sizeof(Class), imt, pointer_size); + ObjPtr java_lang_Class = GetClassRoot(runtime->GetClassLinker()); ObjPtr new_class = kMovingClasses ? - heap->AllocObject(self, java_lang_Class_.Read(), new_length, visitor) : - heap->AllocNonMovableObject(self, java_lang_Class_.Read(), new_length, visitor); + heap->AllocObject(self, java_lang_Class, new_length, visitor) : + heap->AllocNonMovableObject(self, java_lang_Class, new_length, visitor); if (UNLIKELY(new_class == nullptr)) { self->AssertPendingOOMException(); return nullptr; @@ -1249,7 +1237,7 @@ ArtMethod* Class::GetDeclaredConstructor( uint32_t Class::Depth() { uint32_t depth = 0; - for (ObjPtr klass = this; klass->GetSuperClass() != nullptr; klass = klass->GetSuperClass()) { + for (ObjPtr cls = this; cls->GetSuperClass() != nullptr; cls = cls->GetSuperClass()) { depth++; } return depth; @@ -1285,7 +1273,7 @@ ObjPtr Class::GetDeclaredMethodInternal( for (auto& m : h_klass->GetDeclaredVirtualMethods(kPointerSize)) { auto* np_method = m.GetInterfaceMethodIfProxy(kPointerSize); // May cause thread suspension. - ObjPtr np_name = np_method->GetNameAsString(self); + ObjPtr np_name = np_method->ResolveNameString(); if (!np_name->Equals(h_method_name.Get()) || !np_method->EqualParameters(h_args)) { if (UNLIKELY(self->IsExceptionPending())) { return nullptr; @@ -1307,7 +1295,7 @@ ObjPtr Class::GetDeclaredMethodInternal( } auto* np_method = m.GetInterfaceMethodIfProxy(kPointerSize); // May cause thread suspension. - ObjPtr np_name = np_method->GetNameAsString(self); + ObjPtr np_name = np_method->ResolveNameString(); if (np_name == nullptr) { self->AssertPendingException(); return nullptr; @@ -1459,12 +1447,12 @@ template void Class::GetAccessFlagsDCheck() { // circularity issue during loading the names of its members DCHECK(IsIdxLoaded() || IsRetired() || IsErroneous(kVerifyFlags & ~kVerifyThis)>() || - this == String::GetJavaLangString()) + this == GetClassRoot()) << "IsIdxLoaded=" << IsIdxLoaded() << " IsRetired=" << IsRetired() << " IsErroneous=" << IsErroneous(kVerifyFlags & ~kVerifyThis)>() - << " IsString=" << (this == String::GetJavaLangString()) + << " IsString=" << (this == GetClassRoot()) << " status= " << GetStatus() << " descriptor=" << PrettyDescriptor(); } diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 98e25eb320a1b5b932c0d3fd0580da7442406a7b..c3e167c3065432117d9f0a0e18582f86e46c6c63 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -30,7 +30,6 @@ #include "dex/modifiers.h" #include "dex/primitive.h" #include "gc/allocator_type.h" -#include "gc_root.h" #include "imtable.h" #include "object.h" #include "object_array.h" @@ -437,9 +436,6 @@ class MANAGED Class FINAL : public Object { bool IsThrowableClass() REQUIRES_SHARED(Locks::mutator_lock_); - template - bool IsReferenceClass() const REQUIRES_SHARED(Locks::mutator_lock_); - static MemberOffset ComponentTypeOffset() { return OFFSET_OF_OBJECT_MEMBER(Class, component_type_); } @@ -785,7 +781,7 @@ class MANAGED Class FINAL : public Object { ALWAYS_INLINE PointerArray* GetVTableDuringLinking() REQUIRES_SHARED(Locks::mutator_lock_); - void SetVTable(PointerArray* new_vtable) REQUIRES_SHARED(Locks::mutator_lock_); + void SetVTable(ObjPtr new_vtable) REQUIRES_SHARED(Locks::mutator_lock_); static MemberOffset VTableOffset() { return OFFSET_OF_OBJECT_MEMBER(Class, vtable_); @@ -936,12 +932,10 @@ class MANAGED Class FINAL : public Object { ArtMethod* FindConstructor(const StringPiece& signature, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); - ArtMethod* FindDeclaredVirtualMethodByName(const StringPiece& name, - PointerSize pointer_size) + ArtMethod* FindDeclaredVirtualMethodByName(const StringPiece& name, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); - ArtMethod* FindDeclaredDirectMethodByName(const StringPiece& name, - PointerSize pointer_size) + ArtMethod* FindDeclaredDirectMethodByName(const StringPiece& name, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); ArtMethod* FindClassInitializer(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); @@ -1133,21 +1127,6 @@ class MANAGED Class FINAL : public Object { dex::TypeIndex FindTypeIndexInOtherDexFile(const DexFile& dex_file) REQUIRES_SHARED(Locks::mutator_lock_); - static Class* GetJavaLangClass() REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(HasJavaLangClass()); - return java_lang_Class_.Read(); - } - - static bool HasJavaLangClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return !java_lang_Class_.IsNull(); - } - - // Can't call this SetClass or else gets called instead of Object::SetClass in places. - static void SetClassClass(ObjPtr java_lang_Class) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass(); - static void VisitRoots(RootVisitor* visitor) - REQUIRES_SHARED(Locks::mutator_lock_); - // Visit native roots visits roots which are keyed off the native pointers such as ArtFields and // ArtMethods. template @@ -1200,10 +1179,7 @@ class MANAGED Class FINAL : public Object { void AssertInitializedOrInitializingInThread(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); - Class* CopyOf(Thread* self, - int32_t new_length, - ImTable* imt, - PointerSize pointer_size) + Class* CopyOf(Thread* self, int32_t new_length, ImTable* imt, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); // For proxy class only. @@ -1212,12 +1188,6 @@ class MANAGED Class FINAL : public Object { // For proxy class only. ObjectArray>* GetProxyThrows() REQUIRES_SHARED(Locks::mutator_lock_); - // For reference class only. - MemberOffset GetDisableIntrinsicFlagOffset() REQUIRES_SHARED(Locks::mutator_lock_); - MemberOffset GetSlowPathFlagOffset() REQUIRES_SHARED(Locks::mutator_lock_); - bool GetSlowPathEnabled() REQUIRES_SHARED(Locks::mutator_lock_); - void SetSlowPath(bool enabled) REQUIRES_SHARED(Locks::mutator_lock_); - // May cause thread suspension due to EqualParameters. ArtMethod* GetDeclaredConstructor(Thread* self, Handle> args, @@ -1513,9 +1483,6 @@ class MANAGED Class FINAL : public Object { // Static fields, variable size. // uint32_t fields_[0]; - // java.lang.Class - static GcRoot java_lang_Class_; - ART_FRIEND_TEST(DexCacheTest, TestResolvedFieldAccess); // For ResolvedFieldAccessTest friend struct art::ClassOffsets; // for verifying offset information friend class Object; // For VisitReferences diff --git a/runtime/mirror/class_ext.cc b/runtime/mirror/class_ext.cc index 081957964c50e9799937557b65f9ae64d212652b..44bf9891cd683d53067987d503707cf283d7ef7c 100644 --- a/runtime/mirror/class_ext.cc +++ b/runtime/mirror/class_ext.cc @@ -21,6 +21,7 @@ #include "base/enums.h" #include "base/utils.h" #include "class-inl.h" +#include "class_root.h" #include "dex/dex_file-inl.h" #include "gc/accounting/card_table-inl.h" #include "object-inl.h" @@ -31,8 +32,6 @@ namespace art { namespace mirror { -GcRoot ClassExt::dalvik_system_ClassExt_; - uint32_t ClassExt::ClassSize(PointerSize pointer_size) { uint32_t vtable_entries = Object::kVTableLength; return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0, pointer_size); @@ -44,8 +43,8 @@ void ClassExt::SetObsoleteArrays(ObjPtr methods, auto obsolete_dex_cache_off = OFFSET_OF_OBJECT_MEMBER(ClassExt, obsolete_dex_caches_); auto obsolete_methods_off = OFFSET_OF_OBJECT_MEMBER(ClassExt, obsolete_methods_); DCHECK(!Runtime::Current()->IsActiveTransaction()); - SetFieldObject(obsolete_dex_cache_off, dex_caches.Ptr()); - SetFieldObject(obsolete_methods_off, methods.Ptr()); + SetFieldObject(obsolete_dex_cache_off, dex_caches); + SetFieldObject(obsolete_methods_off, methods); } // We really need to be careful how we update this. If we ever in the future make it so that @@ -102,8 +101,7 @@ bool ClassExt::ExtendObsoleteArrays(Thread* self, uint32_t increase) { } ClassExt* ClassExt::Alloc(Thread* self) { - DCHECK(dalvik_system_ClassExt_.Read() != nullptr); - return down_cast(dalvik_system_ClassExt_.Read()->AllocObject(self).Ptr()); + return down_cast(GetClassRoot()->AllocObject(self).Ptr()); } void ClassExt::SetVerifyError(ObjPtr err) { @@ -119,19 +117,5 @@ void ClassExt::SetOriginalDexFile(ObjPtr bytes) { SetFieldObject(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_), bytes); } -void ClassExt::SetClass(ObjPtr dalvik_system_ClassExt) { - CHECK(dalvik_system_ClassExt != nullptr); - dalvik_system_ClassExt_ = GcRoot(dalvik_system_ClassExt); -} - -void ClassExt::ResetClass() { - CHECK(!dalvik_system_ClassExt_.IsNull()); - dalvik_system_ClassExt_ = GcRoot(nullptr); -} - -void ClassExt::VisitRoots(RootVisitor* visitor) { - dalvik_system_ClassExt_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - } // namespace mirror } // namespace art diff --git a/runtime/mirror/class_ext.h b/runtime/mirror/class_ext.h index 75a3800989f480f330ddb0105d75a290dc3af72e..612fd0f2560cf95dab02b55e10c97646144b50b1 100644 --- a/runtime/mirror/class_ext.h +++ b/runtime/mirror/class_ext.h @@ -20,7 +20,6 @@ #include "array.h" #include "class.h" #include "dex_cache.h" -#include "gc_root.h" #include "object.h" #include "object_array.h" #include "string.h" @@ -72,10 +71,6 @@ class MANAGED ClassExt : public Object { bool ExtendObsoleteArrays(Thread* self, uint32_t increase) REQUIRES_SHARED(Locks::mutator_lock_); - static void SetClass(ObjPtr dalvik_system_ClassExt); - static void ResetClass(); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - template inline void VisitNativeRoots(Visitor& visitor, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); @@ -93,8 +88,6 @@ class MANAGED ClassExt : public Object { // The saved verification error of this class. HeapReference verify_error_; - static GcRoot dalvik_system_ClassExt_; - friend struct art::ClassExtOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(ClassExt); }; diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index 3ffedcac4bb0b49b069068f6687c347fdbfca18a..faec6e6bf8de335e7951607eab86414692cc8cbb 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -28,12 +28,13 @@ #include "class_linker.h" #include "dex/dex_file.h" #include "gc/heap-inl.h" -#include "gc_root.h" +#include "gc_root-inl.h" #include "mirror/call_site.h" #include "mirror/class.h" #include "mirror/method_type.h" #include "obj_ptr.h" #include "runtime.h" +#include "write_barrier-inl.h" #include @@ -76,7 +77,7 @@ inline void DexCache::SetResolvedString(dex::StringIndex string_idx, ObjPtrRecordResolveString(this, string_idx); } // TODO: Fine-grained marking, so that we don't need to go through all arrays in full. - runtime->GetHeap()->WriteBarrierEveryFieldOf(this); + WriteBarrier::ForEveryFieldWrite(this); } inline void DexCache::ClearString(dex::StringIndex string_idx) { @@ -113,7 +114,7 @@ inline void DexCache::SetResolvedType(dex::TypeIndex type_idx, ObjPtr res GetResolvedTypes()[TypeSlotIndex(type_idx)].store( TypeDexCachePair(resolved, type_idx.index_), std::memory_order_release); // TODO: Fine-grained marking, so that we don't need to go through all arrays in full. - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this); + WriteBarrier::ForEveryFieldWrite(this); } inline void DexCache::ClearResolvedType(dex::TypeIndex type_idx) { @@ -127,25 +128,25 @@ inline void DexCache::ClearResolvedType(dex::TypeIndex type_idx) { } } -inline uint32_t DexCache::MethodTypeSlotIndex(uint32_t proto_idx) { +inline uint32_t DexCache::MethodTypeSlotIndex(dex::ProtoIndex proto_idx) { DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); - DCHECK_LT(proto_idx, GetDexFile()->NumProtoIds()); - const uint32_t slot_idx = proto_idx % kDexCacheMethodTypeCacheSize; + DCHECK_LT(proto_idx.index_, GetDexFile()->NumProtoIds()); + const uint32_t slot_idx = proto_idx.index_ % kDexCacheMethodTypeCacheSize; DCHECK_LT(slot_idx, NumResolvedMethodTypes()); return slot_idx; } -inline MethodType* DexCache::GetResolvedMethodType(uint32_t proto_idx) { +inline MethodType* DexCache::GetResolvedMethodType(dex::ProtoIndex proto_idx) { return GetResolvedMethodTypes()[MethodTypeSlotIndex(proto_idx)].load( - std::memory_order_relaxed).GetObjectForIndex(proto_idx); + std::memory_order_relaxed).GetObjectForIndex(proto_idx.index_); } -inline void DexCache::SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) { +inline void DexCache::SetResolvedMethodType(dex::ProtoIndex proto_idx, MethodType* resolved) { DCHECK(resolved != nullptr); GetResolvedMethodTypes()[MethodTypeSlotIndex(proto_idx)].store( - MethodTypeDexCachePair(resolved, proto_idx), std::memory_order_relaxed); + MethodTypeDexCachePair(resolved, proto_idx.index_), std::memory_order_relaxed); // TODO: Fine-grained marking, so that we don't need to go through all arrays in full. - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this); + WriteBarrier::ForEveryFieldWrite(this); } inline CallSite* DexCache::GetResolvedCallSite(uint32_t call_site_idx) { @@ -154,10 +155,11 @@ inline CallSite* DexCache::GetResolvedCallSite(uint32_t call_site_idx) { GcRoot& target = GetResolvedCallSites()[call_site_idx]; Atomic>& ref = reinterpret_cast>&>(target); - return ref.LoadSequentiallyConsistent().Read(); + return ref.load(std::memory_order_seq_cst).Read(); } -inline CallSite* DexCache::SetResolvedCallSite(uint32_t call_site_idx, CallSite* call_site) { +inline ObjPtr DexCache::SetResolvedCallSite(uint32_t call_site_idx, + ObjPtr call_site) { DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); DCHECK_LT(call_site_idx, GetDexFile()->NumCallSiteIds()); @@ -170,7 +172,7 @@ inline CallSite* DexCache::SetResolvedCallSite(uint32_t call_site_idx, CallSite* reinterpret_cast>&>(target); if (ref.CompareAndSetStrongSequentiallyConsistent(null_call_site, candidate)) { // TODO: Fine-grained marking, so that we don't need to go through all arrays in full. - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this); + WriteBarrier::ForEveryFieldWrite(this); return call_site; } else { return target.Read(); diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc index eb4db00ccd7d3508c2d18f56d9cfc0f9907a573a..661f954ef551bbe109c29a9a12334d47fbd2bde8 100644 --- a/runtime/mirror/dex_cache.cc +++ b/runtime/mirror/dex_cache.cc @@ -17,10 +17,10 @@ #include "dex_cache-inl.h" #include "art_method-inl.h" +#include "base/globals.h" #include "class_linker.h" #include "gc/accounting/card_table-inl.h" #include "gc/heap.h" -#include "globals.h" #include "linear_alloc.h" #include "oat_file.h" #include "object-inl.h" diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index d940964edb193adb8625a14e9d6029e7cc815b87..941248edf71f7b500e19238ede15007b0f4d7576 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -21,6 +21,7 @@ #include "base/bit_utils.h" #include "base/mutex.h" #include "dex/dex_file_types.h" +#include "gc_root-inl.h" #include "object.h" #include "object_array.h" @@ -307,9 +308,9 @@ class MANAGED DexCache FINAL : public Object { ALWAYS_INLINE void ClearResolvedField(uint32_t idx, PointerSize ptr_size) REQUIRES_SHARED(Locks::mutator_lock_); - MethodType* GetResolvedMethodType(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_); + MethodType* GetResolvedMethodType(dex::ProtoIndex proto_idx) REQUIRES_SHARED(Locks::mutator_lock_); - void SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) + void SetResolvedMethodType(dex::ProtoIndex proto_idx, MethodType* resolved) REQUIRES_SHARED(Locks::mutator_lock_); CallSite* GetResolvedCallSite(uint32_t call_site_idx) REQUIRES_SHARED(Locks::mutator_lock_); @@ -319,8 +320,8 @@ class MANAGED DexCache FINAL : public Object { // 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 SetResolvedCallSite(uint32_t call_site_idx, ObjPtr resolved) + REQUIRES_SHARED(Locks::mutator_lock_) WARN_UNUSED; StringDexCacheType* GetStrings() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) { return GetFieldPtr64(StringsOffset()); @@ -432,7 +433,7 @@ class MANAGED DexCache FINAL : public Object { uint32_t TypeSlotIndex(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_); uint32_t FieldSlotIndex(uint32_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_); uint32_t MethodSlotIndex(uint32_t method_idx) REQUIRES_SHARED(Locks::mutator_lock_); - uint32_t MethodTypeSlotIndex(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_); + uint32_t MethodTypeSlotIndex(dex::ProtoIndex proto_idx) REQUIRES_SHARED(Locks::mutator_lock_); private: void Init(const DexFile* dex_file, diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc index d2bff2c19af6b41910e1125a46ace2365d3a26bf..7a70cae1ef3f2e111f226396ab7418655e9d3a68 100644 --- a/runtime/mirror/dex_cache_test.cc +++ b/runtime/mirror/dex_cache_test.cc @@ -83,7 +83,7 @@ TEST_F(DexCacheTest, LinearAlloc) { StackHandleScope<1> hs(soa.Self()); Handle class_loader(hs.NewHandle( soa.Decode(jclass_loader))); - mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader); + ObjPtr klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader); ASSERT_TRUE(klass != nullptr); LinearAlloc* const linear_alloc = klass->GetClassLoader()->GetAllocator(); EXPECT_NE(linear_alloc, runtime_->GetLinearAlloc()); @@ -169,9 +169,9 @@ TEST_F(DexCacheMethodHandlesTest, TestResolvedMethodTypes) { for (size_t i = 0; i < dex_file.NumProtoIds(); ++i) { const MethodTypeDexCachePair pair = method_types_cache[i].load(std::memory_order_relaxed); - if (pair.index == method1_id.proto_idx_) { + if (dex::ProtoIndex(pair.index) == method1_id.proto_idx_) { ASSERT_EQ(method1_type.Get(), pair.object.Read()); - } else if (pair.index == method2_id.proto_idx_) { + } else if (dex::ProtoIndex(pair.index) == method2_id.proto_idx_) { ASSERT_EQ(method2_type.Get(), pair.object.Read()); } else { ASSERT_TRUE(false); diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc index 5f00c6e9c701cf49b5a8342682af33eea319417a..ce39049e11723a01a573f3bcb04540f08cfe0a6b 100644 --- a/runtime/mirror/emulated_stack_frame.cc +++ b/runtime/mirror/emulated_stack_frame.cc @@ -17,7 +17,7 @@ #include "emulated_stack_frame.h" #include "class-inl.h" -#include "gc_root-inl.h" +#include "class_root.h" #include "jvalue-inl.h" #include "method_handles-inl.h" #include "method_handles.h" @@ -26,8 +26,6 @@ namespace art { namespace mirror { -GcRoot EmulatedStackFrame::static_class_; - // Calculates the size of a stack frame based on the size of its argument // types and return types. static void CalculateFrameAndReferencesSize(ObjPtr> p_types, @@ -166,8 +164,7 @@ mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs( CalculateFrameAndReferencesSize(to_types.Get(), r_type.Get(), &frame_size, &refs_size); // Step 3 : Allocate the arrays. - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - ObjPtr array_class(class_linker->GetClassRoot(ClassLinker::kObjectArrayClass)); + ObjPtr array_class(GetClassRoot>()); Handle> references(hs.NewHandle( mirror::ObjectArray::Alloc(self, array_class, refs_size))); @@ -192,7 +189,7 @@ mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs( // Step 5: Construct the EmulatedStackFrame object. Handle sf(hs.NewHandle( - ObjPtr::DownCast(StaticClass()->AllocObject(self)))); + ObjPtr::DownCast(GetClassRoot()->AllocObject(self)))); sf->SetFieldObject(CallsiteTypeOffset(), caller_type.Get()); sf->SetFieldObject(TypeOffset(), callee_type.Get()); sf->SetFieldObject(ReferencesOffset(), references.Get()); @@ -272,20 +269,5 @@ void EmulatedStackFrame::SetReturnValue(Thread* self, const JValue& value) { } } -void EmulatedStackFrame::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void EmulatedStackFrame::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void EmulatedStackFrame::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - } // namespace mirror } // namespace art diff --git a/runtime/mirror/emulated_stack_frame.h b/runtime/mirror/emulated_stack_frame.h index 23626f46e0098eace44ef9ce3bbf0a2d9252b15f..ec45f57a4d286eda8dbb5c9ee9698814459a0036 100644 --- a/runtime/mirror/emulated_stack_frame.h +++ b/runtime/mirror/emulated_stack_frame.h @@ -64,15 +64,7 @@ class MANAGED EmulatedStackFrame : public Object { return GetReferences()->Get(0); } - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); - } - mirror::ObjectArray* GetReferences() REQUIRES_SHARED(Locks::mutator_lock_) { return GetFieldObject>( OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, references_)); @@ -104,8 +96,6 @@ class MANAGED EmulatedStackFrame : public Object { HeapReference stack_frame_; HeapReference type_; - static GcRoot static_class_; // dalvik.system.EmulatedStackFrame.class - friend struct art::EmulatedStackFrameOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(EmulatedStackFrame); }; diff --git a/runtime/mirror/executable.h b/runtime/mirror/executable.h index 8a28f66868757fb98acae55027133c79003a6310..23dd787c800840b5b471f2895acf884d98dd5fcd 100644 --- a/runtime/mirror/executable.h +++ b/runtime/mirror/executable.h @@ -18,7 +18,6 @@ #define ART_RUNTIME_MIRROR_EXECUTABLE_H_ #include "accessible_object.h" -#include "gc_root.h" #include "object.h" #include "read_barrier_option.h" diff --git a/runtime/mirror/field-inl.h b/runtime/mirror/field-inl.h index bcd2db4dbdea41d3fbdeb65f1f22ade3b4f3e030..2e263b9517c3020dc02a7e4ff3ef9714305023e9 100644 --- a/runtime/mirror/field-inl.h +++ b/runtime/mirror/field-inl.h @@ -21,6 +21,7 @@ #include "art_field-inl.h" #include "class-inl.h" +#include "class_root.h" #include "dex_cache-inl.h" namespace art { @@ -48,7 +49,7 @@ inline mirror::Field* Field::CreateFromArtField(Thread* self, ArtField* field, b self->ClearException(); } } - auto ret = hs.NewHandle(ObjPtr::DownCast(StaticClass()->AllocObject(self))); + auto ret = hs.NewHandle(ObjPtr::DownCast(GetClassRoot()->AllocObject(self))); if (UNLIKELY(ret == nullptr)) { self->AssertPendingOOMException(); return nullptr; diff --git a/runtime/mirror/field.cc b/runtime/mirror/field.cc index b4d93b6d4dfc473506a2f53bfe63d187cbfd4ccd..a2b51d8d9af4560e8b81d5474f9931a4193e1890 100644 --- a/runtime/mirror/field.cc +++ b/runtime/mirror/field.cc @@ -24,36 +24,6 @@ namespace art { namespace mirror { -GcRoot Field::static_class_; -GcRoot Field::array_class_; - -void Field::SetClass(ObjPtr klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void Field::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void Field::SetArrayClass(ObjPtr klass) { - CHECK(array_class_.IsNull()) << array_class_.Read() << " " << klass; - CHECK(klass != nullptr); - array_class_ = GcRoot(klass); -} - -void Field::ResetArrayClass() { - CHECK(!array_class_.IsNull()); - array_class_ = GcRoot(nullptr); -} - -void Field::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); - array_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - ArtField* Field::GetArtField() { mirror::Class* declaring_class = GetDeclaringClass(); if (UNLIKELY(declaring_class->IsProxyClass())) { diff --git a/runtime/mirror/field.h b/runtime/mirror/field.h index 03fd0313043c0c5c3a32bb06933bf8f2e18e270f..3501e71f82d23ee2e50e48b851bec67a860f6829 100644 --- a/runtime/mirror/field.h +++ b/runtime/mirror/field.h @@ -21,7 +21,6 @@ #include "base/enums.h" #include "dex/modifiers.h" #include "dex/primitive.h" -#include "gc_root.h" #include "obj_ptr.h" #include "object.h" #include "read_barrier_option.h" @@ -39,14 +38,6 @@ class String; // C++ mirror of java.lang.reflect.Field. class MANAGED Field : public AccessibleObject { public: - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); - } - - static mirror::Class* ArrayClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return array_class_.Read(); - } - ALWAYS_INLINE uint32_t GetDexFieldIndex() REQUIRES_SHARED(Locks::mutator_lock_) { return GetField32(OFFSET_OF_OBJECT_MEMBER(Field, dex_field_index_)); } @@ -81,14 +72,6 @@ class MANAGED Field : public AccessibleObject { return GetField32(OFFSET_OF_OBJECT_MEMBER(Field, offset_)); } - static void SetClass(ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - - static void SetArrayClass(ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetArrayClass() REQUIRES_SHARED(Locks::mutator_lock_); - - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - // Slow, try to use only for PrettyField and such. ArtField* GetArtField() REQUIRES_SHARED(Locks::mutator_lock_); @@ -128,9 +111,6 @@ class MANAGED Field : public AccessibleObject { SetField32(OFFSET_OF_OBJECT_MEMBER(Field, offset_), offset); } - static GcRoot static_class_; // java.lang.reflect.Field.class. - static GcRoot array_class_; // array of java.lang.reflect.Field. - friend struct art::FieldOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(Field); }; diff --git a/runtime/mirror/method.cc b/runtime/mirror/method.cc index 25cbdc131b3215a55f5e8cddf96184e9df7467ff..cf03b95d5eefcdcce733d0b114c60e0247a102ea 100644 --- a/runtime/mirror/method.cc +++ b/runtime/mirror/method.cc @@ -17,44 +17,17 @@ #include "method.h" #include "art_method.h" -#include "gc_root-inl.h" +#include "class_root.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" namespace art { namespace mirror { -GcRoot Method::static_class_; -GcRoot Method::array_class_; -GcRoot Constructor::static_class_; -GcRoot Constructor::array_class_; - -void Method::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void Method::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void Method::SetArrayClass(Class* klass) { - CHECK(array_class_.IsNull()) << array_class_.Read() << " " << klass; - CHECK(klass != nullptr); - array_class_ = GcRoot(klass); -} - -void Method::ResetArrayClass() { - CHECK(!array_class_.IsNull()); - array_class_ = GcRoot(nullptr); -} - template Method* Method::CreateFromArtMethod(Thread* self, ArtMethod* method) { DCHECK(!method->IsConstructor()) << method->PrettyMethod(); - ObjPtr ret = ObjPtr::DownCast(StaticClass()->AllocObject(self)); + ObjPtr ret = ObjPtr::DownCast(GetClassRoot()->AllocObject(self)); if (LIKELY(ret != nullptr)) { ObjPtr(ret)-> CreateFromArtMethod(method); @@ -71,42 +44,11 @@ template Method* Method::CreateFromArtMethod(Thread* se template Method* Method::CreateFromArtMethod(Thread* self, ArtMethod* method); -void Method::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); - array_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - -void Constructor::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void Constructor::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void Constructor::SetArrayClass(Class* klass) { - CHECK(array_class_.IsNull()) << array_class_.Read() << " " << klass; - CHECK(klass != nullptr); - array_class_ = GcRoot(klass); -} - -void Constructor::ResetArrayClass() { - CHECK(!array_class_.IsNull()); - array_class_ = GcRoot(nullptr); -} - -void Constructor::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); - array_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - template Constructor* Constructor::CreateFromArtMethod(Thread* self, ArtMethod* method) { DCHECK(method->IsConstructor()) << method->PrettyMethod(); - ObjPtr ret = ObjPtr::DownCast(StaticClass()->AllocObject(self)); + ObjPtr ret = + ObjPtr::DownCast(GetClassRoot()->AllocObject(self)); if (LIKELY(ret != nullptr)) { ObjPtr(ret)-> CreateFromArtMethod(method); diff --git a/runtime/mirror/method.h b/runtime/mirror/method.h index 61332e3bd91c18205063bceaa4edbb81689ef5d8..aea15a7748142af72faa2558cd490b6aa7bf28cf 100644 --- a/runtime/mirror/method.h +++ b/runtime/mirror/method.h @@ -18,7 +18,6 @@ #define ART_RUNTIME_MIRROR_METHOD_H_ #include "executable.h" -#include "gc_root.h" namespace art { namespace mirror { @@ -32,28 +31,7 @@ class MANAGED Method : public Executable { static Method* CreateFromArtMethod(Thread* self, ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); - } - - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - - static mirror::Class* ArrayClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return array_class_.Read(); - } - - static void SetArrayClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - - static void ResetArrayClass() REQUIRES_SHARED(Locks::mutator_lock_); - - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: - static GcRoot static_class_; // java.lang.reflect.Method.class. - static GcRoot array_class_; // [java.lang.reflect.Method.class. - DISALLOW_COPY_AND_ASSIGN(Method); }; @@ -64,28 +42,7 @@ class MANAGED Constructor: public Executable { static Constructor* CreateFromArtMethod(Thread* self, ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); - } - - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - - static mirror::Class* ArrayClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return array_class_.Read(); - } - - static void SetArrayClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - - static void ResetArrayClass() REQUIRES_SHARED(Locks::mutator_lock_); - - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: - static GcRoot static_class_; // java.lang.reflect.Constructor.class. - static GcRoot array_class_; // [java.lang.reflect.Constructor.class. - DISALLOW_COPY_AND_ASSIGN(Constructor); }; diff --git a/runtime/mirror/method_handle_impl.cc b/runtime/mirror/method_handle_impl.cc index 0b4dde1aa89d127df03985f8b99594cbee5b564b..88ccbc947d728444323e97beb9051c8c21508017 100644 --- a/runtime/mirror/method_handle_impl.cc +++ b/runtime/mirror/method_handle_impl.cc @@ -17,7 +17,7 @@ #include "method_handle_impl-inl.h" #include "class-inl.h" -#include "gc_root-inl.h" +#include "class_root.h" namespace art { namespace mirror { @@ -30,12 +30,6 @@ const char* MethodHandle::GetReturnTypeDescriptor(const char* invoke_method_name } } -mirror::Class* MethodHandle::StaticClass() { - mirror::Class* klass = MethodHandleImpl::StaticClass()->GetSuperClass(); - DCHECK(klass->DescriptorEquals("Ljava/lang/invoke/MethodHandle;")); - return klass; -} - void MethodHandle::Initialize(uintptr_t art_field_or_method, Kind kind, Handle method_type) @@ -48,35 +42,14 @@ void MethodHandle::Initialize(uintptr_t art_field_or_method, SetField64(ArtFieldOrMethodOffset(), art_field_or_method); } -GcRoot MethodHandleImpl::static_class_; - -mirror::Class* MethodHandleImpl::StaticClass() { - return static_class_.Read(); -} - -void MethodHandleImpl::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void MethodHandleImpl::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void MethodHandleImpl::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - mirror::MethodHandleImpl* MethodHandleImpl::Create(Thread* const self, uintptr_t art_field_or_method, MethodHandle::Kind kind, Handle method_type) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) { StackHandleScope<1> hs(self); - Handle mh( - hs.NewHandle(ObjPtr::DownCast(StaticClass()->AllocObject(self)))); + Handle mh(hs.NewHandle(ObjPtr::DownCast( + GetClassRoot()->AllocObject(self)))); mh->Initialize(art_field_or_method, kind, method_type); return mh.Get(); } diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h index 3b0002c2af0e1d8cb6af2c9eb7fea79caeec06bd..030a49ed1ee9f11fd81a52fc263effd9dc07cce8 100644 --- a/runtime/mirror/method_handle_impl.h +++ b/runtime/mirror/method_handle_impl.h @@ -20,7 +20,6 @@ #include "art_field.h" #include "art_method.h" #include "class.h" -#include "gc_root.h" #include "method_type.h" #include "object.h" @@ -87,8 +86,6 @@ class MANAGED MethodHandle : public Object { // supported. static const char* GetReturnTypeDescriptor(const char* invoke_method_name); - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_); - protected: void Initialize(uintptr_t art_field_or_method, Kind kind, Handle method_type) REQUIRES_SHARED(Locks::mutator_lock_); @@ -130,19 +127,12 @@ class MANAGED MethodHandleImpl : public MethodHandle { Handle method_type) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_); - - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: static MemberOffset InfoOffset() { return MemberOffset(OFFSETOF_MEMBER(MethodHandleImpl, info_)); } HeapReference info_; // Unused by the runtime. - static GcRoot static_class_; // java.lang.invoke.MethodHandleImpl.class friend struct art::MethodHandleImplOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(MethodHandleImpl); diff --git a/runtime/mirror/method_handles_lookup.cc b/runtime/mirror/method_handles_lookup.cc index 039bbf293295480497b7c31bde58eb8c79d08b32..d1e7a6dbfa9362c28ef0be9b67a0fac7dd7c6db5 100644 --- a/runtime/mirror/method_handles_lookup.cc +++ b/runtime/mirror/method_handles_lookup.cc @@ -17,10 +17,10 @@ #include "method_handles_lookup.h" #include "class-inl.h" +#include "class_root.h" #include "dex/modifiers.h" -#include "gc_root-inl.h" #include "handle_scope.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/method_handle_impl.h" #include "object-inl.h" #include "well_known_classes.h" @@ -28,33 +28,15 @@ namespace art { namespace mirror { -GcRoot MethodHandlesLookup::static_class_; - -void MethodHandlesLookup::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void MethodHandlesLookup::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void MethodHandlesLookup::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - MethodHandlesLookup* MethodHandlesLookup::Create(Thread* const self, Handle lookup_class) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) { static constexpr uint32_t kAllModes = kAccPublic | kAccPrivate | kAccProtected | kAccStatic; - StackHandleScope<1> hs(self); - Handle mhl( - hs.NewHandle(ObjPtr::DownCast(StaticClass()->AllocObject(self)))); + ObjPtr mhl = ObjPtr::DownCast( + GetClassRoot()->AllocObject(self)); mhl->SetFieldObject(LookupClassOffset(), lookup_class.Get()); mhl->SetField32(AllowedModesOffset(), kAllModes); - return mhl.Get(); + return mhl.Ptr(); } MethodHandlesLookup* MethodHandlesLookup::GetDefault(Thread* const self) { diff --git a/runtime/mirror/method_handles_lookup.h b/runtime/mirror/method_handles_lookup.h index fefcb2ed29a8a5007c67d37eece98f59aefe6da3..56261eca676aadcd280a0abb119a3f9654facf94 100644 --- a/runtime/mirror/method_handles_lookup.h +++ b/runtime/mirror/method_handles_lookup.h @@ -18,7 +18,6 @@ #define ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_ #include "base/utils.h" -#include "gc_root.h" #include "handle.h" #include "obj_ptr.h" #include "object.h" @@ -40,14 +39,6 @@ class MANAGED MethodHandlesLookup : public Object { Handle lookup_class) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); - } - - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - // Returns the result of java.lang.invoke.MethodHandles.lookup(). static mirror::MethodHandlesLookup* GetDefault(Thread* const self) REQUIRES_SHARED(Locks::mutator_lock_); @@ -71,8 +62,6 @@ class MANAGED MethodHandlesLookup : public Object { int32_t allowed_modes_; - static GcRoot static_class_; // java.lang.invoke.MethodHandles.Lookup.class - friend struct art::MethodHandlesLookupOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(MethodHandlesLookup); }; diff --git a/runtime/mirror/method_type.cc b/runtime/mirror/method_type.cc index 6ac5012ca3238b734aefb2b97fd2882b6f28789f..bc62ebdc8b3fdd488d6a5daeb23f3d9cdaa9cbfb 100644 --- a/runtime/mirror/method_type.cc +++ b/runtime/mirror/method_type.cc @@ -17,20 +17,28 @@ #include "method_type.h" #include "class-inl.h" -#include "gc_root-inl.h" +#include "class_root.h" #include "method_handles.h" namespace art { namespace mirror { -GcRoot MethodType::static_class_; +namespace { + +ObjPtr> AllocatePTypesArray(Thread* self, int count) + REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr class_array_type = GetClassRoot>(); + return ObjectArray::Alloc(self, class_array_type, count); +} + +} // namespace MethodType* MethodType::Create(Thread* const self, Handle return_type, Handle> parameter_types) { StackHandleScope<1> hs(self); Handle mt( - hs.NewHandle(ObjPtr::DownCast(StaticClass()->AllocObject(self)))); + hs.NewHandle(ObjPtr::DownCast(GetClassRoot()->AllocObject(self)))); // TODO: Do we ever create a MethodType during a transaction ? There doesn't // seem like a good reason to do a polymorphic invoke that results in the @@ -47,18 +55,41 @@ MethodType* MethodType::Create(Thread* const self, MethodType* MethodType::CloneWithoutLeadingParameter(Thread* const self, ObjPtr method_type) { StackHandleScope<3> hs(self); - Handle rtype = hs.NewHandle(method_type->GetRType()); Handle> src_ptypes = hs.NewHandle(method_type->GetPTypes()); - ObjPtr class_type = Class::GetJavaLangClass(); - ObjPtr class_array_type = - Runtime::Current()->GetClassLinker()->FindArrayClass(self, &class_type); - const int32_t dst_ptypes_count = src_ptypes->GetLength() - 1; - Handle> dst_ptypes = hs.NewHandle( - ObjectArray::Alloc(self, class_array_type, dst_ptypes_count)); + Handle dst_rtype = hs.NewHandle(method_type->GetRType()); + const int32_t dst_ptypes_count = method_type->GetNumberOfPTypes() - 1; + Handle> dst_ptypes = hs.NewHandle(AllocatePTypesArray(self, dst_ptypes_count)); + if (dst_ptypes.IsNull()) { + return nullptr; + } for (int32_t i = 0; i < dst_ptypes_count; ++i) { dst_ptypes->Set(i, src_ptypes->Get(i + 1)); } - return Create(self, rtype, dst_ptypes); + return Create(self, dst_rtype, dst_ptypes); +} + +MethodType* MethodType::CollectTrailingArguments(Thread* self, + ObjPtr method_type, + ObjPtr collector_array_class, + int32_t start_index) { + int32_t ptypes_length = method_type->GetNumberOfPTypes(); + if (start_index > ptypes_length) { + return method_type.Ptr(); + } + + StackHandleScope<4> hs(self); + Handle collector_class = hs.NewHandle(collector_array_class); + Handle dst_rtype = hs.NewHandle(method_type->GetRType()); + Handle> src_ptypes = hs.NewHandle(method_type->GetPTypes()); + Handle> dst_ptypes = hs.NewHandle(AllocatePTypesArray(self, start_index + 1)); + if (dst_ptypes.IsNull()) { + return nullptr; + } + for (int32_t i = 0; i < start_index; ++i) { + dst_ptypes->Set(i, src_ptypes->Get(i)); + } + dst_ptypes->Set(start_index, collector_class.Get()); + return Create(self, dst_rtype, dst_ptypes); } size_t MethodType::NumberOfVRegs() REQUIRES_SHARED(Locks::mutator_lock_) { @@ -137,20 +168,5 @@ std::string MethodType::PrettyDescriptor() REQUIRES_SHARED(Locks::mutator_lock_) return ss.str(); } -void MethodType::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void MethodType::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void MethodType::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - } // namespace mirror } // namespace art diff --git a/runtime/mirror/method_type.h b/runtime/mirror/method_type.h index 3627214d90fa9df13098edb5b1a7ae21a12503f4..014b211d66a00295a97ffa761c4aa9acc76f0092 100644 --- a/runtime/mirror/method_type.h +++ b/runtime/mirror/method_type.h @@ -40,9 +40,13 @@ class MANAGED MethodType : public Object { ObjPtr method_type) REQUIRES_SHARED(Locks::mutator_lock_); - static Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); - } + // Collects trailing parameter types into an array. Assumes caller + // has checked trailing arguments are all of the same type. + static MethodType* CollectTrailingArguments(Thread* const self, + ObjPtr method_type, + ObjPtr collector_array_class, + int32_t start_index) + REQUIRES_SHARED(Locks::mutator_lock_); ObjectArray* GetPTypes() REQUIRES_SHARED(Locks::mutator_lock_) { return GetFieldObject>(OFFSET_OF_OBJECT_MEMBER(MethodType, p_types_)); @@ -60,10 +64,6 @@ class MANAGED MethodType : public Object { return GetFieldObject(OFFSET_OF_OBJECT_MEMBER(MethodType, r_type_)); } - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - // Returns true iff. |this| is an exact match for method type |target|, i.e // iff. they have the same return types and parameter types. bool IsExactMatch(MethodType* target) REQUIRES_SHARED(Locks::mutator_lock_); @@ -103,8 +103,6 @@ class MANAGED MethodType : public Object { HeapReference r_type_; HeapReference wrap_alt_; // Unused in the runtime - static GcRoot static_class_; // java.lang.invoke.MethodType.class - friend struct art::MethodTypeOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(MethodType); }; diff --git a/runtime/mirror/method_type_test.cc b/runtime/mirror/method_type_test.cc index a361772c58b6795d94ed087841251792adadae8c..2bdea72f147af0083e3bb9c0c6f41a3621d1aa31 100644 --- a/runtime/mirror/method_type_test.cc +++ b/runtime/mirror/method_type_test.cc @@ -22,6 +22,7 @@ #include "class-inl.h" #include "class_linker-inl.h" #include "class_loader.h" +#include "class_root.h" #include "common_runtime_test.h" #include "handle_scope-inl.h" #include "object_array-inl.h" @@ -53,8 +54,8 @@ static mirror::MethodType* CreateMethodType(const std::string& return_type, soa.Self(), FullyQualifiedType(return_type).c_str(), boot_class_loader)); CHECK(return_clazz != nullptr); - ObjPtr class_type = mirror::Class::GetJavaLangClass(); - mirror::Class* class_array_type = class_linker->FindArrayClass(self, &class_type); + ObjPtr class_array_type = + GetClassRoot>(class_linker); Handle> param_classes = hs.NewHandle( mirror::ObjectArray::Alloc(self, class_array_type, param_types.size())); diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index 55dd51427cadb8ebeaffe4d4476b35cd6c88d840..47f0a298de1185dd51f65befbd6cf123ad5a5b11 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -37,8 +37,9 @@ #include "read_barrier-inl.h" #include "reference.h" #include "runtime.h" -#include "string-inl.h" +#include "string.h" #include "throwable.h" +#include "write_barrier-inl.h" namespace art { namespace mirror { @@ -50,8 +51,7 @@ inline uint32_t Object::ClassSize(PointerSize pointer_size) { template inline Class* Object::GetClass() { - return GetFieldObject( - OFFSET_OF_OBJECT_MEMBER(Object, klass_)); + return GetFieldObject(ClassOffset()); } template @@ -61,35 +61,20 @@ inline void Object::SetClass(ObjPtr new_klass) { // backing cards, such as large objects. // We use non transactional version since we can't undo this write. We also disable checking as // we may run in transaction mode here. - SetFieldObjectWithoutWriteBarrier(kVerifyFlags & ~kVerifyThis)>( - OFFSET_OF_OBJECT_MEMBER(Object, klass_), new_klass); + SetFieldObjectWithoutWriteBarrier(ClassOffset(), + new_klass); } template inline void Object::SetLockWord(LockWord new_val, bool as_volatile) { // Force use of non-transactional mode and do not check. if (as_volatile) { - SetField32Volatile( - OFFSET_OF_OBJECT_MEMBER(Object, monitor_), new_val.GetValue()); + SetField32Volatile(MonitorOffset(), new_val.GetValue()); } else { - SetField32( - OFFSET_OF_OBJECT_MEMBER(Object, monitor_), new_val.GetValue()); + SetField32(MonitorOffset(), new_val.GetValue()); } } -inline bool Object::CasLockWordWeakSequentiallyConsistent(LockWord old_val, LockWord new_val) { - // Force use of non-transactional mode and do not check. - return CasFieldWeakSequentiallyConsistent32( - OFFSET_OF_OBJECT_MEMBER(Object, monitor_), old_val.GetValue(), new_val.GetValue()); -} - -inline bool Object::CasLockWordWeakAcquire(LockWord old_val, LockWord new_val) { - // Force use of non-transactional mode and do not check. - return CasFieldWeakAcquire32( - OFFSET_OF_OBJECT_MEMBER(Object, monitor_), old_val.GetValue(), new_val.GetValue()); -} - inline uint32_t Object::GetLockOwnerThreadId() { return Monitor::GetLockOwnerThreadId(this); } @@ -114,28 +99,17 @@ inline void Object::NotifyAll(Thread* self) { Monitor::NotifyAll(self, this); } -inline void Object::Wait(Thread* self) { - Monitor::Wait(self, this, 0, 0, true, kWaiting); -} - inline void Object::Wait(Thread* self, int64_t ms, int32_t ns) { Monitor::Wait(self, this, ms, ns, true, kTimedWaiting); } inline uint32_t Object::GetMarkBit() { -#ifdef USE_READ_BARRIER + CHECK(kUseReadBarrier); return GetLockWord(false).MarkBitState(); -#else - LOG(FATAL) << "Unreachable"; - UNREACHABLE(); -#endif } inline void Object::SetReadBarrierState(uint32_t rb_state) { - if (!kUseBakerReadBarrier) { - LOG(FATAL) << "Unreachable"; - UNREACHABLE(); - } + CHECK(kUseBakerReadBarrier); DCHECK(ReadBarrier::IsValidReadBarrierState(rb_state)) << rb_state; LockWord lw = GetLockWord(false); lw.SetReadBarrierState(rb_state); @@ -145,9 +119,8 @@ inline void Object::SetReadBarrierState(uint32_t rb_state) { inline void Object::AssertReadBarrierState() const { CHECK(kUseBakerReadBarrier); Object* obj = const_cast(this); - DCHECK(obj->GetReadBarrierState() == ReadBarrier::WhiteState()) - << "Bad Baker pointer: obj=" << reinterpret_cast(obj) - << " rb_state" << reinterpret_cast(obj->GetReadBarrierState()); + DCHECK_EQ(obj->GetReadBarrierState(), ReadBarrier::WhiteState()) + << "Bad Baker pointer: obj=" << obj << " rb_state" << obj->GetReadBarrierState(); } template @@ -160,17 +133,16 @@ inline bool Object::VerifierInstanceOf(ObjPtr klass) { template inline bool Object::InstanceOf(ObjPtr klass) { DCHECK(klass != nullptr); - DCHECK(GetClass() != nullptr) - << "this=" << std::hex << reinterpret_cast(this) << std::dec; + DCHECK(GetClass() != nullptr) << "this=" << this; return klass->IsAssignableFrom(GetClass()); } template inline bool Object::IsClass() { + constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags); Class* java_lang_Class = GetClass()-> template GetClass(); - return GetClass(kVerifyFlags & ~kVerifyThis), - kReadBarrierOption>() == java_lang_Class; + return GetClass() == java_lang_Class; } template @@ -181,7 +153,7 @@ inline Class* Object::AsClass() { template inline bool Object::IsObjectArray() { - constexpr auto kNewFlags = static_cast(kVerifyFlags & ~kVerifyThis); + constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags); return IsArrayInstance() && !GetClass()-> template GetComponentType()->IsPrimitive(); @@ -218,7 +190,7 @@ inline Array* Object::AsArray() { template inline BooleanArray* Object::AsBooleanArray() { - constexpr auto kNewFlags = static_cast(kVerifyFlags & ~kVerifyThis); + constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags); DCHECK(GetClass()->IsArrayClass()); DCHECK(GetClass()->GetComponentType()->IsPrimitiveBoolean()); return down_cast(this); @@ -226,7 +198,7 @@ inline BooleanArray* Object::AsBooleanArray() { template inline ByteArray* Object::AsByteArray() { - constexpr auto kNewFlags = static_cast(kVerifyFlags & ~kVerifyThis); + constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags); DCHECK(GetClass()->IsArrayClass()); DCHECK(GetClass()->template GetComponentType()->IsPrimitiveByte()); return down_cast(this); @@ -234,7 +206,7 @@ inline ByteArray* Object::AsByteArray() { template inline ByteArray* Object::AsByteSizedArray() { - constexpr auto kNewFlags = static_cast(kVerifyFlags & ~kVerifyThis); + constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags); DCHECK(GetClass()->IsArrayClass()); DCHECK(GetClass()->template GetComponentType()->IsPrimitiveByte() || GetClass()->template GetComponentType()->IsPrimitiveBoolean()); @@ -243,7 +215,7 @@ inline ByteArray* Object::AsByteSizedArray() { template inline CharArray* Object::AsCharArray() { - constexpr auto kNewFlags = static_cast(kVerifyFlags & ~kVerifyThis); + constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags); DCHECK(GetClass()->IsArrayClass()); DCHECK(GetClass()->template GetComponentType()->IsPrimitiveChar()); return down_cast(this); @@ -251,7 +223,7 @@ inline CharArray* Object::AsCharArray() { template inline ShortArray* Object::AsShortArray() { - constexpr auto kNewFlags = static_cast(kVerifyFlags & ~kVerifyThis); + constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags); DCHECK(GetClass()->IsArrayClass()); DCHECK(GetClass()->template GetComponentType()->IsPrimitiveShort()); return down_cast(this); @@ -259,7 +231,7 @@ inline ShortArray* Object::AsShortArray() { template inline ShortArray* Object::AsShortSizedArray() { - constexpr auto kNewFlags = static_cast(kVerifyFlags & ~kVerifyThis); + constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags); DCHECK(GetClass()->IsArrayClass()); DCHECK(GetClass()->template GetComponentType()->IsPrimitiveShort() || GetClass()->template GetComponentType()->IsPrimitiveChar()); @@ -268,7 +240,7 @@ inline ShortArray* Object::AsShortSizedArray() { template inline bool Object::IsIntArray() { - constexpr auto kNewFlags = static_cast(kVerifyFlags & ~kVerifyThis); + constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags); ObjPtr klass = GetClass(); ObjPtr component_type = klass->GetComponentType(); return component_type != nullptr && component_type->template IsPrimitiveInt(); @@ -282,7 +254,7 @@ inline IntArray* Object::AsIntArray() { template inline bool Object::IsLongArray() { - constexpr auto kNewFlags = static_cast(kVerifyFlags & ~kVerifyThis); + constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags); ObjPtr klass = GetClass(); ObjPtr component_type = klass->GetComponentType(); return component_type != nullptr && component_type->template IsPrimitiveLong(); @@ -296,7 +268,7 @@ inline LongArray* Object::AsLongArray() { template inline bool Object::IsFloatArray() { - constexpr auto kNewFlags = static_cast(kVerifyFlags & ~kVerifyThis); + constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags); auto* component_type = GetClass()->GetComponentType(); return component_type != nullptr && component_type->template IsPrimitiveFloat(); } @@ -304,7 +276,7 @@ inline bool Object::IsFloatArray() { template inline FloatArray* Object::AsFloatArray() { DCHECK(IsFloatArray()); - constexpr auto kNewFlags = static_cast(kVerifyFlags & ~kVerifyThis); + constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags); DCHECK(GetClass()->IsArrayClass()); DCHECK(GetClass()->template GetComponentType()->IsPrimitiveFloat()); return down_cast(this); @@ -312,7 +284,7 @@ inline FloatArray* Object::AsFloatArray() { template inline bool Object::IsDoubleArray() { - constexpr auto kNewFlags = static_cast(kVerifyFlags & ~kVerifyThis); + constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags); auto* component_type = GetClass()->GetComponentType(); return component_type != nullptr && component_type->template IsPrimitiveDouble(); } @@ -320,7 +292,7 @@ inline bool Object::IsDoubleArray() { template inline DoubleArray* Object::AsDoubleArray() { DCHECK(IsDoubleArray()); - constexpr auto kNewFlags = static_cast(kVerifyFlags & ~kVerifyThis); + constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags); DCHECK(GetClass()->IsArrayClass()); DCHECK(GetClass()->template GetComponentType()->IsPrimitiveDouble()); return down_cast(this); @@ -373,32 +345,25 @@ template inline size_t Object::SizeOf() { // Read barrier is never required for SizeOf since objects sizes are constant. Reading from-space // values is OK because of that. - static constexpr ReadBarrierOption kReadBarrierOption = kWithoutReadBarrier; + static constexpr ReadBarrierOption kRBO = kWithoutReadBarrier; size_t result; - constexpr auto kNewFlags = static_cast(kVerifyFlags & ~kVerifyThis); - if (IsArrayInstance()) { - result = AsArray()-> - template SizeOf(); - } else if (IsClass()) { - result = AsClass()-> - template SizeOf(); - } else if (GetClass()->IsStringClass()) { - result = AsString()-> - template SizeOf(); + constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags); + if (IsArrayInstance()) { + result = AsArray()->template SizeOf(); + } else if (IsClass()) { + result = AsClass()->template SizeOf(); + } else if (GetClass()->IsStringClass()) { + result = AsString()->template SizeOf(); } else { - result = GetClass()-> - template GetObjectSize(); + result = GetClass()->template GetObjectSize(); } - DCHECK_GE(result, sizeof(Object)) - << " class=" << Class::PrettyClass(GetClass()); + DCHECK_GE(result, sizeof(Object)) << " class=" << Class::PrettyClass(GetClass()); return result; } template inline int8_t Object::GetFieldByte(MemberOffset field_offset) { - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } + Verify(); return GetField(field_offset); } @@ -412,39 +377,36 @@ inline int8_t Object::GetFieldByteVolatile(MemberOffset field_offset) { return GetFieldByte(field_offset); } -template -inline void Object::SetFieldBoolean(MemberOffset field_offset, uint8_t new_value) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } +template +inline void Object::SetFieldBoolean(MemberOffset field_offset, uint8_t new_value) { + VerifyTransaction(); if (kTransactionActive) { - Runtime::Current()->RecordWriteFieldBoolean(this, field_offset, - GetFieldBoolean(field_offset), - kIsVolatile); - } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); + Runtime::Current()->RecordWriteFieldBoolean( + this, + field_offset, + GetFieldBoolean(field_offset), + kIsVolatile); } + Verify(); SetField(field_offset, new_value); } -template -inline void Object::SetFieldByte(MemberOffset field_offset, int8_t new_value) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } +template +inline void Object::SetFieldByte(MemberOffset field_offset, int8_t new_value) { + VerifyTransaction(); if (kTransactionActive) { - Runtime::Current()->RecordWriteFieldByte(this, field_offset, - GetFieldByte(field_offset), - kIsVolatile); - } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); + Runtime::Current()->RecordWriteFieldByte(this, + field_offset, + GetFieldByte(field_offset), + kIsVolatile); } + Verify(); SetField(field_offset, new_value); } @@ -462,17 +424,13 @@ inline void Object::SetFieldByteVolatile(MemberOffset field_offset, int8_t new_v template inline uint16_t Object::GetFieldChar(MemberOffset field_offset) { - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } + Verify(); return GetField(field_offset); } template inline int16_t Object::GetFieldShort(MemberOffset field_offset) { - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } + Verify(); return GetField(field_offset); } @@ -486,37 +444,35 @@ inline int16_t Object::GetFieldShortVolatile(MemberOffset field_offset) { return GetFieldShort(field_offset); } -template +template inline void Object::SetFieldChar(MemberOffset field_offset, uint16_t new_value) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } + VerifyTransaction(); if (kTransactionActive) { - Runtime::Current()->RecordWriteFieldChar(this, field_offset, - GetFieldChar(field_offset), - kIsVolatile); - } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); + Runtime::Current()->RecordWriteFieldChar(this, + field_offset, + GetFieldChar(field_offset), + kIsVolatile); } + Verify(); SetField(field_offset, new_value); } -template +template inline void Object::SetFieldShort(MemberOffset field_offset, int16_t new_value) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } + VerifyTransaction(); if (kTransactionActive) { - Runtime::Current()->RecordWriteFieldChar(this, field_offset, - GetFieldShort(field_offset), - kIsVolatile); - } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); + Runtime::Current()->RecordWriteFieldChar(this, + field_offset, + GetFieldShort(field_offset), + kIsVolatile); } + Verify(); SetField(field_offset, new_value); } @@ -532,20 +488,19 @@ inline void Object::SetFieldShortVolatile(MemberOffset field_offset, int16_t new field_offset, new_value); } -template +template inline void Object::SetField32(MemberOffset field_offset, int32_t new_value) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } + VerifyTransaction(); if (kTransactionActive) { - Runtime::Current()->RecordWriteField32(this, field_offset, + Runtime::Current()->RecordWriteField32(this, + field_offset, GetField32(field_offset), kIsVolatile); } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } + Verify(); SetField(field_offset, new_value); } @@ -563,94 +518,19 @@ inline void Object::SetField32Transaction(MemberOffset field_offset, int32_t new } } -// TODO: Pass memory_order_ and strong/weak as arguments to avoid code duplication? - -template -inline bool Object::CasFieldWeakSequentiallyConsistent32(MemberOffset field_offset, - int32_t old_value, int32_t new_value) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } - if (kTransactionActive) { - Runtime::Current()->RecordWriteField32(this, field_offset, old_value, true); - } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } - uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); - AtomicInteger* atomic_addr = reinterpret_cast(raw_addr); - - return atomic_addr->CompareAndSetWeakSequentiallyConsistent(old_value, new_value); -} - -template -inline bool Object::CasFieldWeakAcquire32(MemberOffset field_offset, - int32_t old_value, int32_t new_value) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } - if (kTransactionActive) { - Runtime::Current()->RecordWriteField32(this, field_offset, old_value, true); - } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } - uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); - AtomicInteger* atomic_addr = reinterpret_cast(raw_addr); - - return atomic_addr->CompareAndSetWeakAcquire(old_value, new_value); -} - -template -inline bool Object::CasFieldWeakRelease32(MemberOffset field_offset, - int32_t old_value, int32_t new_value) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } - if (kTransactionActive) { - Runtime::Current()->RecordWriteField32(this, field_offset, old_value, true); - } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } - uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); - AtomicInteger* atomic_addr = reinterpret_cast(raw_addr); - - return atomic_addr->CompareAndSetWeakRelease(old_value, new_value); -} - -template -inline bool Object::CasFieldStrongSequentiallyConsistent32(MemberOffset field_offset, - int32_t old_value, int32_t new_value) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } - if (kTransactionActive) { - Runtime::Current()->RecordWriteField32(this, field_offset, old_value, true); - } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } - uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); - AtomicInteger* atomic_addr = reinterpret_cast(raw_addr); - - return atomic_addr->CompareAndSetStrongSequentiallyConsistent(old_value, new_value); -} - -template +template inline void Object::SetField64(MemberOffset field_offset, int64_t new_value) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } + VerifyTransaction(); if (kTransactionActive) { - Runtime::Current()->RecordWriteField64(this, field_offset, + Runtime::Current()->RecordWriteField64(this, + field_offset, GetField64(field_offset), kIsVolatile); } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } + Verify(); SetField(field_offset, new_value); } @@ -673,21 +553,18 @@ template inline kSize Object::GetFieldAcquire(MemberOffset field_offset) { const uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); const kSize* addr = reinterpret_cast(raw_addr); - return reinterpret_cast*>(addr)->LoadAcquire(); + return reinterpret_cast*>(addr)->load(std::memory_order_acquire); } template inline bool Object::CasFieldWeakSequentiallyConsistent64(MemberOffset field_offset, - int64_t old_value, int64_t new_value) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } + int64_t old_value, + int64_t new_value) { + VerifyTransaction(); if (kTransactionActive) { Runtime::Current()->RecordWriteField64(this, field_offset, old_value, true); } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } + Verify(); uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); Atomic* atomic_addr = reinterpret_cast*>(raw_addr); return atomic_addr->CompareAndSetWeakSequentiallyConsistent(old_value, new_value); @@ -695,36 +572,31 @@ inline bool Object::CasFieldWeakSequentiallyConsistent64(MemberOffset field_offs template inline bool Object::CasFieldStrongSequentiallyConsistent64(MemberOffset field_offset, - int64_t old_value, int64_t new_value) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } + int64_t old_value, + int64_t new_value) { + VerifyTransaction(); if (kTransactionActive) { Runtime::Current()->RecordWriteField64(this, field_offset, old_value, true); } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } + Verify(); uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); Atomic* atomic_addr = reinterpret_cast*>(raw_addr); return atomic_addr->CompareAndSetStrongSequentiallyConsistent(old_value, new_value); } -template inline T* Object::GetFieldObject(MemberOffset field_offset) { - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } + Verify(); uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); HeapReference* objref_addr = reinterpret_cast*>(raw_addr); T* result = ReadBarrier::Barrier( this, field_offset, objref_addr); - if (kVerifyFlags & kVerifyReads) { - VerifyObject(result); - } + VerifyRead(result); return result; } @@ -733,13 +605,13 @@ inline T* Object::GetFieldObjectVolatile(MemberOffset field_offset) { return GetFieldObject(field_offset); } -template +template inline void Object::SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, ObjPtr new_value) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } + VerifyTransaction(); if (kTransactionActive) { ObjPtr obj; if (kIsVolatile) { @@ -747,26 +619,24 @@ inline void Object::SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, } else { obj = GetFieldObject(field_offset); } - Runtime::Current()->RecordWriteFieldReference(this, field_offset, obj.Ptr(), true); - } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } - if (kVerifyFlags & kVerifyWrites) { - VerifyObject(new_value); + Runtime::Current()->RecordWriteFieldReference(this, field_offset, obj, true); } + Verify(); + VerifyWrite(new_value); uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); HeapReference* objref_addr = reinterpret_cast*>(raw_addr); objref_addr->Assign(new_value.Ptr()); } -template +template inline void Object::SetFieldObject(MemberOffset field_offset, ObjPtr new_value) { SetFieldObjectWithoutWriteBarrier(field_offset, new_value); if (new_value != nullptr) { - Runtime::Current()->GetHeap()->WriteBarrierField(this, field_offset, new_value); + WriteBarrier::ForFieldWrite(this, field_offset, new_value); // TODO: Check field assignment could theoretically cause thread suspension, TODO: fix this. CheckFieldAssignment(field_offset, new_value); } @@ -789,42 +659,19 @@ inline void Object::SetFieldObjectTransaction(MemberOffset field_offset, ObjPtr< template inline HeapReference* Object::GetFieldObjectReferenceAddr(MemberOffset field_offset) { - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } + Verify(); return reinterpret_cast*>(reinterpret_cast(this) + field_offset.Int32Value()); } template -inline bool Object::CasFieldWeakSequentiallyConsistentObject(MemberOffset field_offset, - ObjPtr old_value, - ObjPtr new_value) { - bool success = CasFieldWeakSequentiallyConsistentObjectWithoutWriteBarrier< - kTransactionActive, kCheckTransaction, kVerifyFlags>(field_offset, old_value, new_value); - if (success) { - Runtime::Current()->GetHeap()->WriteBarrierField(this, field_offset, new_value); - } - return success; -} - -template -inline bool Object::CasFieldWeakSequentiallyConsistentObjectWithoutWriteBarrier( - MemberOffset field_offset, - ObjPtr old_value, - ObjPtr new_value) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } - if (kVerifyFlags & kVerifyWrites) { - VerifyObject(new_value); - } - if (kVerifyFlags & kVerifyReads) { - VerifyObject(old_value); - } +inline bool Object::CasFieldObjectWithoutWriteBarrier(MemberOffset field_offset, + ObjPtr old_value, + ObjPtr new_value, + CASMode mode, + std::memory_order memory_order) { + VerifyTransaction(); + VerifyCAS(new_value, old_value); if (kTransactionActive) { Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true); } @@ -832,107 +679,24 @@ inline bool Object::CasFieldWeakSequentiallyConsistentObjectWithoutWriteBarrier( uint32_t new_ref(PtrCompression::Compress(new_value)); uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); Atomic* atomic_addr = reinterpret_cast*>(raw_addr); - - bool success = atomic_addr->CompareAndSetWeakSequentiallyConsistent(old_ref, new_ref); - return success; + return atomic_addr->CompareAndSet(old_ref, new_ref, mode, memory_order); } template -inline bool Object::CasFieldStrongSequentiallyConsistentObject(MemberOffset field_offset, - ObjPtr old_value, - ObjPtr new_value) { - bool success = CasFieldStrongSequentiallyConsistentObjectWithoutWriteBarrier< - kTransactionActive, kCheckTransaction, kVerifyFlags>(field_offset, old_value, new_value); +inline bool Object::CasFieldObject(MemberOffset field_offset, + ObjPtr old_value, + ObjPtr new_value, + CASMode mode, + std::memory_order memory_order) { + bool success = CasFieldObjectWithoutWriteBarrier< + kTransactionActive, kCheckTransaction, kVerifyFlags>(field_offset, + old_value, + new_value, + mode, + memory_order); if (success) { - Runtime::Current()->GetHeap()->WriteBarrierField(this, field_offset, new_value); - } - return success; -} - -template -inline bool Object::CasFieldStrongSequentiallyConsistentObjectWithoutWriteBarrier( - MemberOffset field_offset, - ObjPtr old_value, - ObjPtr new_value) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } - if (kVerifyFlags & kVerifyWrites) { - VerifyObject(new_value); - } - if (kVerifyFlags & kVerifyReads) { - VerifyObject(old_value); - } - if (kTransactionActive) { - Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true); - } - uint32_t old_ref(PtrCompression::Compress(old_value)); - uint32_t new_ref(PtrCompression::Compress(new_value)); - uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); - Atomic* atomic_addr = reinterpret_cast*>(raw_addr); - - bool success = atomic_addr->CompareAndSetStrongSequentiallyConsistent(old_ref, new_ref); - return success; -} - -template -inline bool Object::CasFieldWeakRelaxedObjectWithoutWriteBarrier( - MemberOffset field_offset, - ObjPtr old_value, - ObjPtr new_value) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } - if (kVerifyFlags & kVerifyWrites) { - VerifyObject(new_value); - } - if (kVerifyFlags & kVerifyReads) { - VerifyObject(old_value); - } - if (kTransactionActive) { - Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true); - } - uint32_t old_ref(PtrCompression::Compress(old_value)); - uint32_t new_ref(PtrCompression::Compress(new_value)); - uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); - Atomic* atomic_addr = reinterpret_cast*>(raw_addr); - - bool success = atomic_addr->CompareAndSetWeakRelaxed(old_ref, new_ref); - return success; -} - -template -inline bool Object::CasFieldWeakReleaseObjectWithoutWriteBarrier( - MemberOffset field_offset, - ObjPtr old_value, - ObjPtr new_value) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } - if (kVerifyFlags & kVerifyWrites) { - VerifyObject(new_value); - } - if (kVerifyFlags & kVerifyReads) { - VerifyObject(old_value); - } - if (kTransactionActive) { - Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true); + WriteBarrier::ForFieldWrite(this, field_offset, new_value); } - uint32_t old_ref(PtrCompression::Compress(old_value)); - uint32_t new_ref(PtrCompression::Compress(new_value)); - uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); - Atomic* atomic_addr = reinterpret_cast*>(raw_addr); - - bool success = atomic_addr->CompareAndSetWeakRelease(old_ref, new_ref); return success; } @@ -940,53 +704,38 @@ template Object::CompareAndExchangeFieldObject(MemberOffset field_offset, ObjPtr old_value, ObjPtr new_value) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } - if (kVerifyFlags & kVerifyWrites) { - VerifyObject(new_value); - } - if (kVerifyFlags & kVerifyReads) { - VerifyObject(old_value); - } + VerifyTransaction(); + VerifyCAS(new_value, old_value); uint32_t old_ref(PtrCompression::Compress(old_value)); uint32_t new_ref(PtrCompression::Compress(new_value)); uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); Atomic* atomic_addr = reinterpret_cast*>(raw_addr); - bool success = atomic_addr->CompareAndExchangeStrongSequentiallyConsistent(&old_ref, new_ref); + bool success = atomic_addr->compare_exchange_strong(old_ref, new_ref, std::memory_order_seq_cst); ObjPtr witness_value(PtrCompression::Decompress(old_ref)); if (kIsDebugBuild) { // Ensure caller has done read barrier on the reference field so it's in the to-space. ReadBarrier::AssertToSpaceInvariant(witness_value.Ptr()); } - if (kTransactionActive && success) { - Runtime::Current()->RecordWriteFieldReference(this, field_offset, witness_value, true); - } - if (kVerifyFlags & kVerifyReads) { - VerifyObject(witness_value); + if (success) { + if (kTransactionActive) { + Runtime::Current()->RecordWriteFieldReference(this, field_offset, witness_value, true); + } + WriteBarrier::ForFieldWrite(this, field_offset, new_value); } + VerifyRead(witness_value); return witness_value; } template inline ObjPtr Object::ExchangeFieldObject(MemberOffset field_offset, ObjPtr new_value) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } - if (kVerifyFlags & kVerifyWrites) { - VerifyObject(new_value); - } + VerifyTransaction(); + VerifyCAS(new_value, /*old_value*/ nullptr); + uint32_t new_ref(PtrCompression::Compress(new_value)); uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); Atomic* atomic_addr = reinterpret_cast*>(raw_addr); - uint32_t old_ref = atomic_addr->ExchangeSequentiallyConsistent(new_ref); + uint32_t old_ref = atomic_addr->exchange(new_ref, std::memory_order_seq_cst); ObjPtr old_value(PtrCompression::Decompress(old_ref)); if (kIsDebugBuild) { // Ensure caller has done read barrier on the reference field so it's in the to-space. @@ -995,17 +744,14 @@ inline ObjPtr Object::ExchangeFieldObject(MemberOffset field_offset, if (kTransactionActive) { Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true); } - if (kVerifyFlags & kVerifyReads) { - VerifyObject(old_value); - } + WriteBarrier::ForFieldWrite(this, field_offset, new_value); + VerifyRead(old_value); return old_value; } template inline void Object::GetPrimitiveFieldViaAccessor(MemberOffset field_offset, Accessor* accessor) { - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } + Verify(); uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); T* addr = reinterpret_cast(raw_addr); accessor->Access(addr); @@ -1014,17 +760,13 @@ inline void Object::GetPrimitiveFieldViaAccessor(MemberOffset field_offset, Acce template inline void Object::UpdateFieldBooleanViaAccessor(MemberOffset field_offset, Accessor* accessor) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } + VerifyTransaction(); if (kTransactionActive) { static const bool kIsVolatile = true; uint8_t old_value = GetFieldBoolean(field_offset); Runtime::Current()->RecordWriteFieldBoolean(this, field_offset, old_value, kIsVolatile); } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } + Verify(); uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); uint8_t* addr = raw_addr; accessor->Access(addr); @@ -1033,17 +775,13 @@ inline void Object::UpdateFieldBooleanViaAccessor(MemberOffset field_offset, template inline void Object::UpdateFieldByteViaAccessor(MemberOffset field_offset, Accessor* accessor) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } + VerifyTransaction(); if (kTransactionActive) { static const bool kIsVolatile = true; int8_t old_value = GetFieldByte(field_offset); Runtime::Current()->RecordWriteFieldByte(this, field_offset, old_value, kIsVolatile); } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } + Verify(); uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); int8_t* addr = reinterpret_cast(raw_addr); accessor->Access(addr); @@ -1052,17 +790,13 @@ inline void Object::UpdateFieldByteViaAccessor(MemberOffset field_offset, template inline void Object::UpdateFieldCharViaAccessor(MemberOffset field_offset, Accessor* accessor) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } + VerifyTransaction(); if (kTransactionActive) { static const bool kIsVolatile = true; uint16_t old_value = GetFieldChar(field_offset); Runtime::Current()->RecordWriteFieldChar(this, field_offset, old_value, kIsVolatile); } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } + Verify(); uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); uint16_t* addr = reinterpret_cast(raw_addr); accessor->Access(addr); @@ -1071,17 +805,13 @@ inline void Object::UpdateFieldCharViaAccessor(MemberOffset field_offset, template inline void Object::UpdateFieldShortViaAccessor(MemberOffset field_offset, Accessor* accessor) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } + VerifyTransaction(); if (kTransactionActive) { static const bool kIsVolatile = true; int16_t old_value = GetFieldShort(field_offset); Runtime::Current()->RecordWriteFieldShort(this, field_offset, old_value, kIsVolatile); } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } + Verify(); uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); int16_t* addr = reinterpret_cast(raw_addr); accessor->Access(addr); @@ -1090,17 +820,13 @@ inline void Object::UpdateFieldShortViaAccessor(MemberOffset field_offset, template inline void Object::UpdateField32ViaAccessor(MemberOffset field_offset, Accessor* accessor) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } + VerifyTransaction(); if (kTransactionActive) { static const bool kIsVolatile = true; int32_t old_value = GetField32(field_offset); Runtime::Current()->RecordWriteField32(this, field_offset, old_value, kIsVolatile); } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } + Verify(); uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); int32_t* addr = reinterpret_cast(raw_addr); accessor->Access(addr); @@ -1109,17 +835,13 @@ inline void Object::UpdateField32ViaAccessor(MemberOffset field_offset, template inline void Object::UpdateField64ViaAccessor(MemberOffset field_offset, Accessor* accessor) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } + VerifyTransaction(); if (kTransactionActive) { static const bool kIsVolatile = true; int64_t old_value = GetField64(field_offset); Runtime::Current()->RecordWriteField64(this, field_offset, old_value, kIsVolatile); } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } + Verify(); uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); int64_t* addr = reinterpret_cast(raw_addr); accessor->Access(addr); @@ -1206,6 +928,13 @@ inline mirror::DexCache* Object::AsDexCache() { return down_cast(this); } +template +inline void Object::VerifyTransaction() { + if (kCheckTransaction) { + DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); + } +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/object-readbarrier-inl.h b/runtime/mirror/object-readbarrier-inl.h index aeaa850abed787cb7de63f488ac2bf5bb85e4f65..df50d0613a8914b435b64423d0b2ef1f35e3e318 100644 --- a/runtime/mirror/object-readbarrier-inl.h +++ b/runtime/mirror/object-readbarrier-inl.h @@ -32,14 +32,17 @@ namespace mirror { template inline LockWord Object::GetLockWord(bool as_volatile) { if (as_volatile) { - return LockWord(GetField32Volatile(OFFSET_OF_OBJECT_MEMBER(Object, monitor_))); + return LockWord(GetField32Volatile(MonitorOffset())); } - return LockWord(GetField32(OFFSET_OF_OBJECT_MEMBER(Object, monitor_))); + return LockWord(GetField32(MonitorOffset())); } template -inline bool Object::CasFieldWeakRelaxed32(MemberOffset field_offset, - int32_t old_value, int32_t new_value) { +inline bool Object::CasField32(MemberOffset field_offset, + int32_t old_value, + int32_t new_value, + CASMode mode, + std::memory_order memory_order) { if (kCheckTransaction) { DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); } @@ -52,19 +55,19 @@ inline bool Object::CasFieldWeakRelaxed32(MemberOffset field_offset, uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); AtomicInteger* atomic_addr = reinterpret_cast(raw_addr); - return atomic_addr->CompareAndSetWeakRelaxed(old_value, new_value); + return atomic_addr->CompareAndSet(old_value, new_value, mode, memory_order); } -inline bool Object::CasLockWordWeakRelaxed(LockWord old_val, LockWord new_val) { +inline bool Object::CasLockWord(LockWord old_val, + LockWord new_val, + CASMode mode, + std::memory_order memory_order) { // Force use of non-transactional mode and do not check. - return CasFieldWeakRelaxed32( - OFFSET_OF_OBJECT_MEMBER(Object, monitor_), old_val.GetValue(), new_val.GetValue()); -} - -inline bool Object::CasLockWordWeakRelease(LockWord old_val, LockWord new_val) { - // Force use of non-transactional mode and do not check. - return CasFieldWeakRelease32( - OFFSET_OF_OBJECT_MEMBER(Object, monitor_), old_val.GetValue(), new_val.GetValue()); + return CasField32(MonitorOffset(), + old_val.GetValue(), + new_val.GetValue(), + mode, + memory_order); } inline uint32_t Object::GetReadBarrierState(uintptr_t* fake_address_dependency) { @@ -128,7 +131,7 @@ inline uint32_t Object::GetReadBarrierState() { UNREACHABLE(); } DCHECK(kUseBakerReadBarrier); - LockWord lw(GetField(OFFSET_OF_OBJECT_MEMBER(Object, monitor_))); + LockWord lw(GetField(MonitorOffset())); uint32_t rb_state = lw.ReadBarrierState(); DCHECK(ReadBarrier::IsValidReadBarrierState(rb_state)) << rb_state; return rb_state; @@ -139,13 +142,13 @@ inline uint32_t Object::GetReadBarrierStateAcquire() { LOG(FATAL) << "Unreachable"; UNREACHABLE(); } - LockWord lw(GetFieldAcquire(OFFSET_OF_OBJECT_MEMBER(Object, monitor_))); + LockWord lw(GetFieldAcquire(MonitorOffset())); uint32_t rb_state = lw.ReadBarrierState(); DCHECK(ReadBarrier::IsValidReadBarrierState(rb_state)) << rb_state; return rb_state; } -template +template inline bool Object::AtomicSetReadBarrierState(uint32_t expected_rb_state, uint32_t rb_state) { if (!kUseBakerReadBarrier) { LOG(FATAL) << "Unreachable"; @@ -169,9 +172,7 @@ inline bool Object::AtomicSetReadBarrierState(uint32_t expected_rb_state, uint32 // If kCasRelease == true, use a CAS release so that when GC updates all the fields of // an object and then changes the object from gray to black, the field updates (stores) will be // visible (won't be reordered after this CAS.) - } while (!(kCasRelease ? - CasLockWordWeakRelease(expected_lw, new_lw) : - CasLockWordWeakRelaxed(expected_lw, new_lw))); + } while (!CasLockWord(expected_lw, new_lw, CASMode::kWeak, kMemoryOrder)); return true; } @@ -188,68 +189,10 @@ inline bool Object::AtomicSetMarkBit(uint32_t expected_mark_bit, uint32_t mark_b new_lw = lw; new_lw.SetMarkBitState(mark_bit); // Since this is only set from the mutator, we can use the non-release CAS. - } while (!CasLockWordWeakRelaxed(expected_lw, new_lw)); + } while (!CasLockWord(expected_lw, new_lw, CASMode::kWeak, std::memory_order_relaxed)); return true; } -template -inline bool Object::CasFieldStrongRelaxedObjectWithoutWriteBarrier( - MemberOffset field_offset, - ObjPtr old_value, - ObjPtr new_value) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } - if (kVerifyFlags & kVerifyWrites) { - VerifyObject(new_value); - } - if (kVerifyFlags & kVerifyReads) { - VerifyObject(old_value); - } - if (kTransactionActive) { - Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true); - } - uint32_t old_ref(PtrCompression::Compress(old_value)); - uint32_t new_ref(PtrCompression::Compress(new_value)); - uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); - Atomic* atomic_addr = reinterpret_cast*>(raw_addr); - - bool success = atomic_addr->CompareAndSetStrongRelaxed(old_ref, new_ref); - return success; -} - -template -inline bool Object::CasFieldStrongReleaseObjectWithoutWriteBarrier( - MemberOffset field_offset, - ObjPtr old_value, - ObjPtr new_value) { - if (kCheckTransaction) { - DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); - } - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } - if (kVerifyFlags & kVerifyWrites) { - VerifyObject(new_value); - } - if (kVerifyFlags & kVerifyReads) { - VerifyObject(old_value); - } - if (kTransactionActive) { - Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true); - } - uint32_t old_ref(PtrCompression::Compress(old_value)); - uint32_t new_ref(PtrCompression::Compress(new_value)); - uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); - Atomic* atomic_addr = reinterpret_cast*>(raw_addr); - - bool success = atomic_addr->CompareAndSetStrongRelease(old_ref, new_ref); - return success; -} - } // namespace mirror } // namespace art diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index f274cfc2fa69ee890ba4b6188c407b94b4583a76..025c10bc2a4b85699f427165043cf679c14a3d66 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -87,16 +87,18 @@ Object* Object::CopyObject(ObjPtr dest, DCHECK_ALIGNED(dst_bytes, sizeof(uintptr_t)); // Use word sized copies to begin. while (num_bytes >= sizeof(uintptr_t)) { - reinterpret_cast*>(dst_bytes)->StoreRelaxed( - reinterpret_cast*>(src_bytes)->LoadRelaxed()); + reinterpret_cast*>(dst_bytes)->store( + reinterpret_cast*>(src_bytes)->load(std::memory_order_relaxed), + std::memory_order_relaxed); src_bytes += sizeof(uintptr_t); dst_bytes += sizeof(uintptr_t); num_bytes -= sizeof(uintptr_t); } // Copy possible 32 bit word. if (sizeof(uintptr_t) != sizeof(uint32_t) && num_bytes >= sizeof(uint32_t)) { - reinterpret_cast*>(dst_bytes)->StoreRelaxed( - reinterpret_cast*>(src_bytes)->LoadRelaxed()); + reinterpret_cast*>(dst_bytes)->store( + reinterpret_cast*>(src_bytes)->load(std::memory_order_relaxed), + std::memory_order_relaxed); src_bytes += sizeof(uint32_t); dst_bytes += sizeof(uint32_t); num_bytes -= sizeof(uint32_t); @@ -104,8 +106,9 @@ Object* Object::CopyObject(ObjPtr dest, // Copy remaining bytes, avoid going past the end of num_bytes since there may be a redzone // there. while (num_bytes > 0) { - reinterpret_cast*>(dst_bytes)->StoreRelaxed( - reinterpret_cast*>(src_bytes)->LoadRelaxed()); + reinterpret_cast*>(dst_bytes)->store( + reinterpret_cast*>(src_bytes)->load(std::memory_order_relaxed), + std::memory_order_relaxed); src_bytes += sizeof(uint8_t); dst_bytes += sizeof(uint8_t); num_bytes -= sizeof(uint8_t); @@ -118,16 +121,15 @@ Object* Object::CopyObject(ObjPtr dest, CopyReferenceFieldsWithReadBarrierVisitor visitor(dest); src->VisitReferences(visitor, visitor); } - gc::Heap* heap = Runtime::Current()->GetHeap(); // Perform write barriers on copied object references. ObjPtr c = src->GetClass(); if (c->IsArrayClass()) { if (!c->GetComponentType()->IsPrimitive()) { ObjectArray* array = dest->AsObjectArray(); - heap->WriteBarrierArray(dest, 0, array->GetLength()); + WriteBarrier::ForArrayWrite(dest, 0, array->GetLength()); } } else { - heap->WriteBarrierEveryFieldOf(dest); + WriteBarrier::ForEveryFieldWrite(dest); } return dest.Ptr(); } @@ -173,7 +175,7 @@ Object* Object::Clone(Thread* self) { uint32_t Object::GenerateIdentityHashCode() { uint32_t expected_value, new_value; do { - expected_value = hash_code_seed.LoadRelaxed(); + expected_value = hash_code_seed.load(std::memory_order_relaxed); new_value = expected_value * 1103515245 + 12345; } while (!hash_code_seed.CompareAndSetWeakRelaxed(expected_value, new_value) || (expected_value & LockWord::kHashMask) == 0); @@ -181,7 +183,7 @@ uint32_t Object::GenerateIdentityHashCode() { } void Object::SetHashCodeSeed(uint32_t new_seed) { - hash_code_seed.StoreRelaxed(new_seed); + hash_code_seed.store(new_seed, std::memory_order_relaxed); } int32_t Object::IdentityHashCode() { @@ -194,7 +196,9 @@ int32_t Object::IdentityHashCode() { // loop iteration. LockWord hash_word = LockWord::FromHashCode(GenerateIdentityHashCode(), lw.GCState()); DCHECK_EQ(hash_word.GetState(), LockWord::kHashCode); - if (current_this->CasLockWordWeakRelaxed(lw, hash_word)) { + // Use a strong CAS to prevent spurious failures since these can make the boot image + // non-deterministic. + if (current_this->CasLockWord(lw, hash_word, CASMode::kStrong, std::memory_order_relaxed)) { return hash_word.GetHashCode(); } break; @@ -268,7 +272,7 @@ void Object::CheckFieldAssignmentImpl(MemberOffset field_offset, ObjPtr } } LOG(FATAL) << "Failed to find field for assignment to " << reinterpret_cast(this) - << " of type " << c->PrettyDescriptor() << " at offset " << field_offset; + << " of type " << c->PrettyDescriptor() << " at offset " << field_offset; UNREACHABLE(); } @@ -278,10 +282,7 @@ ArtField* Object::FindFieldByOffset(MemberOffset offset) { } std::string Object::PrettyTypeOf(ObjPtr obj) { - if (obj == nullptr) { - return "null"; - } - return obj->PrettyTypeOf(); + return (obj == nullptr) ? "null" : obj->PrettyTypeOf(); } std::string Object::PrettyTypeOf() { diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index 95f82cb1475d73c1f4944eed5a94e2fcc496fbb0..c7cffed69b79dc14dc8e2fc042c697b16b047e53 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -20,7 +20,7 @@ #include "base/atomic.h" #include "base/casts.h" #include "base/enums.h" -#include "globals.h" +#include "base/globals.h" #include "obj_ptr.h" #include "object_reference.h" #include "offsets.h" @@ -106,12 +106,9 @@ class MANAGED LOCKABLE Object { // Get the read barrier state with a load-acquire. ALWAYS_INLINE uint32_t GetReadBarrierStateAcquire() REQUIRES_SHARED(Locks::mutator_lock_); -#ifndef USE_BAKER_OR_BROOKS_READ_BARRIER - NO_RETURN -#endif ALWAYS_INLINE void SetReadBarrierState(uint32_t rb_state) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE bool AtomicSetReadBarrierState(uint32_t expected_rb_state, uint32_t rb_state) REQUIRES_SHARED(Locks::mutator_lock_); @@ -151,13 +148,7 @@ class MANAGED LOCKABLE Object { LockWord GetLockWord(bool as_volatile) REQUIRES_SHARED(Locks::mutator_lock_); template void SetLockWord(LockWord new_val, bool as_volatile) REQUIRES_SHARED(Locks::mutator_lock_); - bool CasLockWordWeakSequentiallyConsistent(LockWord old_val, LockWord new_val) - REQUIRES_SHARED(Locks::mutator_lock_); - bool CasLockWordWeakRelaxed(LockWord old_val, LockWord new_val) - REQUIRES_SHARED(Locks::mutator_lock_); - bool CasLockWordWeakAcquire(LockWord old_val, LockWord new_val) - REQUIRES_SHARED(Locks::mutator_lock_); - bool CasLockWordWeakRelease(LockWord old_val, LockWord new_val) + bool CasLockWord(LockWord old_val, LockWord new_val, CASMode mode, std::memory_order memory_order) REQUIRES_SHARED(Locks::mutator_lock_); uint32_t GetLockOwnerThreadId(); @@ -176,7 +167,6 @@ class MANAGED LOCKABLE Object { UNLOCK_FUNCTION(); void Notify(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); void NotifyAll(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); - void Wait(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); void Wait(Thread* self, int64_t timeout, int32_t nanos) REQUIRES_SHARED(Locks::mutator_lock_); template + template ALWAYS_INLINE T* GetFieldObject(MemberOffset field_offset) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE T* GetFieldObjectVolatile(MemberOffset field_offset) REQUIRES_SHARED(Locks::mutator_lock_); @@ -310,11 +303,11 @@ class MANAGED LOCKABLE Object { template - ALWAYS_INLINE void SetFieldObjectVolatile(MemberOffset field_offset, - ObjPtr new_value) + ALWAYS_INLINE void SetFieldObjectVolatile(MemberOffset field_offset, ObjPtr new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template ALWAYS_INLINE void SetFieldObjectTransaction(MemberOffset field_offset, ObjPtr new_value) REQUIRES_SHARED(Locks::mutator_lock_); @@ -322,30 +315,20 @@ class MANAGED LOCKABLE Object { template - bool CasFieldWeakSequentiallyConsistentObject(MemberOffset field_offset, - ObjPtr old_value, - ObjPtr new_value) - REQUIRES_SHARED(Locks::mutator_lock_); - template - bool CasFieldWeakSequentiallyConsistentObjectWithoutWriteBarrier(MemberOffset field_offset, - ObjPtr old_value, - ObjPtr new_value) - REQUIRES_SHARED(Locks::mutator_lock_); - template - bool CasFieldStrongSequentiallyConsistentObject(MemberOffset field_offset, - ObjPtr old_value, - ObjPtr new_value) + ALWAYS_INLINE bool CasFieldObject(MemberOffset field_offset, + ObjPtr old_value, + ObjPtr new_value, + CASMode mode, + std::memory_order memory_order) REQUIRES_SHARED(Locks::mutator_lock_); template - bool CasFieldStrongSequentiallyConsistentObjectWithoutWriteBarrier(MemberOffset field_offset, - ObjPtr old_value, - ObjPtr new_value) + ALWAYS_INLINE bool CasFieldObjectWithoutWriteBarrier(MemberOffset field_offset, + ObjPtr old_value, + ObjPtr new_value, + CASMode mode, + std::memory_order memory_order) REQUIRES_SHARED(Locks::mutator_lock_); template ExchangeFieldObject(MemberOffset field_offset, ObjPtr new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template - bool CasFieldWeakRelaxedObjectWithoutWriteBarrier(MemberOffset field_offset, - ObjPtr old_value, - ObjPtr new_value) - REQUIRES_SHARED(Locks::mutator_lock_); - template - bool CasFieldWeakReleaseObjectWithoutWriteBarrier(MemberOffset field_offset, - ObjPtr old_value, - ObjPtr new_value) - REQUIRES_SHARED(Locks::mutator_lock_); - - template - bool CasFieldStrongRelaxedObjectWithoutWriteBarrier(MemberOffset field_offset, - ObjPtr old_value, - ObjPtr new_value) - REQUIRES_SHARED(Locks::mutator_lock_); - template - bool CasFieldStrongReleaseObjectWithoutWriteBarrier(MemberOffset field_offset, - ObjPtr old_value, - ObjPtr new_value) - REQUIRES_SHARED(Locks::mutator_lock_); - template - HeapReference* GetFieldObjectReferenceAddr(MemberOffset field_offset); + HeapReference* GetFieldObjectReferenceAddr(MemberOffset field_offset) + REQUIRES_SHARED(Locks::mutator_lock_); template ALWAYS_INLINE uint8_t GetFieldBoolean(MemberOffset field_offset) REQUIRES_SHARED(Locks::mutator_lock_) { - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } + Verify(); return GetField(field_offset); } @@ -416,23 +368,29 @@ class MANAGED LOCKABLE Object { ALWAYS_INLINE int8_t GetFieldByteVolatile(MemberOffset field_offset) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE void SetFieldBoolean(MemberOffset field_offset, uint8_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE void SetFieldByte(MemberOffset field_offset, int8_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE void SetFieldBooleanVolatile(MemberOffset field_offset, uint8_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE void SetFieldByteVolatile(MemberOffset field_offset, int8_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); @@ -452,32 +410,36 @@ class MANAGED LOCKABLE Object { ALWAYS_INLINE int16_t GetFieldShortVolatile(MemberOffset field_offset) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE void SetFieldChar(MemberOffset field_offset, uint16_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE void SetFieldShort(MemberOffset field_offset, int16_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE void SetFieldCharVolatile(MemberOffset field_offset, uint16_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE void SetFieldShortVolatile(MemberOffset field_offset, int16_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); template ALWAYS_INLINE int32_t GetField32(MemberOffset field_offset) REQUIRES_SHARED(Locks::mutator_lock_) { - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } + Verify(); return GetField(field_offset); } @@ -487,13 +449,16 @@ class MANAGED LOCKABLE Object { return GetField32(field_offset); } - template + template ALWAYS_INLINE void SetField32(MemberOffset field_offset, int32_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE void SetField32Volatile(MemberOffset field_offset, int32_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); @@ -503,42 +468,20 @@ class MANAGED LOCKABLE Object { ALWAYS_INLINE void SetField32Transaction(MemberOffset field_offset, int32_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template - ALWAYS_INLINE bool CasFieldWeakSequentiallyConsistent32(MemberOffset field_offset, - int32_t old_value, int32_t new_value) - REQUIRES_SHARED(Locks::mutator_lock_); - - template - bool CasFieldWeakRelaxed32(MemberOffset field_offset, int32_t old_value, - int32_t new_value) ALWAYS_INLINE - REQUIRES_SHARED(Locks::mutator_lock_); - - template - bool CasFieldWeakAcquire32(MemberOffset field_offset, int32_t old_value, - int32_t new_value) ALWAYS_INLINE - REQUIRES_SHARED(Locks::mutator_lock_); - - template - bool CasFieldWeakRelease32(MemberOffset field_offset, int32_t old_value, - int32_t new_value) ALWAYS_INLINE - REQUIRES_SHARED(Locks::mutator_lock_); - - template - bool CasFieldStrongSequentiallyConsistent32(MemberOffset field_offset, int32_t old_value, - int32_t new_value) ALWAYS_INLINE + template + ALWAYS_INLINE bool CasField32(MemberOffset field_offset, + int32_t old_value, + int32_t new_value, + CASMode mode, + std::memory_order memory_order) REQUIRES_SHARED(Locks::mutator_lock_); template ALWAYS_INLINE int64_t GetField64(MemberOffset field_offset) REQUIRES_SHARED(Locks::mutator_lock_) { - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } + Verify(); return GetField(field_offset); } @@ -548,13 +491,16 @@ class MANAGED LOCKABLE Object { return GetField64(field_offset); } - template + template ALWAYS_INLINE void SetField64(MemberOffset field_offset, int64_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE void SetField64Volatile(MemberOffset field_offset, int64_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); @@ -564,35 +510,45 @@ class MANAGED LOCKABLE Object { ALWAYS_INLINE void SetField64Transaction(MemberOffset field_offset, int32_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template - bool CasFieldWeakSequentiallyConsistent64(MemberOffset field_offset, int64_t old_value, + template + bool CasFieldWeakSequentiallyConsistent64(MemberOffset field_offset, + int64_t old_value, int64_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template - bool CasFieldStrongSequentiallyConsistent64(MemberOffset field_offset, int64_t old_value, + template + bool CasFieldStrongSequentiallyConsistent64(MemberOffset field_offset, + int64_t old_value, int64_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template + template void SetFieldPtr(MemberOffset field_offset, T new_value) REQUIRES_SHARED(Locks::mutator_lock_) { SetFieldPtrWithSize( field_offset, new_value, kRuntimePointerSize); } - template + template void SetFieldPtr64(MemberOffset field_offset, T new_value) REQUIRES_SHARED(Locks::mutator_lock_) { SetFieldPtrWithSize( field_offset, new_value, 8u); } - template + template ALWAYS_INLINE void SetFieldPtrWithSize(MemberOffset field_offset, T new_value, PointerSize pointer_size) @@ -628,28 +584,34 @@ class MANAGED LOCKABLE Object { // Update methods that expose the raw address of a primitive value-type to an Accessor instance // that will attempt to update the field. These are used by VarHandle accessor methods to // atomically update fields with a wider range of memory orderings than usually required. - template + template void UpdateFieldBooleanViaAccessor(MemberOffset field_offset, Accessor* accessor) REQUIRES_SHARED(Locks::mutator_lock_); - template + template void UpdateFieldByteViaAccessor(MemberOffset field_offset, Accessor* accessor) REQUIRES_SHARED(Locks::mutator_lock_); - template + template void UpdateFieldCharViaAccessor(MemberOffset field_offset, Accessor* accessor) REQUIRES_SHARED(Locks::mutator_lock_); - template + template void UpdateFieldShortViaAccessor(MemberOffset field_offset, Accessor* accessor) REQUIRES_SHARED(Locks::mutator_lock_); - template + template void UpdateField32ViaAccessor(MemberOffset field_offset, Accessor* accessor) REQUIRES_SHARED(Locks::mutator_lock_); - template + template void UpdateField64ViaAccessor(MemberOffset field_offset, Accessor* accessor) REQUIRES_SHARED(Locks::mutator_lock_); @@ -689,8 +651,7 @@ class MANAGED LOCKABLE Object { template T GetFieldPtr64(MemberOffset field_offset) REQUIRES_SHARED(Locks::mutator_lock_) { - return GetFieldPtrWithSize(field_offset, - PointerSize::k64); + return GetFieldPtrWithSize(field_offset, PointerSize::k64); } template @@ -730,7 +691,7 @@ class MANAGED LOCKABLE Object { uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); kSize* addr = reinterpret_cast(raw_addr); if (kIsVolatile) { - reinterpret_cast*>(addr)->StoreSequentiallyConsistent(new_value); + reinterpret_cast*>(addr)->store(new_value, std::memory_order_seq_cst); } else { reinterpret_cast*>(addr)->StoreJavaData(new_value); } @@ -742,7 +703,7 @@ class MANAGED LOCKABLE Object { const uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); const kSize* addr = reinterpret_cast(raw_addr); if (kIsVolatile) { - return reinterpret_cast*>(addr)->LoadSequentiallyConsistent(); + return reinterpret_cast*>(addr)->load(std::memory_order_seq_cst); } else { return reinterpret_cast*>(addr)->LoadJavaData(); } @@ -764,6 +725,39 @@ class MANAGED LOCKABLE Object { } } + template + ALWAYS_INLINE void Verify() { + if (kVerifyFlags & kVerifyThis) { + VerifyObject(this); + } + } + + // Not ObjPtr since the values may be unaligned for logic in verification.cc. + template + ALWAYS_INLINE static void VerifyRead(Reference value) { + if (kVerifyFlags & kVerifyReads) { + VerifyObject(value); + } + } + + template + ALWAYS_INLINE static void VerifyWrite(ObjPtr value) { + if (kVerifyFlags & kVerifyWrites) { + VerifyObject(value); + } + } + + template + ALWAYS_INLINE void VerifyCAS(ObjPtr new_value, ObjPtr old_value) { + Verify(); + VerifyRead(old_value); + VerifyWrite(new_value); + } + + // Verify transaction is active (if required). + template + ALWAYS_INLINE void VerifyTransaction(); + // A utility function that copies an object in a read barrier and write barrier-aware way. // This is internally used by Clone() and Class::CopyOf(). If the object is finalizable, // it is the callers job to call Heap::AddFinalizerReference. diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h index 086d2f46723435769aa16383385690773026043b..1d2f47f86ab7cfc914b527975b4b57f0e3122b06 100644 --- a/runtime/mirror/object_array-inl.h +++ b/runtime/mirror/object_array-inl.h @@ -32,19 +32,21 @@ #include "object-inl.h" #include "runtime.h" #include "thread.h" +#include "write_barrier-inl.h" namespace art { namespace mirror { template -inline ObjectArray* ObjectArray::Alloc(Thread* self, - ObjPtr object_array_class, - int32_t length, gc::AllocatorType allocator_type) { - Array* array = Array::Alloc(self, - object_array_class.Ptr(), - length, - ComponentSizeShiftWidth(kHeapReferenceSize), - allocator_type); +inline ObjPtr> ObjectArray::Alloc(Thread* self, + ObjPtr object_array_class, + int32_t length, + gc::AllocatorType allocator_type) { + ObjPtr array = Array::Alloc(self, + object_array_class, + length, + ComponentSizeShiftWidth(kHeapReferenceSize), + allocator_type); if (UNLIKELY(array == nullptr)) { return nullptr; } @@ -54,9 +56,9 @@ inline ObjectArray* ObjectArray::Alloc(Thread* self, } template -inline ObjectArray* ObjectArray::Alloc(Thread* self, - ObjPtr object_array_class, - int32_t length) { +inline ObjPtr> ObjectArray::Alloc(Thread* self, + ObjPtr object_array_class, + int32_t length) { return Alloc(self, object_array_class, length, @@ -196,7 +198,7 @@ inline void ObjectArray::AssignableMemmove(int32_t dst_pos, } } } - Runtime::Current()->GetHeap()->WriteBarrierArray(this, dst_pos, count); + WriteBarrier::ForArrayWrite(this, dst_pos, count); if (kIsDebugBuild) { for (int i = 0; i < count; ++i) { // The get will perform the VerifyObject. @@ -245,7 +247,7 @@ inline void ObjectArray::AssignableMemcpy(int32_t dst_pos, SetWithoutChecksAndWriteBarrier(dst_pos + i, obj); } } - Runtime::Current()->GetHeap()->WriteBarrierArray(this, dst_pos, count); + WriteBarrier::ForArrayWrite(this, dst_pos, count); if (kIsDebugBuild) { for (int i = 0; i < count; ++i) { // The get will perform the VerifyObject. @@ -327,7 +329,7 @@ inline void ObjectArray::AssignableCheckingMemcpy(int32_t dst_pos, } } } - Runtime::Current()->GetHeap()->WriteBarrierArray(this, dst_pos, count); + WriteBarrier::ForArrayWrite(this, dst_pos, count); if (UNLIKELY(i != count)) { std::string actualSrcType(mirror::Object::PrettyTypeOf(o)); std::string dstType(PrettyTypeOf()); @@ -346,7 +348,7 @@ inline void ObjectArray::AssignableCheckingMemcpy(int32_t dst_pos, } template -inline ObjectArray* ObjectArray::CopyOf(Thread* self, int32_t new_length) { +inline ObjPtr> ObjectArray::CopyOf(Thread* self, int32_t new_length) { DCHECK_GE(new_length, 0); // We may get copied by a compacting GC. StackHandleScope<1> hs(self); @@ -354,7 +356,7 @@ inline ObjectArray* ObjectArray::CopyOf(Thread* self, int32_t new_length) gc::Heap* heap = Runtime::Current()->GetHeap(); gc::AllocatorType allocator_type = heap->IsMovableObject(this) ? heap->GetCurrentAllocator() : heap->GetCurrentNonMovingAllocator(); - ObjectArray* new_array = Alloc(self, GetClass(), new_length, allocator_type); + ObjPtr> new_array = Alloc(self, GetClass(), new_length, allocator_type); if (LIKELY(new_array != nullptr)) { new_array->AssignableMemcpy(0, h_this.Get(), 0, std::min(h_this->GetLength(), new_length)); } diff --git a/runtime/mirror/object_array.h b/runtime/mirror/object_array.h index b7a956176f7d3f85e77d4862e32c4c652a815a9b..6506f6ea9a1c0c57b5a702877a73b4d59fd70dd9 100644 --- a/runtime/mirror/object_array.h +++ b/runtime/mirror/object_array.h @@ -31,15 +31,15 @@ class MANAGED ObjectArray: public Array { return Array::ClassSize(pointer_size); } - static ObjectArray* Alloc(Thread* self, - ObjPtr object_array_class, - int32_t length, - gc::AllocatorType allocator_type) + static ObjPtr> Alloc(Thread* self, + ObjPtr object_array_class, + int32_t length, + gc::AllocatorType allocator_type) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - static ObjectArray* Alloc(Thread* self, - ObjPtr object_array_class, - int32_t length) + static ObjPtr> Alloc(Thread* self, + ObjPtr object_array_class, + int32_t length) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); template* CopyOf(Thread* self, int32_t new_length) + ObjPtr> CopyOf(Thread* self, int32_t new_length) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); diff --git a/runtime/mirror/object_reference.h b/runtime/mirror/object_reference.h index cf1f85d23692916533f36200fffc6312ccfc59d7..77154e2cda3545441f3826f56ac97f6d71a4065f 100644 --- a/runtime/mirror/object_reference.h +++ b/runtime/mirror/object_reference.h @@ -18,8 +18,8 @@ #define ART_RUNTIME_MIRROR_OBJECT_REFERENCE_H_ #include "base/atomic.h" +#include "base/globals.h" #include "base/mutex.h" // For Locks::mutator_lock_. -#include "globals.h" #include "heap_poisoning.h" #include "obj_ptr.h" @@ -110,13 +110,13 @@ class MANAGED HeapReference { template MirrorType* AsMirrorPtr() const REQUIRES_SHARED(Locks::mutator_lock_) { return Compression::Decompress( - kIsVolatile ? reference_.LoadSequentiallyConsistent() : reference_.LoadJavaData()); + kIsVolatile ? reference_.load(std::memory_order_seq_cst) : reference_.LoadJavaData()); } template void Assign(MirrorType* other) REQUIRES_SHARED(Locks::mutator_lock_) { if (kIsVolatile) { - reference_.StoreSequentiallyConsistent(Compression::Compress(other)); + reference_.store(Compression::Compress(other), std::memory_order_seq_cst); } else { reference_.StoreJavaData(Compression::Compress(other)); } diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index 5306eac7f684be25fba5b035f0c534de16d62c22..0b615a6b9aed8045bd92f3085aca7047daf766b5 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -28,6 +28,7 @@ #include "class-inl.h" #include "class_linker-inl.h" #include "class_linker.h" +#include "class_root.h" #include "common_runtime_test.h" #include "dex/dex_file.h" #include "entrypoints/entrypoint_utils-inl.h" @@ -75,10 +76,10 @@ class ObjectTest : public CommonRuntimeTest { } template - mirror::ObjectArray* AllocObjectArray(Thread* self, size_t length) + ObjPtr> AllocObjectArray(Thread* self, size_t length) REQUIRES_SHARED(Locks::mutator_lock_) { return mirror::ObjectArray::Alloc( - self, class_linker_->GetClassRoot(ClassLinker::ClassRoot::kObjectArrayClass), length); + self, GetClassRoot(ClassRoot::kObjectArrayClass, class_linker_), length); } }; @@ -116,7 +117,7 @@ TEST_F(ObjectTest, Clone) { TEST_F(ObjectTest, AllocObjectArray) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); + StackHandleScope<3> hs(soa.Self()); Handle> oa(hs.NewHandle(AllocObjectArray(soa.Self(), 2))); EXPECT_EQ(2, oa->GetLength()); EXPECT_TRUE(oa->Get(0) == nullptr); @@ -128,17 +129,17 @@ TEST_F(ObjectTest, AllocObjectArray) { EXPECT_TRUE(oa->Get(0) == oa.Get()); EXPECT_TRUE(oa->Get(1) == oa.Get()); - Class* aioobe = class_linker_->FindSystemClass(soa.Self(), - "Ljava/lang/ArrayIndexOutOfBoundsException;"); + Handle aioobe = hs.NewHandle( + class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/ArrayIndexOutOfBoundsException;")); EXPECT_TRUE(oa->Get(-1) == nullptr); EXPECT_TRUE(soa.Self()->IsExceptionPending()); - EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass()); + EXPECT_OBJ_PTR_EQ(aioobe.Get(), soa.Self()->GetException()->GetClass()); soa.Self()->ClearException(); EXPECT_TRUE(oa->Get(2) == nullptr); EXPECT_TRUE(soa.Self()->IsExceptionPending()); - EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass()); + EXPECT_OBJ_PTR_EQ(aioobe.Get(), soa.Self()->GetException()->GetClass()); soa.Self()->ClearException(); ASSERT_TRUE(oa->GetClass() != nullptr); @@ -152,53 +153,51 @@ TEST_F(ObjectTest, AllocObjectArray) { TEST_F(ObjectTest, AllocArray) { ScopedObjectAccess soa(Thread::Current()); - Class* c = class_linker_->FindSystemClass(soa.Self(), "[I"); - StackHandleScope<1> hs(soa.Self()); - MutableHandle a( - hs.NewHandle(Array::Alloc(soa.Self(), c, 1, c->GetComponentSizeShift(), - Runtime::Current()->GetHeap()->GetCurrentAllocator()))); - EXPECT_TRUE(c == a->GetClass()); + StackHandleScope<2> hs(soa.Self()); + MutableHandle c = hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[I")); + gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); + MutableHandle a = hs.NewHandle( + Array::Alloc(soa.Self(), c.Get(), 1, c->GetComponentSizeShift(), allocator_type)); + EXPECT_TRUE(c.Get() == a->GetClass()); EXPECT_EQ(1, a->GetLength()); - c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;"); - a.Assign(Array::Alloc(soa.Self(), c, 1, c->GetComponentSizeShift(), - Runtime::Current()->GetHeap()->GetCurrentAllocator())); - EXPECT_TRUE(c == a->GetClass()); + c.Assign(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;")); + a.Assign(Array::Alloc(soa.Self(), c.Get(), 1, c->GetComponentSizeShift(), allocator_type)); + EXPECT_TRUE(c.Get() == a->GetClass()); EXPECT_EQ(1, a->GetLength()); - c = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;"); - a.Assign(Array::Alloc(soa.Self(), c, 1, c->GetComponentSizeShift(), - Runtime::Current()->GetHeap()->GetCurrentAllocator())); - EXPECT_TRUE(c == a->GetClass()); + c.Assign(class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;")); + a.Assign(Array::Alloc(soa.Self(), c.Get(), 1, c->GetComponentSizeShift(), allocator_type)); + EXPECT_TRUE(c.Get() == a->GetClass()); EXPECT_EQ(1, a->GetLength()); } TEST_F(ObjectTest, AllocArray_FillUsable) { ScopedObjectAccess soa(Thread::Current()); - Class* c = class_linker_->FindSystemClass(soa.Self(), "[B"); - StackHandleScope<1> hs(soa.Self()); - MutableHandle a( - hs.NewHandle(Array::Alloc(soa.Self(), c, 1, c->GetComponentSizeShift(), - Runtime::Current()->GetHeap()->GetCurrentAllocator()))); - EXPECT_TRUE(c == a->GetClass()); + StackHandleScope<2> hs(soa.Self()); + MutableHandle c = hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[B")); + gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); + MutableHandle a = hs.NewHandle( + Array::Alloc(soa.Self(), c.Get(), 1, c->GetComponentSizeShift(), allocator_type)); + EXPECT_TRUE(c.Get() == a->GetClass()); EXPECT_LE(1, a->GetLength()); - c = class_linker_->FindSystemClass(soa.Self(), "[I"); - a.Assign(Array::Alloc(soa.Self(), c, 2, c->GetComponentSizeShift(), - Runtime::Current()->GetHeap()->GetCurrentAllocator())); - EXPECT_TRUE(c == a->GetClass()); + c.Assign(class_linker_->FindSystemClass(soa.Self(), "[I")); + a.Assign( + Array::Alloc(soa.Self(), c.Get(), 2, c->GetComponentSizeShift(), allocator_type)); + EXPECT_TRUE(c.Get() == a->GetClass()); EXPECT_LE(2, a->GetLength()); - c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;"); - a.Assign(Array::Alloc(soa.Self(), c, 2, c->GetComponentSizeShift(), - Runtime::Current()->GetHeap()->GetCurrentAllocator())); - EXPECT_TRUE(c == a->GetClass()); + c.Assign(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;")); + a.Assign( + Array::Alloc(soa.Self(), c.Get(), 2, c->GetComponentSizeShift(), allocator_type)); + EXPECT_TRUE(c.Get() == a->GetClass()); EXPECT_LE(2, a->GetLength()); - c = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;"); - a.Assign(Array::Alloc(soa.Self(), c, 2, c->GetComponentSizeShift(), - Runtime::Current()->GetHeap()->GetCurrentAllocator())); - EXPECT_TRUE(c == a->GetClass()); + c.Assign(class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;")); + a.Assign( + Array::Alloc(soa.Self(), c.Get(), 2, c->GetComponentSizeShift(), allocator_type)); + EXPECT_TRUE(c.Get() == a->GetClass()); EXPECT_LE(2, a->GetLength()); } @@ -207,7 +206,8 @@ void TestPrimitiveArray(ClassLinker* cl) { ScopedObjectAccess soa(Thread::Current()); typedef typename ArrayT::ElementType T; - ArrayT* a = ArrayT::Alloc(soa.Self(), 2); + StackHandleScope<2> hs(soa.Self()); + Handle a = hs.NewHandle(ArrayT::Alloc(soa.Self(), 2)); EXPECT_EQ(2, a->GetLength()); EXPECT_EQ(0, a->Get(0)); EXPECT_EQ(0, a->Get(1)); @@ -218,16 +218,17 @@ void TestPrimitiveArray(ClassLinker* cl) { EXPECT_EQ(T(123), a->Get(0)); EXPECT_EQ(T(321), a->Get(1)); - Class* aioobe = cl->FindSystemClass(soa.Self(), "Ljava/lang/ArrayIndexOutOfBoundsException;"); + Handle aioobe = hs.NewHandle( + cl->FindSystemClass(soa.Self(), "Ljava/lang/ArrayIndexOutOfBoundsException;")); EXPECT_EQ(0, a->Get(-1)); EXPECT_TRUE(soa.Self()->IsExceptionPending()); - EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass()); + EXPECT_OBJ_PTR_EQ(aioobe.Get(), soa.Self()->GetException()->GetClass()); soa.Self()->ClearException(); EXPECT_EQ(0, a->Get(2)); EXPECT_TRUE(soa.Self()->IsExceptionPending()); - EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass()); + EXPECT_OBJ_PTR_EQ(aioobe.Get(), soa.Self()->GetException()->GetClass()); soa.Self()->ClearException(); } @@ -255,7 +256,8 @@ TEST_F(ObjectTest, PrimitiveArray_Double_Alloc) { ScopedObjectAccess soa(Thread::Current()); typedef typename ArrayT::ElementType T; - ArrayT* a = ArrayT::Alloc(soa.Self(), 2); + StackHandleScope<2> hs(soa.Self()); + Handle a = hs.NewHandle(ArrayT::Alloc(soa.Self(), 2)); EXPECT_EQ(2, a->GetLength()); EXPECT_DOUBLE_EQ(0, a->Get(0)); EXPECT_DOUBLE_EQ(0, a->Get(1)); @@ -266,17 +268,17 @@ TEST_F(ObjectTest, PrimitiveArray_Double_Alloc) { EXPECT_DOUBLE_EQ(T(123), a->Get(0)); EXPECT_DOUBLE_EQ(T(321), a->Get(1)); - Class* aioobe = class_linker_->FindSystemClass(soa.Self(), - "Ljava/lang/ArrayIndexOutOfBoundsException;"); + Handle aioobe = hs.NewHandle( + class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/ArrayIndexOutOfBoundsException;")); EXPECT_DOUBLE_EQ(0, a->Get(-1)); EXPECT_TRUE(soa.Self()->IsExceptionPending()); - EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass()); + EXPECT_OBJ_PTR_EQ(aioobe.Get(), soa.Self()->GetException()->GetClass()); soa.Self()->ClearException(); EXPECT_DOUBLE_EQ(0, a->Get(2)); EXPECT_TRUE(soa.Self()->IsExceptionPending()); - EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass()); + EXPECT_OBJ_PTR_EQ(aioobe.Get(), soa.Self()->GetException()->GetClass()); soa.Self()->ClearException(); } @@ -285,7 +287,8 @@ TEST_F(ObjectTest, PrimitiveArray_Float_Alloc) { ScopedObjectAccess soa(Thread::Current()); typedef typename ArrayT::ElementType T; - ArrayT* a = ArrayT::Alloc(soa.Self(), 2); + StackHandleScope<2> hs(soa.Self()); + Handle a = hs.NewHandle(ArrayT::Alloc(soa.Self(), 2)); EXPECT_FLOAT_EQ(2, a->GetLength()); EXPECT_FLOAT_EQ(0, a->Get(0)); EXPECT_FLOAT_EQ(0, a->Get(1)); @@ -296,17 +299,17 @@ TEST_F(ObjectTest, PrimitiveArray_Float_Alloc) { EXPECT_FLOAT_EQ(T(123), a->Get(0)); EXPECT_FLOAT_EQ(T(321), a->Get(1)); - Class* aioobe = class_linker_->FindSystemClass(soa.Self(), - "Ljava/lang/ArrayIndexOutOfBoundsException;"); + Handle aioobe = hs.NewHandle( + class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/ArrayIndexOutOfBoundsException;")); EXPECT_FLOAT_EQ(0, a->Get(-1)); EXPECT_TRUE(soa.Self()->IsExceptionPending()); - EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass()); + EXPECT_OBJ_PTR_EQ(aioobe.Get(), soa.Self()->GetException()->GetClass()); soa.Self()->ClearException(); EXPECT_FLOAT_EQ(0, a->Get(2)); EXPECT_TRUE(soa.Self()->IsExceptionPending()); - EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass()); + EXPECT_OBJ_PTR_EQ(aioobe.Get(), soa.Self()->GetException()->GetClass()); soa.Self()->ClearException(); } @@ -314,16 +317,17 @@ TEST_F(ObjectTest, PrimitiveArray_Float_Alloc) { TEST_F(ObjectTest, CreateMultiArray) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); - Handle c(hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "I"))); + StackHandleScope<4> hs(soa.Self()); + Handle int_class(hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "I"))); + Handle int_array_class = hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[I")); MutableHandle dims(hs.NewHandle(IntArray::Alloc(soa.Self(), 1))); dims->Set(0, 1); - Array* multi = Array::CreateMultiArray(soa.Self(), c, dims); - EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass(soa.Self(), "[I")); + MutableHandle multi = hs.NewHandle(Array::CreateMultiArray(soa.Self(), int_class, dims)); + EXPECT_OBJ_PTR_EQ(int_array_class.Get(), multi->GetClass()); EXPECT_EQ(1, multi->GetLength()); dims->Set(0, -1); - multi = Array::CreateMultiArray(soa.Self(), c, dims); + multi.Assign(Array::CreateMultiArray(soa.Self(), int_class, dims)); EXPECT_TRUE(soa.Self()->IsExceptionPending()); EXPECT_EQ(mirror::Class::PrettyDescriptor(soa.Self()->GetException()->GetClass()), "java.lang.NegativeArraySizeException"); @@ -334,12 +338,12 @@ TEST_F(ObjectTest, CreateMultiArray) { for (int j = 0; j < 20; ++j) { dims->Set(0, i); dims->Set(1, j); - multi = Array::CreateMultiArray(soa.Self(), c, dims); + multi.Assign(Array::CreateMultiArray(soa.Self(), int_class, dims)); EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass(soa.Self(), "[[I")); EXPECT_EQ(i, multi->GetLength()); for (int k = 0; k < i; ++k) { - Array* outer = multi->AsObjectArray()->Get(k); - EXPECT_TRUE(outer->GetClass() == class_linker_->FindSystemClass(soa.Self(), "[I")); + ObjPtr outer = multi->AsObjectArray()->Get(k); + EXPECT_OBJ_PTR_EQ(int_array_class.Get(), outer->GetClass()); EXPECT_EQ(j, outer->GetLength()); } } @@ -352,9 +356,10 @@ TEST_F(ObjectTest, StaticFieldFromCode) { jobject class_loader = LoadDex("StaticsFromCode"); const DexFile* dex_file = GetFirstDexFile(class_loader); - StackHandleScope<2> hs(soa.Self()); + StackHandleScope<3> hs(soa.Self()); Handle loader(hs.NewHandle(soa.Decode(class_loader))); - Class* klass = class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", loader); + Handle klass = + hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", loader)); ArtMethod* clinit = klass->FindClassInitializer(kRuntimePointerSize); const DexFile::TypeId* klass_type_id = dex_file->FindTypeId("LStaticsFromCode;"); ASSERT_TRUE(klass_type_id != nullptr); @@ -372,15 +377,15 @@ TEST_F(ObjectTest, StaticFieldFromCode) { ArtField* field = FindFieldFromCode(field_idx, clinit, Thread::Current(), sizeof(HeapReference)); - ObjPtr s0 = field->GetObj(klass); + ObjPtr s0 = field->GetObj(klass.Get()); EXPECT_TRUE(s0 != nullptr); Handle char_array(hs.NewHandle(CharArray::Alloc(soa.Self(), 0))); field->SetObj(field->GetDeclaringClass(), char_array.Get()); - EXPECT_OBJ_PTR_EQ(char_array.Get(), field->GetObj(klass)); + EXPECT_OBJ_PTR_EQ(char_array.Get(), field->GetObj(klass.Get())); field->SetObj(field->GetDeclaringClass(), nullptr); - EXPECT_EQ(nullptr, field->GetObj(klass)); + EXPECT_EQ(nullptr, field->GetObj(klass.Get())); // TODO: more exhaustive tests of all 6 cases of ArtField::*FromCode } @@ -486,9 +491,11 @@ TEST_F(ObjectTest, DescriptorCompare) { Handle class_loader_1(hs.NewHandle(soa.Decode(jclass_loader_1))); Handle class_loader_2(hs.NewHandle(soa.Decode(jclass_loader_2))); - Class* klass1 = linker->FindClass(soa.Self(), "LProtoCompare;", class_loader_1); + Handle klass1 = + hs.NewHandle(linker->FindClass(soa.Self(), "LProtoCompare;", class_loader_1)); ASSERT_TRUE(klass1 != nullptr); - Class* klass2 = linker->FindClass(soa.Self(), "LProtoCompare2;", class_loader_2); + Handle klass2 = + hs.NewHandle(linker->FindClass(soa.Self(), "LProtoCompare2;", class_loader_2)); ASSERT_TRUE(klass2 != nullptr); ArtMethod* m1_1 = klass1->GetVirtualMethod(0, kRuntimePointerSize); @@ -525,11 +532,11 @@ TEST_F(ObjectTest, StringHashCode) { TEST_F(ObjectTest, InstanceOf) { ScopedObjectAccess soa(Thread::Current()); jobject jclass_loader = LoadDex("XandY"); - StackHandleScope<3> hs(soa.Self()); + StackHandleScope<10> hs(soa.Self()); Handle class_loader(hs.NewHandle(soa.Decode(jclass_loader))); - Class* X = class_linker_->FindClass(soa.Self(), "LX;", class_loader); - Class* Y = class_linker_->FindClass(soa.Self(), "LY;", class_loader); + Handle X = hs.NewHandle(class_linker_->FindClass(soa.Self(), "LX;", class_loader)); + Handle Y = hs.NewHandle(class_linker_->FindClass(soa.Self(), "LY;", class_loader)); ASSERT_TRUE(X != nullptr); ASSERT_TRUE(Y != nullptr); @@ -538,51 +545,56 @@ TEST_F(ObjectTest, InstanceOf) { ASSERT_TRUE(x != nullptr); ASSERT_TRUE(y != nullptr); - EXPECT_TRUE(x->InstanceOf(X)); - EXPECT_FALSE(x->InstanceOf(Y)); - EXPECT_TRUE(y->InstanceOf(X)); - EXPECT_TRUE(y->InstanceOf(Y)); + EXPECT_TRUE(x->InstanceOf(X.Get())); + EXPECT_FALSE(x->InstanceOf(Y.Get())); + EXPECT_TRUE(y->InstanceOf(X.Get())); + EXPECT_TRUE(y->InstanceOf(Y.Get())); - Class* java_lang_Class = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Class;"); - Class* Object_array_class = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;"); + Handle java_lang_Class = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Class;")); + Handle Object_array_class = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;")); - EXPECT_FALSE(java_lang_Class->InstanceOf(Object_array_class)); - EXPECT_TRUE(Object_array_class->InstanceOf(java_lang_Class)); + EXPECT_FALSE(java_lang_Class->InstanceOf(Object_array_class.Get())); + EXPECT_TRUE(Object_array_class->InstanceOf(java_lang_Class.Get())); // All array classes implement Cloneable and Serializable. - Object* array = ObjectArray::Alloc(soa.Self(), Object_array_class, 1); - Class* java_lang_Cloneable = - class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Cloneable;"); - Class* java_io_Serializable = - class_linker_->FindSystemClass(soa.Self(), "Ljava/io/Serializable;"); - EXPECT_TRUE(array->InstanceOf(java_lang_Cloneable)); - EXPECT_TRUE(array->InstanceOf(java_io_Serializable)); + Handle array = + hs.NewHandle(ObjectArray::Alloc(soa.Self(), Object_array_class.Get(), 1)); + Handle java_lang_Cloneable = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Cloneable;")); + Handle java_io_Serializable = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/io/Serializable;")); + EXPECT_TRUE(array->InstanceOf(java_lang_Cloneable.Get())); + EXPECT_TRUE(array->InstanceOf(java_io_Serializable.Get())); } TEST_F(ObjectTest, IsAssignableFrom) { ScopedObjectAccess soa(Thread::Current()); jobject jclass_loader = LoadDex("XandY"); - StackHandleScope<1> hs(soa.Self()); + StackHandleScope<5> hs(soa.Self()); Handle class_loader(hs.NewHandle(soa.Decode(jclass_loader))); - Class* X = class_linker_->FindClass(soa.Self(), "LX;", class_loader); - Class* Y = class_linker_->FindClass(soa.Self(), "LY;", class_loader); + Handle X = hs.NewHandle(class_linker_->FindClass(soa.Self(), "LX;", class_loader)); + Handle Y = hs.NewHandle(class_linker_->FindClass(soa.Self(), "LY;", class_loader)); - EXPECT_TRUE(X->IsAssignableFrom(X)); - EXPECT_TRUE(X->IsAssignableFrom(Y)); - EXPECT_FALSE(Y->IsAssignableFrom(X)); - EXPECT_TRUE(Y->IsAssignableFrom(Y)); + EXPECT_TRUE(X->IsAssignableFrom(X.Get())); + EXPECT_TRUE(X->IsAssignableFrom(Y.Get())); + EXPECT_FALSE(Y->IsAssignableFrom(X.Get())); + EXPECT_TRUE(Y->IsAssignableFrom(Y.Get())); // class final String implements CharSequence, .. - Class* string = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/String;"); - Class* charseq = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/CharSequence;"); + Handle string = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/String;")); + Handle charseq = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/CharSequence;")); // Can String be assigned to CharSequence without a cast? - EXPECT_TRUE(charseq->IsAssignableFrom(string)); + EXPECT_TRUE(charseq->IsAssignableFrom(string.Get())); // Can CharSequence be assigned to String without a cast? - EXPECT_FALSE(string->IsAssignableFrom(charseq)); + EXPECT_FALSE(string->IsAssignableFrom(charseq.Get())); // Primitive types are only assignable to themselves const char* prims = "ZBCSIJFD"; - std::vector prim_types(strlen(prims)); + std::vector> prim_types(strlen(prims)); for (size_t i = 0; i < strlen(prims); i++) { prim_types[i] = class_linker_->FindPrimitiveClass(prims[i]); } @@ -600,55 +612,61 @@ TEST_F(ObjectTest, IsAssignableFrom) { TEST_F(ObjectTest, IsAssignableFromArray) { ScopedObjectAccess soa(Thread::Current()); jobject jclass_loader = LoadDex("XandY"); - StackHandleScope<1> hs(soa.Self()); + StackHandleScope<14> hs(soa.Self()); Handle class_loader(hs.NewHandle(soa.Decode(jclass_loader))); - Class* X = class_linker_->FindClass(soa.Self(), "LX;", class_loader); - Class* Y = class_linker_->FindClass(soa.Self(), "LY;", class_loader); + Handle X = hs.NewHandle(class_linker_->FindClass(soa.Self(), "LX;", class_loader)); + Handle Y = hs.NewHandle(class_linker_->FindClass(soa.Self(), "LY;", class_loader)); ASSERT_TRUE(X != nullptr); ASSERT_TRUE(Y != nullptr); - Class* YA = class_linker_->FindClass(soa.Self(), "[LY;", class_loader); - Class* YAA = class_linker_->FindClass(soa.Self(), "[[LY;", class_loader); + Handle YA = hs.NewHandle(class_linker_->FindClass(soa.Self(), "[LY;", class_loader)); + Handle YAA = hs.NewHandle(class_linker_->FindClass(soa.Self(), "[[LY;", class_loader)); ASSERT_TRUE(YA != nullptr); ASSERT_TRUE(YAA != nullptr); - Class* XAA = class_linker_->FindClass(soa.Self(), "[[LX;", class_loader); + Handle XAA = hs.NewHandle(class_linker_->FindClass(soa.Self(), "[[LX;", class_loader)); ASSERT_TRUE(XAA != nullptr); - Class* O = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); - Class* OA = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;"); - Class* OAA = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;"); - Class* OAAA = class_linker_->FindSystemClass(soa.Self(), "[[[Ljava/lang/Object;"); + Handle O = hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")); + Handle OA = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;")); + Handle OAA = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;")); + Handle OAAA = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[[[Ljava/lang/Object;")); ASSERT_TRUE(O != nullptr); ASSERT_TRUE(OA != nullptr); ASSERT_TRUE(OAA != nullptr); ASSERT_TRUE(OAAA != nullptr); - Class* S = class_linker_->FindSystemClass(soa.Self(), "Ljava/io/Serializable;"); - Class* SA = class_linker_->FindSystemClass(soa.Self(), "[Ljava/io/Serializable;"); - Class* SAA = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/io/Serializable;"); + Handle S = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/io/Serializable;")); + Handle SA = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/io/Serializable;")); + Handle SAA = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[[Ljava/io/Serializable;")); ASSERT_TRUE(S != nullptr); ASSERT_TRUE(SA != nullptr); ASSERT_TRUE(SAA != nullptr); - Class* IA = class_linker_->FindSystemClass(soa.Self(), "[I"); + Handle IA = hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[I")); ASSERT_TRUE(IA != nullptr); - EXPECT_TRUE(YAA->IsAssignableFrom(YAA)); // identity - EXPECT_TRUE(XAA->IsAssignableFrom(YAA)); // element superclass - EXPECT_FALSE(YAA->IsAssignableFrom(XAA)); - EXPECT_FALSE(Y->IsAssignableFrom(YAA)); - EXPECT_FALSE(YA->IsAssignableFrom(YAA)); - EXPECT_TRUE(O->IsAssignableFrom(YAA)); // everything is an Object - EXPECT_TRUE(OA->IsAssignableFrom(YAA)); - EXPECT_TRUE(OAA->IsAssignableFrom(YAA)); - EXPECT_TRUE(S->IsAssignableFrom(YAA)); // all arrays are Serializable - EXPECT_TRUE(SA->IsAssignableFrom(YAA)); - EXPECT_FALSE(SAA->IsAssignableFrom(YAA)); // unless Y was Serializable + EXPECT_TRUE(YAA->IsAssignableFrom(YAA.Get())); // identity + EXPECT_TRUE(XAA->IsAssignableFrom(YAA.Get())); // element superclass + EXPECT_FALSE(YAA->IsAssignableFrom(XAA.Get())); + EXPECT_FALSE(Y->IsAssignableFrom(YAA.Get())); + EXPECT_FALSE(YA->IsAssignableFrom(YAA.Get())); + EXPECT_TRUE(O->IsAssignableFrom(YAA.Get())); // everything is an Object + EXPECT_TRUE(OA->IsAssignableFrom(YAA.Get())); + EXPECT_TRUE(OAA->IsAssignableFrom(YAA.Get())); + EXPECT_TRUE(S->IsAssignableFrom(YAA.Get())); // all arrays are Serializable + EXPECT_TRUE(SA->IsAssignableFrom(YAA.Get())); + EXPECT_FALSE(SAA->IsAssignableFrom(YAA.Get())); // unless Y was Serializable - EXPECT_FALSE(IA->IsAssignableFrom(OA)); - EXPECT_FALSE(OA->IsAssignableFrom(IA)); - EXPECT_TRUE(O->IsAssignableFrom(IA)); + EXPECT_FALSE(IA->IsAssignableFrom(OA.Get())); + EXPECT_FALSE(OA->IsAssignableFrom(IA.Get())); + EXPECT_TRUE(O->IsAssignableFrom(IA.Get())); } TEST_F(ObjectTest, FindInstanceField) { @@ -656,7 +674,7 @@ TEST_F(ObjectTest, FindInstanceField) { StackHandleScope<1> hs(soa.Self()); Handle s(hs.NewHandle(String::AllocFromModifiedUtf8(soa.Self(), "ABC"))); ASSERT_TRUE(s != nullptr); - Class* c = s->GetClass(); + ObjPtr c = s->GetClass(); ASSERT_TRUE(c != nullptr); // Wrong type. @@ -798,9 +816,9 @@ TEST_F(ObjectTest, PrettyTypeOf) { Handle a(hs.NewHandle(mirror::ShortArray::Alloc(soa.Self(), 2))); EXPECT_EQ("short[]", mirror::Object::PrettyTypeOf(a.Get())); - mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); + ObjPtr c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); ASSERT_TRUE(c != nullptr); - mirror::Object* o = mirror::ObjectArray::Alloc(soa.Self(), c, 0); + ObjPtr o = mirror::ObjectArray::Alloc(soa.Self(), c, 0); EXPECT_EQ("java.lang.String[]", mirror::Object::PrettyTypeOf(o)); EXPECT_EQ("java.lang.Class", mirror::Object::PrettyTypeOf(o->GetClass())); } diff --git a/runtime/mirror/reference-inl.h b/runtime/mirror/reference-inl.h index 84e54948dd4f79dfc661eff59ef02490d1907bc5..f8de6e6d90fcf14388701a162c1ed3c0e2b99372 100644 --- a/runtime/mirror/reference-inl.h +++ b/runtime/mirror/reference-inl.h @@ -19,7 +19,6 @@ #include "reference.h" -#include "gc_root-inl.h" #include "obj_ptr-inl.h" #include "runtime.h" @@ -49,12 +48,6 @@ inline void FinalizerReference::SetZombie(ObjPtr zombie) { return SetFieldObjectVolatile(ZombieOffset(), zombie); } -template -inline Class* Reference::GetJavaLangRefReference() { - DCHECK(!java_lang_ref_Reference_.IsNull()); - return java_lang_ref_Reference_.Read(); -} - } // namespace mirror } // namespace art diff --git a/runtime/mirror/reference.cc b/runtime/mirror/reference.cc deleted file mode 100644 index 1d0b4c5b275844acbb1484a84665bb97360dfeec..0000000000000000000000000000000000000000 --- a/runtime/mirror/reference.cc +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "reference-inl.h" - -#include "art_method.h" -#include "gc_root-inl.h" - -namespace art { -namespace mirror { - -GcRoot Reference::java_lang_ref_Reference_; - -void Reference::SetClass(ObjPtr java_lang_ref_Reference) { - CHECK(java_lang_ref_Reference_.IsNull()); - CHECK(java_lang_ref_Reference != nullptr); - java_lang_ref_Reference_ = GcRoot(java_lang_ref_Reference); -} - -void Reference::ResetClass() { - CHECK(!java_lang_ref_Reference_.IsNull()); - java_lang_ref_Reference_ = GcRoot(nullptr); -} - -void Reference::VisitRoots(RootVisitor* visitor) { - java_lang_ref_Reference_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - -} // namespace mirror -} // namespace art diff --git a/runtime/mirror/reference.h b/runtime/mirror/reference.h index b10c29444e97aa145f907d1c90034b0bce51622e..63c5ae5ff884ed4d1da9c57d2d2d3a8da337f3c3 100644 --- a/runtime/mirror/reference.h +++ b/runtime/mirror/reference.h @@ -20,8 +20,6 @@ #include "base/enums.h" #include "base/macros.h" #include "base/mutex.h" -#include "class.h" -#include "gc_root.h" #include "obj_ptr.h" #include "object.h" #include "read_barrier_option.h" @@ -98,12 +96,6 @@ class MANAGED Reference : public Object { return GetPendingNext() == nullptr; } - template - static ALWAYS_INLINE Class* GetJavaLangRefReference() REQUIRES_SHARED(Locks::mutator_lock_); - static void SetClass(ObjPtr klass); - static void ResetClass(); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: // Note: This avoids a read barrier, it should only be used by the GC. HeapReference* GetReferentReferenceAddr() REQUIRES_SHARED(Locks::mutator_lock_) { @@ -116,8 +108,6 @@ class MANAGED Reference : public Object { HeapReference queue_next_; HeapReference referent_; // Note this is Java volatile: - static GcRoot java_lang_ref_Reference_; - friend struct art::ReferenceOffsets; // for verifying offset information friend class gc::ReferenceProcessor; friend class gc::ReferenceQueue; diff --git a/runtime/mirror/stack_trace_element.cc b/runtime/mirror/stack_trace_element.cc index bb3242e0353594230bb38b5a9fdcb7ec25ef07cb..5a7575a0277a71cf64f3ea5452eb71530344cb76 100644 --- a/runtime/mirror/stack_trace_element.cc +++ b/runtime/mirror/stack_trace_element.cc @@ -18,8 +18,8 @@ #include "class-inl.h" #include "class.h" +#include "class_root.h" #include "gc/accounting/card_table-inl.h" -#include "gc_root-inl.h" #include "handle_scope-inl.h" #include "object-inl.h" #include "string.h" @@ -27,26 +27,13 @@ namespace art { namespace mirror { -GcRoot StackTraceElement::java_lang_StackTraceElement_; - -void StackTraceElement::SetClass(ObjPtr java_lang_StackTraceElement) { - CHECK(java_lang_StackTraceElement_.IsNull()); - CHECK(java_lang_StackTraceElement != nullptr); - java_lang_StackTraceElement_ = GcRoot(java_lang_StackTraceElement); -} - -void StackTraceElement::ResetClass() { - CHECK(!java_lang_StackTraceElement_.IsNull()); - java_lang_StackTraceElement_ = GcRoot(nullptr); -} - StackTraceElement* StackTraceElement::Alloc(Thread* self, Handle declaring_class, Handle method_name, Handle file_name, int32_t line_number) { ObjPtr trace = - ObjPtr::DownCast(GetStackTraceElement()->AllocObject(self)); + ObjPtr::DownCast(GetClassRoot()->AllocObject(self)); if (LIKELY(trace != nullptr)) { if (Runtime::Current()->IsActiveTransaction()) { trace->Init(declaring_class.Get(), method_name.Get(), file_name.Get(), line_number); @@ -72,10 +59,5 @@ void StackTraceElement::Init(ObjPtr declaring_class, line_number); } -void StackTraceElement::VisitRoots(RootVisitor* visitor) { - java_lang_StackTraceElement_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - - } // namespace mirror } // namespace art diff --git a/runtime/mirror/stack_trace_element.h b/runtime/mirror/stack_trace_element.h index 87e8a1f65990b05add5b02cbea044491a555645f..55a2ef0b4992c9e73b1f6880d0d94597fe6cab5e 100644 --- a/runtime/mirror/stack_trace_element.h +++ b/runtime/mirror/stack_trace_element.h @@ -17,7 +17,6 @@ #ifndef ART_RUNTIME_MIRROR_STACK_TRACE_ELEMENT_H_ #define ART_RUNTIME_MIRROR_STACK_TRACE_ELEMENT_H_ -#include "gc_root.h" #include "object.h" namespace art { @@ -53,15 +52,6 @@ class MANAGED StackTraceElement FINAL : public Object { int32_t line_number) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - static void SetClass(ObjPtr java_lang_StackTraceElement); - static void ResetClass(); - static void VisitRoots(RootVisitor* visitor) - REQUIRES_SHARED(Locks::mutator_lock_); - static Class* GetStackTraceElement() REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(!java_lang_StackTraceElement_.IsNull()); - return java_lang_StackTraceElement_.Read(); - } - private: // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". HeapReference declaring_class_; @@ -76,8 +66,6 @@ class MANAGED StackTraceElement FINAL : public Object { int32_t line_number) REQUIRES_SHARED(Locks::mutator_lock_); - static GcRoot java_lang_StackTraceElement_; - friend struct art::StackTraceElementOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(StackTraceElement); }; diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h index a60861cc281a0f04ab1c5c52de473a1f5f82049b..8fa2c6cf7ff342e91022db3d8cc027d30548f05f 100644 --- a/runtime/mirror/string-inl.h +++ b/runtime/mirror/string-inl.h @@ -25,6 +25,7 @@ #include "base/globals.h" #include "base/utils.h" #include "class.h" +#include "class_root.h" #include "common_throws.h" #include "dex/utf.h" #include "gc/heap-inl.h" @@ -194,21 +195,6 @@ int32_t String::FastIndexOf(MemoryType* chars, int32_t ch, int32_t start) { return -1; } -template -inline size_t String::SizeOf() { - size_t size = sizeof(String); - if (IsCompressed()) { - size += (sizeof(uint8_t) * GetLength()); - } else { - size += (sizeof(uint16_t) * GetLength()); - } - // String.equals() intrinsics assume zero-padding up to kObjectAlignment, - // so make sure the zero-padding is actually copied around if GC compaction - // chooses to copy only SizeOf() bytes. - // http://b/23528461 - return RoundUp(size, kObjectAlignment); -} - template inline String* String::Alloc(Thread* self, int32_t utf16_length_with_flag, gc::AllocatorType allocator_type, @@ -226,7 +212,8 @@ inline String* String::Alloc(Thread* self, int32_t utf16_length_with_flag, // http://b/23528461 size_t alloc_size = RoundUp(size, kObjectAlignment); - Class* string_class = GetJavaLangString(); + Runtime* runtime = Runtime::Current(); + ObjPtr string_class = GetClassRoot(runtime->GetClassLinker()); // Check for overflow and throw OutOfMemoryError if this was an unreasonable request. // Do this by comparing with the maximum length that will _not_ cause an overflow. const size_t overflow_length = (-header_size) / block_size; // Unsigned arithmetic. @@ -242,7 +229,7 @@ inline String* String::Alloc(Thread* self, int32_t utf16_length_with_flag, return nullptr; } - gc::Heap* heap = Runtime::Current()->GetHeap(); + gc::Heap* heap = runtime->GetHeap(); return down_cast( heap->AllocObjectWithAllocator(self, string_class, alloc_size, allocator_type, pre_fence_visitor)); diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc index 6208a962e54524ae19e4be9b5c5428c00b8ba4ee..d5ef0392737738ca0b98bacd71ec98892f0d29ec 100644 --- a/runtime/mirror/string.cc +++ b/runtime/mirror/string.cc @@ -24,7 +24,6 @@ #include "dex/descriptors_names.h" #include "dex/utf-inl.h" #include "gc/accounting/card_table-inl.h" -#include "gc_root-inl.h" #include "handle_scope-inl.h" #include "intern_table.h" #include "object-inl.h" @@ -35,9 +34,6 @@ namespace art { namespace mirror { -// TODO: get global references for these -GcRoot String::java_lang_String_; - int32_t String::FastIndexOf(int32_t ch, int32_t start) { int32_t count = GetLength(); if (start < 0) { @@ -52,18 +48,6 @@ int32_t String::FastIndexOf(int32_t ch, int32_t start) { } } -void String::SetClass(ObjPtr java_lang_String) { - CHECK(java_lang_String_.IsNull()); - CHECK(java_lang_String != nullptr); - CHECK(java_lang_String->IsStringClass()); - java_lang_String_ = GcRoot(java_lang_String); -} - -void String::ResetClass() { - CHECK(!java_lang_String_.IsNull()); - java_lang_String_ = GcRoot(nullptr); -} - int String::ComputeHashCode() { int32_t hash_code = 0; if (IsCompressed()) { @@ -372,10 +356,6 @@ int32_t String::CompareTo(ObjPtr rhs) { return count_diff; } -void String::VisitRoots(RootVisitor* visitor) { - java_lang_String_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - CharArray* String::ToCharArray(Thread* self) { StackHandleScope<1> hs(self); Handle string(hs.NewHandle(this)); diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index 545fe93516f5d139841e88e266588047b0c15239..0e2fc903b53da6b086bdfcf7084b6e3a3949ad6d 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -17,8 +17,10 @@ #ifndef ART_RUNTIME_MIRROR_STRING_H_ #define ART_RUNTIME_MIRROR_STRING_H_ +#include "base/bit_utils.h" +#include "base/globals.h" #include "gc/allocator_type.h" -#include "gc_root.h" +#include "class.h" #include "object.h" namespace art { @@ -65,7 +67,19 @@ class MANAGED String FINAL : public Object { } template - size_t SizeOf() REQUIRES_SHARED(Locks::mutator_lock_); + size_t SizeOf() REQUIRES_SHARED(Locks::mutator_lock_) { + size_t size = sizeof(String); + if (IsCompressed()) { + size += (sizeof(uint8_t) * GetLength()); + } else { + size += (sizeof(uint16_t) * GetLength()); + } + // String.equals() intrinsics assume zero-padding up to kObjectAlignment, + // so make sure the zero-padding is actually copied around if GC compaction + // chooses to copy only SizeOf() bytes. + // http://b/23528461 + return RoundUp(size, kObjectAlignment); + } // Taking out the first/uppermost bit because it is not part of actual length value template @@ -213,15 +227,6 @@ class MANAGED String FINAL : public Object { : length; } - static Class* GetJavaLangString() REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(!java_lang_String_.IsNull()); - return java_lang_String_.Read(); - } - - static void SetClass(ObjPtr java_lang_String) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - // Returns a human-readable equivalent of 'descriptor'. So "I" would be "int", // "[[I" would be "int[][]", "[Ljava/lang/String;" would be // "java.lang.String[]", and so forth. @@ -266,10 +271,7 @@ class MANAGED String FINAL : public Object { uint8_t value_compressed_[0]; }; - static GcRoot java_lang_String_; - friend struct art::StringOffsets; // for verifying offset information - ART_FRIEND_TEST(art::StubTest, ReadBarrierForRoot); // For java_lang_String_. DISALLOW_IMPLICIT_CONSTRUCTORS(String); }; diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc index b6173d422bd7518509b73413cae63b5311286df0..82e295a616b5ad11189001d481feb3cdecf77c35 100644 --- a/runtime/mirror/throwable.cc +++ b/runtime/mirror/throwable.cc @@ -22,6 +22,7 @@ #include "base/enums.h" #include "base/utils.h" #include "class-inl.h" +#include "class_root.h" #include "dex/dex_file-inl.h" #include "gc/accounting/card_table-inl.h" #include "object-inl.h" @@ -36,8 +37,6 @@ namespace mirror { using android::base::StringPrintf; -GcRoot Throwable::java_lang_Throwable_; - void Throwable::SetDetailMessage(ObjPtr new_detail_message) { if (Runtime::Current()->IsActiveTransaction()) { SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_), new_detail_message); @@ -75,6 +74,10 @@ bool Throwable::IsCheckedException() { return !InstanceOf(WellKnownClasses::ToClass(WellKnownClasses::java_lang_RuntimeException)); } +bool Throwable::IsError() { + return InstanceOf(WellKnownClasses::ToClass(WellKnownClasses::java_lang_Error)); +} + int32_t Throwable::GetStackDepth() { ObjPtr stack_state = GetStackState(); if (stack_state == nullptr || !stack_state->IsObjectArray()) { @@ -124,8 +127,7 @@ std::string Throwable::Dump() { } else { ObjPtr stack_trace = GetStackTrace(); if (stack_trace != nullptr && stack_trace->IsObjectArray()) { - CHECK_EQ(stack_trace->GetClass()->GetComponentType(), - StackTraceElement::GetStackTraceElement()); + CHECK_EQ(stack_trace->GetClass()->GetComponentType(), GetClassRoot()); ObjPtr> ste_array = ObjPtr>::DownCast(stack_trace); if (ste_array->GetLength() == 0) { @@ -155,21 +157,6 @@ std::string Throwable::Dump() { return result; } -void Throwable::SetClass(ObjPtr java_lang_Throwable) { - CHECK(java_lang_Throwable_.IsNull()); - CHECK(java_lang_Throwable != nullptr); - java_lang_Throwable_ = GcRoot(java_lang_Throwable); -} - -void Throwable::ResetClass() { - CHECK(!java_lang_Throwable_.IsNull()); - java_lang_Throwable_ = GcRoot(nullptr); -} - -void Throwable::VisitRoots(RootVisitor* visitor) { - java_lang_Throwable_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - Object* Throwable::GetStackState() { return GetFieldObjectVolatile(OFFSET_OF_OBJECT_MEMBER(Throwable, backtrace_)); } diff --git a/runtime/mirror/throwable.h b/runtime/mirror/throwable.h index fb45228f49cfb979b14b38e0496351addfe31583..a9e5d1a30b30aac7185d640d5ddb206de096de6d 100644 --- a/runtime/mirror/throwable.h +++ b/runtime/mirror/throwable.h @@ -17,7 +17,6 @@ #ifndef ART_RUNTIME_MIRROR_THROWABLE_H_ #define ART_RUNTIME_MIRROR_THROWABLE_H_ -#include "gc_root.h" #include "object.h" namespace art { @@ -44,19 +43,10 @@ class MANAGED Throwable : public Object { void SetCause(ObjPtr cause) REQUIRES_SHARED(Locks::mutator_lock_); void SetStackState(ObjPtr state) REQUIRES_SHARED(Locks::mutator_lock_); bool IsCheckedException() REQUIRES_SHARED(Locks::mutator_lock_); - - static Class* GetJavaLangThrowable() REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(!java_lang_Throwable_.IsNull()); - return java_lang_Throwable_.Read(); - } + bool IsError() REQUIRES_SHARED(Locks::mutator_lock_); int32_t GetStackDepth() REQUIRES_SHARED(Locks::mutator_lock_); - static void SetClass(ObjPtr java_lang_Throwable); - static void ResetClass(); - static void VisitRoots(RootVisitor* visitor) - REQUIRES_SHARED(Locks::mutator_lock_); - private: Object* GetStackState() REQUIRES_SHARED(Locks::mutator_lock_); Object* GetStackTrace() REQUIRES_SHARED(Locks::mutator_lock_); @@ -68,8 +58,6 @@ class MANAGED Throwable : public Object { HeapReference stack_trace_; HeapReference suppressed_exceptions_; - static GcRoot java_lang_Throwable_; - friend struct art::ThrowableOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(Throwable); }; diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc index a79c0a26d069d8725577bd7d7909c105dc605338..56c953b8164109716439da957d17d1ddd718dd8c 100644 --- a/runtime/mirror/var_handle.cc +++ b/runtime/mirror/var_handle.cc @@ -20,12 +20,13 @@ #include "art_field-inl.h" #include "class-inl.h" #include "class_linker.h" -#include "gc_root-inl.h" +#include "class_root.h" #include "intrinsics_enum.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "jvalue-inl.h" -#include "method_handles.h" +#include "method_handles-inl.h" #include "method_type.h" +#include "obj_ptr-inl.h" #include "well_known_classes.h" namespace art { @@ -265,31 +266,22 @@ int32_t BuildParameterArray(ObjPtr (¶meters)[VarHandle::kMaxAccessorP // Returns the return type associated with an AccessModeTemplate based // on the template and the variable type specified. -Class* GetReturnType(AccessModeTemplate access_mode_template, ObjPtr varType) +static ObjPtr GetReturnType(AccessModeTemplate access_mode_template, ObjPtr varType) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(varType != nullptr); switch (access_mode_template) { case AccessModeTemplate::kCompareAndSet: - return Runtime::Current()->GetClassLinker()->FindPrimitiveClass('Z'); + return GetClassRoot(ClassRoot::kPrimitiveBoolean); case AccessModeTemplate::kCompareAndExchange: case AccessModeTemplate::kGet: case AccessModeTemplate::kGetAndUpdate: - return varType.Ptr(); + return varType; case AccessModeTemplate::kSet: - return Runtime::Current()->GetClassLinker()->FindPrimitiveClass('V'); + return GetClassRoot(ClassRoot::kPrimitiveVoid); } return nullptr; } -ObjectArray* NewArrayOfClasses(Thread* self, int count) - REQUIRES_SHARED(Locks::mutator_lock_) { - Runtime* const runtime = Runtime::Current(); - ClassLinker* const class_linker = runtime->GetClassLinker(); - ObjPtr class_type = mirror::Class::GetJavaLangClass(); - ObjPtr array_of_class = class_linker->FindArrayClass(self, &class_type); - return ObjectArray::Alloc(Thread::Current(), array_of_class, count); -} - // Method to insert a read barrier for accessors to reference fields. inline void ReadBarrierForVarHandleAccess(ObjPtr obj, MemberOffset field_offset) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -1029,15 +1021,17 @@ bool FieldAccessor>::Dispatch(VarHandle::AccessMode access_mode, ObjPtr desired_value = ValueGetter>::Get(getter); bool cas_result; if (Runtime::Current()->IsActiveTransaction()) { - cas_result = obj->CasFieldStrongSequentiallyConsistentObject( - field_offset, - expected_value, - desired_value); + cas_result = obj->CasFieldObject(field_offset, + expected_value, + desired_value, + CASMode::kStrong, + std::memory_order_seq_cst); } else { - cas_result = obj->CasFieldStrongSequentiallyConsistentObject( - field_offset, - expected_value, - desired_value); + cas_result = obj->CasFieldObject(field_offset, + expected_value, + desired_value, + CASMode::kStrong, + std::memory_order_seq_cst); } StoreResult(cas_result, result); break; @@ -1051,15 +1045,18 @@ bool FieldAccessor>::Dispatch(VarHandle::AccessMode access_mode, ObjPtr desired_value = ValueGetter>::Get(getter); bool cas_result; if (Runtime::Current()->IsActiveTransaction()) { - cas_result = obj->CasFieldWeakSequentiallyConsistentObject( - field_offset, - expected_value, - desired_value); + cas_result = obj->CasFieldObject(field_offset, + expected_value, + desired_value, + CASMode::kWeak, + std::memory_order_seq_cst); } else { - cas_result = obj->CasFieldWeakSequentiallyConsistentObject( + cas_result = obj->CasFieldObject( field_offset, expected_value, - desired_value); + desired_value, + CASMode::kWeak, + std::memory_order_seq_cst); } StoreResult(cas_result, result); break; @@ -1072,15 +1069,13 @@ bool FieldAccessor>::Dispatch(VarHandle::AccessMode access_mode, ObjPtr desired_value = ValueGetter>::Get(getter); ObjPtr witness_value; if (Runtime::Current()->IsActiveTransaction()) { - witness_value = obj->CompareAndExchangeFieldObject( - field_offset, - expected_value, - desired_value); + witness_value = obj->CompareAndExchangeFieldObject(field_offset, + expected_value, + desired_value); } else { - witness_value = obj->CompareAndExchangeFieldObject( - field_offset, - expected_value, - desired_value); + witness_value = obj->CompareAndExchangeFieldObject(field_offset, + expected_value, + desired_value); } StoreResult(witness_value, result); break; @@ -1409,15 +1404,15 @@ class ByteArrayViewAccessor { } // namespace -Class* VarHandle::GetVarType() { +ObjPtr VarHandle::GetVarType() { return GetFieldObject(VarTypeOffset()); } -Class* VarHandle::GetCoordinateType0() { +ObjPtr VarHandle::GetCoordinateType0() { return GetFieldObject(CoordinateType0Offset()); } -Class* VarHandle::GetCoordinateType1() { +ObjPtr VarHandle::GetCoordinateType1() { return GetFieldObject(CoordinateType1Offset()); } @@ -1425,21 +1420,24 @@ int32_t VarHandle::GetAccessModesBitMask() { return GetField32(AccessModesBitMaskOffset()); } -bool VarHandle::IsMethodTypeCompatible(AccessMode access_mode, MethodType* method_type) { - StackHandleScope<3> hs(Thread::Current()); - Handle mt_rtype(hs.NewHandle(method_type->GetRType())); - Handle vh(hs.NewHandle(this)); - Handle var_type(hs.NewHandle(vh->GetVarType())); +VarHandle::MatchKind VarHandle::GetMethodTypeMatchForAccessMode(AccessMode access_mode, + MethodType* method_type) { + MatchKind match = MatchKind::kExact; + + ObjPtr vh = this; + ObjPtr var_type = vh->GetVarType(); + ObjPtr mt_rtype = method_type->GetRType(); AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode); - // Check return type first. - if (mt_rtype->GetPrimitiveType() == Primitive::Type::kPrimVoid) { - // The result of the operation will be discarded. The return type - // of the VarHandle is immaterial. - } else { - ObjPtr vh_rtype(GetReturnType(access_mode_template, var_type.Get())); - if (!IsReturnTypeConvertible(vh_rtype, mt_rtype.Get())) { - return false; + // Check return type first. If the return type of the method + // of the VarHandle is immaterial. + if (mt_rtype->GetPrimitiveType() != Primitive::Type::kPrimVoid) { + ObjPtr vh_rtype = GetReturnType(access_mode_template, var_type); + if (vh_rtype != mt_rtype) { + if (!IsReturnTypeConvertible(vh_rtype, mt_rtype)) { + return MatchKind::kNone; + } + match = MatchKind::kWithConversions; } } @@ -1447,21 +1445,25 @@ bool VarHandle::IsMethodTypeCompatible(AccessMode access_mode, MethodType* metho ObjPtr vh_ptypes[VarHandle::kMaxAccessorParameters]; const int32_t vh_ptypes_count = BuildParameterArray(vh_ptypes, access_mode_template, - var_type.Get(), + var_type, GetCoordinateType0(), GetCoordinateType1()); if (vh_ptypes_count != method_type->GetPTypes()->GetLength()) { - return false; + return MatchKind::kNone; } // Check the parameter types are compatible. ObjPtr> mt_ptypes = method_type->GetPTypes(); for (int32_t i = 0; i < vh_ptypes_count; ++i) { + if (mt_ptypes->Get(i) == vh_ptypes[i]) { + continue; + } if (!IsParameterTypeConvertible(mt_ptypes->Get(i), vh_ptypes[i])) { - return false; + return MatchKind::kNone; } + match = MatchKind::kWithConversions; } - return true; + return match; } bool VarHandle::IsInvokerMethodTypeCompatible(AccessMode access_mode, @@ -1505,10 +1507,10 @@ bool VarHandle::IsInvokerMethodTypeCompatible(AccessMode access_mode, return true; } -MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self, - ObjPtr var_handle, - AccessMode access_mode) { - // This is a static as the var_handle might be moved by the GC during it's execution. +ObjPtr VarHandle::GetMethodTypeForAccessMode(Thread* self, + ObjPtr var_handle, + AccessMode access_mode) { + // This is a static method as the var_handle might be moved by the GC during it's execution. AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode); StackHandleScope<3> hs(self); @@ -1517,7 +1519,9 @@ MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self, const int32_t ptypes_count = GetNumberOfParameters(access_mode_template, vh->GetCoordinateType0(), vh->GetCoordinateType1()); - Handle> ptypes = hs.NewHandle(NewArrayOfClasses(self, ptypes_count)); + ObjPtr array_of_class = GetClassRoot>(); + Handle> ptypes = + hs.NewHandle(ObjectArray::Alloc(Thread::Current(), array_of_class, ptypes_count)); if (ptypes == nullptr) { return nullptr; } @@ -1529,30 +1533,62 @@ MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self, vh->GetCoordinateType0(), vh->GetCoordinateType1()); for (int32_t i = 0; i < ptypes_count; ++i) { - ptypes->Set(i, ptypes_array[i].Ptr()); + ptypes->Set(i, ptypes_array[i]); } return MethodType::Create(self, rtype, ptypes); } -MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self, AccessMode access_mode) { +ObjPtr VarHandle::GetMethodTypeForAccessMode(Thread* self, AccessMode access_mode) { return GetMethodTypeForAccessMode(self, this, access_mode); } +std::string VarHandle::PrettyDescriptorForAccessMode(AccessMode access_mode) { + // Effect MethodType::PrettyDescriptor() without first creating a method type first. + std::ostringstream oss; + oss << '('; + + AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode); + ObjPtr var_type = GetVarType(); + ObjPtr ctypes[2] = { GetCoordinateType0(), GetCoordinateType1() }; + const int32_t ptypes_count = GetNumberOfParameters(access_mode_template, ctypes[0], ctypes[1]); + int32_t ptypes_done = 0; + for (ObjPtr ctype : ctypes) { + if (!ctype.IsNull()) { + if (ptypes_done != 0) { + oss << ", "; + } + oss << ctype->PrettyDescriptor();; + ptypes_done++; + } + } + while (ptypes_done != ptypes_count) { + if (ptypes_done != 0) { + oss << ", "; + } + oss << var_type->PrettyDescriptor(); + ptypes_done++; + } + ObjPtr rtype = GetReturnType(access_mode_template, var_type); + oss << ')' << rtype->PrettyDescriptor(); + return oss.str(); +} + bool VarHandle::Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) { - Class* klass = GetClass(); - if (klass == FieldVarHandle::StaticClass()) { + ObjPtr> class_roots = Runtime::Current()->GetClassLinker()->GetClassRoots(); + ObjPtr klass = GetClass(); + if (klass == GetClassRoot(class_roots)) { auto vh = reinterpret_cast(this); return vh->Access(access_mode, shadow_frame, operands, result); - } else if (klass == ArrayElementVarHandle::StaticClass()) { + } else if (klass == GetClassRoot(class_roots)) { auto vh = reinterpret_cast(this); return vh->Access(access_mode, shadow_frame, operands, result); - } else if (klass == ByteArrayViewVarHandle::StaticClass()) { + } else if (klass == GetClassRoot(class_roots)) { auto vh = reinterpret_cast(this); return vh->Access(access_mode, shadow_frame, operands, result); - } else if (klass == ByteBufferViewVarHandle::StaticClass()) { + } else if (klass == GetClassRoot(class_roots)) { auto vh = reinterpret_cast(this); return vh->Access(access_mode, shadow_frame, operands, result); } else { @@ -1643,27 +1679,6 @@ bool VarHandle::GetAccessModeByMethodName(const char* method_name, AccessMode* a return true; } -Class* VarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); -} - -void VarHandle::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void VarHandle::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void VarHandle::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - -GcRoot VarHandle::static_class_; - ArtField* FieldVarHandle::GetField() { uintptr_t opaque_field = static_cast(GetField64(ArtFieldOffset())); return reinterpret_cast(opaque_field); @@ -1671,7 +1686,7 @@ ArtField* FieldVarHandle::GetField() { bool FieldVarHandle::Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) { ShadowFrameGetter getter(*shadow_frame, operands); ArtField* field = GetField(); @@ -1720,30 +1735,9 @@ bool FieldVarHandle::Access(AccessMode access_mode, UNREACHABLE(); } -Class* FieldVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); -} - -void FieldVarHandle::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void FieldVarHandle::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void FieldVarHandle::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - -GcRoot FieldVarHandle::static_class_; - bool ArrayElementVarHandle::Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) { ShadowFrameGetter getter(*shadow_frame, operands); @@ -1829,34 +1823,13 @@ bool ArrayElementVarHandle::Access(AccessMode access_mode, UNREACHABLE(); } -Class* ArrayElementVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); -} - -void ArrayElementVarHandle::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void ArrayElementVarHandle::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void ArrayElementVarHandle::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - -GcRoot ArrayElementVarHandle::static_class_; - bool ByteArrayViewVarHandle::GetNativeByteOrder() { return GetFieldBoolean(NativeByteOrderOffset()); } bool ByteArrayViewVarHandle::Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) { ShadowFrameGetter getter(*shadow_frame, operands); @@ -1938,34 +1911,13 @@ bool ByteArrayViewVarHandle::Access(AccessMode access_mode, UNREACHABLE(); } -Class* ByteArrayViewVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); -} - -void ByteArrayViewVarHandle::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void ByteArrayViewVarHandle::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void ByteArrayViewVarHandle::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - -GcRoot ByteArrayViewVarHandle::static_class_; - bool ByteBufferViewVarHandle::GetNativeByteOrder() { return GetFieldBoolean(NativeByteOrderOffset()); } bool ByteBufferViewVarHandle::Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) { ShadowFrameGetter getter(*shadow_frame, operands); @@ -2079,26 +2031,5 @@ bool ByteBufferViewVarHandle::Access(AccessMode access_mode, UNREACHABLE(); } -Class* ByteBufferViewVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); -} - -void ByteBufferViewVarHandle::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void ByteBufferViewVarHandle::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void ByteBufferViewVarHandle::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - -GcRoot ByteBufferViewVarHandle::static_class_; - } // namespace mirror } // namespace art diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h index d46d900a8deeb934b069493d37e84985e42af9a9..48c9d74e301fd8ce8715fc56741006b480867783 100644 --- a/runtime/mirror/var_handle.h +++ b/runtime/mirror/var_handle.h @@ -19,7 +19,6 @@ #include "handle.h" #include "interpreter/shadow_frame.h" -#include "gc_root.h" #include "jvalue.h" #include "object.h" @@ -27,6 +26,7 @@ namespace art { template class Handle; class InstructionOperands; +template class ObjPtr; enum class Intrinsics; @@ -99,14 +99,16 @@ class MANAGED VarHandle : public Object { return (GetAccessModesBitMask() & (1u << static_cast(accessMode))) != 0; } - // Returns true if the MethodType specified is compatible with the - // method type associated with the specified AccessMode. The - // supplied MethodType is assumed to be from the point of invocation - // so it is valid for the supplied MethodType to have a void return - // value when the return value for the AccessMode is non-void. This - // corresponds to the result of the accessor being discarded. - bool IsMethodTypeCompatible(AccessMode access_mode, MethodType* method_type) - REQUIRES_SHARED(Locks::mutator_lock_); + enum MatchKind : uint8_t { + kNone, + kWithConversions, + kExact + }; + + // Returns match information on the compatability between the exact method type for + // 'access_mode' and the provided 'method_type'. + MatchKind GetMethodTypeMatchForAccessMode(AccessMode access_mode, MethodType* method_type) + REQUIRES_SHARED(Locks::mutator_lock_); // Returns true if the MethodType specified is compatible with the // specified access_mode if the first parameter of method_type is @@ -119,17 +121,22 @@ class MANAGED VarHandle : public Object { // AccessMode. No check is made for whether the AccessMode is a // supported operation so the MethodType can be used when raising a // WrongMethodTypeException exception. - MethodType* GetMethodTypeForAccessMode(Thread* self, AccessMode accessMode) + ObjPtr GetMethodTypeForAccessMode(Thread* self, AccessMode accessMode) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Returns a string representing the descriptor of the MethodType associated with + // this AccessMode. + std::string PrettyDescriptorForAccessMode(AccessMode access_mode) REQUIRES_SHARED(Locks::mutator_lock_); bool Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); // Gets the variable type that is operated on by this VarHandle instance. - Class* GetVarType() REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr GetVarType() REQUIRES_SHARED(Locks::mutator_lock_); // Gets the return type descriptor for a named accessor method, // nullptr if accessor_method is not supported. @@ -142,19 +149,14 @@ class MANAGED VarHandle : public Object { // VarHandle access method, such as "setOpaque". Returns false otherwise. static bool GetAccessModeByMethodName(const char* method_name, AccessMode* access_mode); - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: - Class* GetCoordinateType0() REQUIRES_SHARED(Locks::mutator_lock_); - Class* GetCoordinateType1() REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr GetCoordinateType0() REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr GetCoordinateType1() REQUIRES_SHARED(Locks::mutator_lock_); int32_t GetAccessModesBitMask() REQUIRES_SHARED(Locks::mutator_lock_); - static MethodType* GetMethodTypeForAccessMode(Thread* self, - ObjPtr var_handle, - AccessMode access_mode) + static ObjPtr GetMethodTypeForAccessMode(Thread* self, + ObjPtr var_handle, + AccessMode access_mode) REQUIRES_SHARED(Locks::mutator_lock_); static MemberOffset VarTypeOffset() { @@ -178,9 +180,6 @@ class MANAGED VarHandle : public Object { HeapReference var_type_; int32_t access_modes_bit_mask_; - // Root representing java.lang.invoke.VarHandle.class. - static GcRoot static_class_; - friend class VarHandleTest; // for testing purposes friend struct art::VarHandleOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(VarHandle); @@ -192,7 +191,7 @@ class MANAGED FieldVarHandle : public VarHandle { public: bool Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); @@ -211,9 +210,6 @@ class MANAGED FieldVarHandle : public VarHandle { // ArtField instance corresponding to variable for accessors. int64_t art_field_; - // Root representing java.lang.invoke.FieldVarHandle.class. - static GcRoot static_class_; - friend class VarHandleTest; // for var_handle_test. friend struct art::FieldVarHandleOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(FieldVarHandle); @@ -225,19 +221,11 @@ class MANAGED ArrayElementVarHandle : public VarHandle { public: bool Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: - // Root representing java.lang.invoke.ArrayElementVarHandle.class. - static GcRoot static_class_; - friend class VarHandleTest; DISALLOW_IMPLICIT_CONSTRUCTORS(ArrayElementVarHandle); }; @@ -248,17 +236,12 @@ class MANAGED ByteArrayViewVarHandle : public VarHandle { public: bool Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); bool GetNativeByteOrder() REQUIRES_SHARED(Locks::mutator_lock_); - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: static MemberOffset NativeByteOrderOffset() { return MemberOffset(OFFSETOF_MEMBER(ByteArrayViewVarHandle, native_byte_order_)); @@ -267,9 +250,6 @@ class MANAGED ByteArrayViewVarHandle : public VarHandle { // Flag indicating that accessors should use native byte-ordering. uint8_t native_byte_order_; - // Root representing java.lang.invoke.ByteArrayViewVarHandle.class. - static GcRoot static_class_; - friend class VarHandleTest; // for var_handle_test. friend struct art::ByteArrayViewVarHandleOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(ByteArrayViewVarHandle); @@ -281,17 +261,12 @@ class MANAGED ByteBufferViewVarHandle : public VarHandle { public: bool Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); bool GetNativeByteOrder() REQUIRES_SHARED(Locks::mutator_lock_); - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: bool AccessHeapBuffer(AccessMode access_mode, ObjPtr byte_buffer, @@ -315,9 +290,6 @@ class MANAGED ByteBufferViewVarHandle : public VarHandle { // Flag indicating that accessors should use native byte-ordering. uint8_t native_byte_order_; - // Root representing java.lang.invoke.ByteBufferViewVarHandle.class. - static GcRoot static_class_; - friend class VarHandleTest; // for var_handle_test. friend struct art::ByteBufferViewVarHandleOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(ByteBufferViewVarHandle); diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc index d9fa07f207aee5c2ebe5ebd90d46e7091368a141..9df96ddbd14e401cf3872639c980444fb3573037 100644 --- a/runtime/mirror/var_handle_test.cc +++ b/runtime/mirror/var_handle_test.cc @@ -23,6 +23,7 @@ #include "class-inl.h" #include "class_linker-inl.h" #include "class_loader.h" +#include "class_root.h" #include "common_runtime_test.h" #include "handle_scope-inl.h" #include "jvalue-inl.h" @@ -43,7 +44,7 @@ class VarHandleTest : public CommonRuntimeTest { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) { StackHandleScope<4> hs(self); Handle fvh = hs.NewHandle( - ObjPtr::DownCast(FieldVarHandle::StaticClass()->AllocObject(self))); + ObjPtr::DownCast(GetClassRoot()->AllocObject(self))); Handle var_type = hs.NewHandle(art_field->ResolveType()); if (art_field->IsStatic()) { @@ -67,7 +68,7 @@ class VarHandleTest : public CommonRuntimeTest { StackHandleScope<3> hs(self); Handle vh = hs.NewHandle( ObjPtr::DownCast( - ArrayElementVarHandle::StaticClass()->AllocObject(self))); + GetClassRoot()->AllocObject(self))); // Initialize super class fields ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); @@ -85,14 +86,13 @@ class VarHandleTest : public CommonRuntimeTest { StackHandleScope<4> hs(self); Handle bvh = hs.NewHandle( ObjPtr::DownCast( - ByteArrayViewVarHandle::StaticClass()->AllocObject(self))); + GetClassRoot()->AllocObject(self))); // Initialize super class fields ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle var_type = hs.NewHandle(view_array_class->GetComponentType()); Handle index_type = hs.NewHandle(class_linker->FindPrimitiveClass('I')); - ObjPtr byte_class = class_linker->FindPrimitiveClass('B'); - Handle byte_array_class(hs.NewHandle(class_linker->FindArrayClass(self, &byte_class))); + Handle byte_array_class(hs.NewHandle(GetClassRoot())); InitializeVarHandle(bvh.Get(), var_type, byte_array_class, index_type, access_modes_bit_mask); bvh->SetFieldBoolean(ByteArrayViewVarHandle::NativeByteOrderOffset(), native_byte_order); return bvh.Get(); @@ -106,7 +106,7 @@ class VarHandleTest : public CommonRuntimeTest { StackHandleScope<5> hs(self); Handle bvh = hs.NewHandle( ObjPtr::DownCast( - ByteArrayViewVarHandle::StaticClass()->AllocObject(self))); + GetClassRoot()->AllocObject(self))); // Initialize super class fields ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle var_type = hs.NewHandle(view_array_class->GetComponentType()); @@ -130,17 +130,17 @@ class VarHandleTest : public CommonRuntimeTest { } // Helper to get the VarType of a VarHandle. - static Class* GetVarType(VarHandle* vh) REQUIRES_SHARED(Locks::mutator_lock_) { + static ObjPtr GetVarType(VarHandle* vh) REQUIRES_SHARED(Locks::mutator_lock_) { return vh->GetVarType(); } // Helper to get the CoordinateType0 of a VarHandle. - static Class* GetCoordinateType0(VarHandle* vh) REQUIRES_SHARED(Locks::mutator_lock_) { + static ObjPtr GetCoordinateType0(VarHandle* vh) REQUIRES_SHARED(Locks::mutator_lock_) { return vh->GetCoordinateType0(); } // Helper to get the CoordinateType1 of a VarHandle. - static Class* GetCoordinateType1(VarHandle* vh) REQUIRES_SHARED(Locks::mutator_lock_) { + static ObjPtr GetCoordinateType1(VarHandle* vh) REQUIRES_SHARED(Locks::mutator_lock_) { return vh->GetCoordinateType1(); } @@ -150,7 +150,7 @@ class VarHandleTest : public CommonRuntimeTest { } private: - static void InitializeVarHandle(VarHandle* vh, + static void InitializeVarHandle(ObjPtr vh, Handle var_type, int32_t access_modes_bit_mask) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -158,7 +158,7 @@ class VarHandleTest : public CommonRuntimeTest { vh->SetField32(VarHandle::AccessModesBitMaskOffset(), access_modes_bit_mask); } - static void InitializeVarHandle(VarHandle* vh, + static void InitializeVarHandle(ObjPtr vh, Handle var_type, Handle coordinate_type0, int32_t access_modes_bit_mask) @@ -167,7 +167,7 @@ class VarHandleTest : public CommonRuntimeTest { vh->SetFieldObject(VarHandle::CoordinateType0Offset(), coordinate_type0.Get()); } - static void InitializeVarHandle(VarHandle* vh, + static void InitializeVarHandle(ObjPtr vh, Handle var_type, Handle coordinate_type0, Handle coordinate_type1, @@ -233,8 +233,7 @@ static MethodType* MethodTypeOf(const std::string& method_descriptor) { ScopedObjectAccess soa(self); StackHandleScope<3> hs(self); int ptypes_count = static_cast(descriptors.size()) - 1; - ObjPtr class_type = mirror::Class::GetJavaLangClass(); - ObjPtr array_of_class = class_linker->FindArrayClass(self, &class_type); + ObjPtr array_of_class = GetClassRoot>(); Handle> ptypes = hs.NewHandle( ObjectArray::Alloc(Thread::Current(), array_of_class, ptypes_count)); Handle boot_class_loader = hs.NewHandle(nullptr); @@ -246,6 +245,47 @@ static MethodType* MethodTypeOf(const std::string& method_descriptor) { return MethodType::Create(self, rtype, ptypes); } +static bool AccessModeMatch(VarHandle* vh, + VarHandle::AccessMode access_mode, + MethodType* method_type, + VarHandle::MatchKind expected_match) + REQUIRES_SHARED(Locks::mutator_lock_) { + return vh->GetMethodTypeMatchForAccessMode(access_mode, method_type) == expected_match; +} + +template +static bool AccessModeExactMatch(Handle vh, + VarHandle::AccessMode access_mode, + const char* descriptor) + REQUIRES_SHARED(Locks::mutator_lock_) { + return AccessModeMatch(vh.Get(), + access_mode, + MethodTypeOf(descriptor), + VarHandle::MatchKind::kExact); +} + +template +static bool AccessModeWithConversionsMatch(Handle vh, + VarHandle::AccessMode access_mode, + const char* descriptor) + REQUIRES_SHARED(Locks::mutator_lock_) { + return AccessModeMatch(vh.Get(), + access_mode, + MethodTypeOf(descriptor), + VarHandle::MatchKind::kWithConversions); +} + +template +static bool AccessModeNoMatch(Handle vh, + VarHandle::AccessMode access_mode, + const char* descriptor) + REQUIRES_SHARED(Locks::mutator_lock_) { + return AccessModeMatch(vh.Get(), + access_mode, + MethodTypeOf(descriptor), + VarHandle::MatchKind::kNone); +} + TEST_F(VarHandleTest, InstanceFieldVarHandle) { Thread * const self = Thread::Current(); ScopedObjectAccess soa(self); @@ -296,47 +336,53 @@ TEST_F(VarHandleTest, InstanceFieldVarHandle) { // Check compatibility - "Get" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)I"))); - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;)I")); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;)V")); + EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;)D")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)Z")); } // Check compatibility - "Set" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;I)V")); + EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;S)V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndSet" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, - MethodTypeOf("(Ljava/lang/Integer;II)I"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)Z")); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)V")); + EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;II)Ljava/lang/Boolean;")); + EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;IB)V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;II)I")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndExchange" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)I"))); - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(IIII)V"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)I")); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)V")); + EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;II)J")); + EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;BS)F")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;I)Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(IIII)V")); } // Check compatibility - "GetAndUpdate" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)I"))); - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)S"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;I)I")); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;I)V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;I)Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)S")); } // Check synthesized method types match expected forms. @@ -430,48 +476,47 @@ TEST_F(VarHandleTest, StaticFieldVarHandle) { // Check compatibility - "Get" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()I"))); - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "()I")); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "()V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)Z")); } // Check compatibility - "Set" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(F)V"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(I)V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(F)V")); } // Check compatibility - "CompareAndSet" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, - MethodTypeOf("(II)Ljava/lang/String;"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(II)Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)Ljava/lang/String;")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndExchange" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)I"))); - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(ID)I"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)S"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(IIJ)V"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(II)I")); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(II)V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(ID)I")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)S")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(IIJ)V")); } // Check compatibility - "GetAndUpdate" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)I"))); - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(I)I")); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(I)V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(I)Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)V")); } // Check synthesized method types match expected forms. @@ -552,10 +597,10 @@ TEST_F(VarHandleTest, ArrayElementVarHandle) { VarHandle::AccessMode::kGetAndBitwiseXorRelease, VarHandle::AccessMode::kGetAndBitwiseXorAcquire); - ObjPtr string_class = mirror::String::GetJavaLangString(); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - Handle string_array_class(hs.NewHandle(class_linker->FindArrayClass(self, &string_class))); - Handle vh(hs.NewHandle(CreateArrayElementVarHandle(self, string_array_class, mask))); + Handle string_array_class = hs.NewHandle( + GetClassRoot>()); + Handle vh( + hs.NewHandle(CreateArrayElementVarHandle(self, string_array_class, mask))); EXPECT_FALSE(vh.IsNull()); // Check access modes @@ -594,50 +639,46 @@ TEST_F(VarHandleTest, ArrayElementVarHandle) { // Check compatibility - "Get" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Ljava/lang/String;"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;Ljava/lang/String;)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;I)Ljava/lang/String;")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;I)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;Ljava/lang/String;)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)Z")); } // Check compatibility - "Set" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;I)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;I)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndSet" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet; - EXPECT_TRUE( - vh->IsMethodTypeCompatible( - access_mode, - MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, - MethodTypeOf("([Ljava/lang/String;III)I"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;III)I")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;I)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndExchange" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;II)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;II)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(III)V")); } // Check compatibility - "GetAndUpdate" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String;"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String;")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(II)V")); } // Check synthesized method types match expected forms. @@ -703,11 +744,10 @@ TEST_F(VarHandleTest, ByteArrayViewVarHandle) { VarHandle::AccessMode::kGetAndBitwiseXor, VarHandle::AccessMode::kGetAndBitwiseXorAcquire); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - ObjPtr char_class = class_linker->FindPrimitiveClass('C'); - Handle char_array_class(hs.NewHandle(class_linker->FindArrayClass(self, &char_class))); + Handle char_array_class(hs.NewHandle(GetClassRoot())); const bool native_byte_order = true; - Handle vh(hs.NewHandle(CreateByteArrayViewVarHandle(self, char_array_class, native_byte_order, mask))); + Handle vh( + hs.NewHandle(CreateByteArrayViewVarHandle(self, char_array_class, native_byte_order, mask))); EXPECT_FALSE(vh.IsNull()); EXPECT_EQ(native_byte_order, vh->GetNativeByteOrder()); @@ -747,50 +787,46 @@ TEST_F(VarHandleTest, ByteArrayViewVarHandle) { // Check compatibility - "Get" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)C"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BC)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BI)C")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BI)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BC)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)Z")); } // Check compatibility - "Set" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BIC)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BI)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BI)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndSet" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet; - EXPECT_TRUE( - vh->IsMethodTypeCompatible( - access_mode, - MethodTypeOf("([BICC)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, - MethodTypeOf("([BIII)I"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BICC)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BIII)I")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BI)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndExchange" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BICC)C"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BICC)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BII)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BICC)C")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BICC)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BII)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(III)V")); } // Check compatibility - "GetAndUpdate" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)C"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BIC)C")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BIC)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BIC)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(II)V")); } // Check synthesized method types match expected forms. @@ -856,11 +892,10 @@ TEST_F(VarHandleTest, ByteBufferViewVarHandle) { VarHandle::AccessMode::kGetAndBitwiseXor, VarHandle::AccessMode::kGetAndBitwiseXorAcquire); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - ObjPtr double_class = class_linker->FindPrimitiveClass('D'); - Handle double_array_class(hs.NewHandle(class_linker->FindArrayClass(self, &double_class))); + Handle double_array_class(hs.NewHandle(GetClassRoot())); const bool native_byte_order = false; - Handle vh(hs.NewHandle(CreateByteBufferViewVarHandle(self, double_array_class, native_byte_order, mask))); + Handle vh(hs.NewHandle( + CreateByteBufferViewVarHandle(self, double_array_class, native_byte_order, mask))); EXPECT_FALSE(vh.IsNull()); EXPECT_EQ(native_byte_order, vh->GetNativeByteOrder()); @@ -900,50 +935,46 @@ TEST_F(VarHandleTest, ByteBufferViewVarHandle) { // Check compatibility - "Get" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)D"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;D)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)D")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;D)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)Z")); } // Check compatibility - "Set" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndSet" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet; - EXPECT_TRUE( - vh->IsMethodTypeCompatible( - access_mode, - MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, - MethodTypeOf("(Ljava/nio/ByteBuffer;IDI)D"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDD)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDI)D")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndExchange" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)D"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;II)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDD)D")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDD)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;II)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(III)V")); } // Check compatibility - "GetAndUpdate" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)D"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)D")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(II)V")); } // Check synthesized method types match expected forms. diff --git a/runtime/monitor.cc b/runtime/monitor.cc index 2a938da15bd780d924c7cb0abc71e8cc97f2a7b8..d47bc0d12ec2cbdf18acc189178ef6808b363965 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -59,8 +59,8 @@ static constexpr uint64_t kLongWaitMs = 100 * kDebugThresholdFudgeFactor; * though, because we have a full 32 bits to work with. * * The two states of an Object's lock are referred to as "thin" and "fat". A lock may transition - * from the "thin" state to the "fat" state and this transition is referred to as inflation. Once - * a lock has been inflated it remains in the "fat" state indefinitely. + * from the "thin" state to the "fat" state and this transition is referred to as inflation. We + * deflate locks from time to time as part of heap trimming. * * The lock value itself is stored in mirror::Object::monitor_ and the representation is described * in the LockWord value type. @@ -134,13 +134,15 @@ Monitor::Monitor(Thread* self, Thread* owner, mirror::Object* obj, int32_t hash_ } int32_t Monitor::GetHashCode() { - while (!HasHashCode()) { - if (hash_code_.CompareAndSetWeakRelaxed(0, mirror::Object::GenerateIdentityHashCode())) { - break; - } + int32_t hc = hash_code_.load(std::memory_order_relaxed); + if (!HasHashCode()) { + // Use a strong CAS to prevent spurious failures since these can make the boot image + // non-deterministic. + hash_code_.CompareAndSetStrongRelaxed(0, mirror::Object::GenerateIdentityHashCode()); + hc = hash_code_.load(std::memory_order_relaxed); } DCHECK(HasHashCode()); - return hash_code_.LoadRelaxed(); + return hc; } bool Monitor::Install(Thread* self) { @@ -155,7 +157,7 @@ bool Monitor::Install(Thread* self) { break; } case LockWord::kHashCode: { - CHECK_EQ(hash_code_.LoadRelaxed(), static_cast(lw.GetHashCode())); + CHECK_EQ(hash_code_.load(std::memory_order_relaxed), static_cast(lw.GetHashCode())); break; } case LockWord::kFatLocked: { @@ -173,7 +175,7 @@ bool Monitor::Install(Thread* self) { } LockWord fat(this, lw.GCState()); // Publish the updated lock word, which may race with other threads. - bool success = GetObject()->CasLockWordWeakRelease(lw, fat); + bool success = GetObject()->CasLockWord(lw, fat, CASMode::kWeak, std::memory_order_release); // Lock profiling. if (success && owner_ != nullptr && lock_profiling_threshold_ != 0) { // Do not abort on dex pc errors. This can easily happen when we want to dump a stack trace on @@ -1039,7 +1041,7 @@ mirror::Object* Monitor::MonitorEnter(Thread* self, mirror::Object* obj, bool tr case LockWord::kUnlocked: { // No ordering required for preceding lockword read, since we retest. LockWord thin_locked(LockWord::FromThinLockId(thread_id, 0, lock_word.GCState())); - if (h_obj->CasLockWordWeakAcquire(lock_word, thin_locked)) { + if (h_obj->CasLockWord(lock_word, thin_locked, CASMode::kWeak, std::memory_order_acquire)) { AtraceMonitorLock(self, h_obj.Get(), false /* is_wait */); return h_obj.Get(); // Success! } @@ -1063,7 +1065,10 @@ mirror::Object* Monitor::MonitorEnter(Thread* self, mirror::Object* obj, bool tr return h_obj.Get(); // Success! } else { // Use CAS to preserve the read barrier state. - if (h_obj->CasLockWordWeakRelaxed(lock_word, thin_locked)) { + if (h_obj->CasLockWord(lock_word, + thin_locked, + CASMode::kWeak, + std::memory_order_relaxed)) { AtraceMonitorLock(self, h_obj.Get(), false /* is_wait */); return h_obj.Get(); // Success! } @@ -1101,7 +1106,7 @@ mirror::Object* Monitor::MonitorEnter(Thread* self, mirror::Object* obj, bool tr case LockWord::kFatLocked: { // We should have done an acquire read of the lockword initially, to ensure // visibility of the monitor data structure. Use an explicit fence instead. - QuasiAtomic::ThreadFenceAcquire(); + std::atomic_thread_fence(std::memory_order_acquire); Monitor* mon = lock_word.FatLockMonitor(); if (trylock) { return mon->TryLock(self) ? h_obj.Get() : nullptr; @@ -1165,7 +1170,7 @@ bool Monitor::MonitorExit(Thread* self, mirror::Object* obj) { return true; } else { // Use CAS to preserve the read barrier state. - if (h_obj->CasLockWordWeakRelease(lock_word, new_lw)) { + if (h_obj->CasLockWord(lock_word, new_lw, CASMode::kWeak, std::memory_order_release)) { AtraceMonitorUnlock(); // Success! return true; diff --git a/runtime/monitor.h b/runtime/monitor.h index 384ebbedaa76a331fe1aef2c4fcf989cde0cea2d..6b7604ec8a86df27641d3496bd06b1c6762bb0dd 100644 --- a/runtime/monitor.h +++ b/runtime/monitor.h @@ -130,7 +130,7 @@ class Monitor { bool IsLocked() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!monitor_lock_); bool HasHashCode() const { - return hash_code_.LoadRelaxed() != 0; + return hash_code_.load(std::memory_order_relaxed) != 0; } MonitorId GetMonitorId() const { diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 637e7facaf2a4f6f3d842bd356147c493752ae11..cdba6b204fab09559ccfcd6140a1738ec7ea78cf 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -25,6 +25,7 @@ #include "base/os.h" #include "base/stl_util.h" #include "base/utils.h" +#include "base/zip_archive.h" #include "class_linker.h" #include #include "common_throws.h" @@ -34,7 +35,7 @@ #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" #include "jit/debugger_interface.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" #include "mirror/string.h" @@ -48,7 +49,6 @@ #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "well_known_classes.h" -#include "zip_archive.h" namespace art { diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index 0955e86c4a04b1bd31b3fc6c562a44cbe3a882f1..f1e267beccec97031ed662e475e3ab08e5975fd6 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -35,8 +35,8 @@ #include "gc/space/zygote_space.h" #include "handle_scope-inl.h" #include "hprof/hprof.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/object_array-inl.h" #include "native_util.h" @@ -89,17 +89,27 @@ static void VMDebug_resetAllocCount(JNIEnv*, jclass, jint kinds) { static void VMDebug_startMethodTracingDdmsImpl(JNIEnv*, jclass, jint bufferSize, jint flags, jboolean samplingEnabled, jint intervalUs) { - Trace::Start("[DDMS]", -1, bufferSize, flags, Trace::TraceOutputMode::kDDMS, - samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, - intervalUs); -} - -static void VMDebug_startMethodTracingFd(JNIEnv* env, jclass, jstring javaTraceFilename, - jint javaFd, jint bufferSize, jint flags, - jboolean samplingEnabled, jint intervalUs, + Trace::StartDDMS(bufferSize, + flags, + samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, + intervalUs); +} + +static void VMDebug_startMethodTracingFd(JNIEnv* env, + jclass, + jstring javaTraceFilename ATTRIBUTE_UNUSED, + jint javaFd, + jint bufferSize, + jint flags, + jboolean samplingEnabled, + jint intervalUs, jboolean streamingOutput) { int originalFd = javaFd; if (originalFd < 0) { + ScopedObjectAccess soa(env); + soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", + "Trace fd is invalid: %d", + originalFd); return; } @@ -107,18 +117,20 @@ static void VMDebug_startMethodTracingFd(JNIEnv* env, jclass, jstring javaTraceF if (fd < 0) { ScopedObjectAccess soa(env); soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", - "dup(%d) failed: %s", originalFd, strerror(errno)); + "dup(%d) failed: %s", + originalFd, + strerror(errno)); return; } - ScopedUtfChars traceFilename(env, javaTraceFilename); - if (traceFilename.c_str() == nullptr) { - return; - } + // Ignore the traceFilename. Trace::TraceOutputMode outputMode = streamingOutput ? Trace::TraceOutputMode::kStreaming : Trace::TraceOutputMode::kFile; - Trace::Start(traceFilename.c_str(), fd, bufferSize, flags, outputMode, + Trace::Start(fd, + bufferSize, + flags, + outputMode, samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, intervalUs); } @@ -130,7 +142,10 @@ static void VMDebug_startMethodTracingFilename(JNIEnv* env, jclass, jstring java if (traceFilename.c_str() == nullptr) { return; } - Trace::Start(traceFilename.c_str(), -1, bufferSize, flags, Trace::TraceOutputMode::kFile, + Trace::Start(traceFilename.c_str(), + bufferSize, + flags, + Trace::TraceOutputMode::kFile, samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, intervalUs); } diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 12332d2c59661b95899220616f75258b947468eb..9b3fd16ac0e1ddaf5d2ca7d17aeda10b4763b895 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -32,6 +32,7 @@ extern "C" void android_set_application_target_sdk_version(uint32_t version); #include "class_linker-inl.h" #include "common_throws.h" #include "debugger.h" +#include "dex/class_accessor-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_types.h" #include "gc/accounting/card_table-inl.h" @@ -41,8 +42,8 @@ extern "C" void android_set_application_target_sdk_version(uint32_t version); #include "gc/space/image_space.h" #include "gc/task_processor.h" #include "intern_table.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/dex_cache-inl.h" #include "mirror/object-inl.h" @@ -111,7 +112,7 @@ static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaEle } Runtime* runtime = Runtime::Current(); ObjPtr array_class = - runtime->GetClassLinker()->FindArrayClass(soa.Self(), &element_class); + runtime->GetClassLinker()->FindArrayClass(soa.Self(), element_class); if (UNLIKELY(array_class == nullptr)) { return nullptr; } @@ -138,7 +139,7 @@ static jobject VMRuntime_newUnpaddedArray(JNIEnv* env, jobject, jclass javaEleme } Runtime* runtime = Runtime::Current(); ObjPtr array_class = runtime->GetClassLinker()->FindArrayClass(soa.Self(), - &element_class); + element_class); if (UNLIKELY(array_class == nullptr)) { return nullptr; } @@ -573,30 +574,12 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { } if (kPreloadDexCachesFieldsAndMethods) { - for (size_t class_def_index = 0; - class_def_index < dex_file->NumClassDefs(); - class_def_index++) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - continue; + for (ClassAccessor accessor : dex_file->GetClasses()) { + for (const ClassAccessor::Field& field : accessor.GetFields()) { + PreloadDexCachesResolveField(dex_cache, field.GetIndex(), field.IsStatic()); } - ClassDataItemIterator it(*dex_file, class_data); - for (; it.HasNextStaticField(); it.Next()) { - uint32_t field_idx = it.GetMemberIndex(); - PreloadDexCachesResolveField(dex_cache, field_idx, true); - } - for (; it.HasNextInstanceField(); it.Next()) { - uint32_t field_idx = it.GetMemberIndex(); - PreloadDexCachesResolveField(dex_cache, field_idx, false); - } - for (; it.HasNextDirectMethod(); it.Next()) { - uint32_t method_idx = it.GetMemberIndex(); - PreloadDexCachesResolveMethod(dex_cache, method_idx); - } - for (; it.HasNextVirtualMethod(); it.Next()) { - uint32_t method_idx = it.GetMemberIndex(); - PreloadDexCachesResolveMethod(dex_cache, method_idx); + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + PreloadDexCachesResolveMethod(dex_cache, method.GetIndex()); } } } diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc index ed0eb97da166cc12a5f1f31a389eecdf081f76cc..39192274ad96bbe37c424c55494abc4c989e048a 100644 --- a/runtime/native/dalvik_system_VMStack.cc +++ b/runtime/native/dalvik_system_VMStack.cc @@ -22,7 +22,7 @@ #include "art_method-inl.h" #include "gc/task_processor.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index 891cdfab1a46f920c61cd731ce7a6d88129d3f71..5b47eaca868434822f21e9913f4b1d505b0b94a9 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -28,9 +28,9 @@ #include "base/runtime_debug.h" #include "debugger.h" #include "hidden_api.h" -#include "java_vm_ext.h" #include "jit/jit.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "native_util.h" #include "nativehelper/jni_macros.h" #include "nativehelper/scoped_utf_chars.h" @@ -100,7 +100,7 @@ class ClassSet { } void AddClass(ObjPtr klass) REQUIRES(Locks::mutator_lock_) { - class_set_.insert(self_->GetJniEnv()->AddLocalReference(klass.Ptr())); + class_set_.insert(self_->GetJniEnv()->AddLocalReference(klass)); } const std::unordered_set& GetClasses() const { @@ -316,6 +316,8 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, LOG(ERROR) << StringPrintf("Unknown bits set in runtime_flags: %#x", runtime_flags); } + Runtime::Current()->GetHeap()->PostForkChildAction(thread); + // Update tracing. if (Trace::GetMethodTracingMode() != TracingMode::kTracingInactive) { Trace::TraceOutputMode output_mode = Trace::GetOutputMode(); @@ -346,7 +348,6 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, std::string trace_file = StringPrintf("/data/misc/trace/%s.trace.bin", proc_name.c_str()); Trace::Start(trace_file.c_str(), - -1, buffer_size, 0, // TODO: Expose flags. output_mode, diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index c07c32ad1ce1f61d120af96dfd066a93e2311b98..82e54e2f4c5eb36b8e0864c35e2a80482eac25da 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -22,13 +22,14 @@ #include "art_method-inl.h" #include "base/enums.h" #include "class_linker-inl.h" +#include "class_root.h" #include "common_throws.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_annotations.h" #include "dex/utf.h" #include "hidden_api.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/field-inl.h" @@ -82,7 +83,7 @@ static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) // is subject to change so conservatively cover the entire package. // NB Static initializers within java.lang.invoke are permitted and do not // need further stack inspection. - ObjPtr lookup_class = mirror::MethodHandlesLookup::StaticClass(); + ObjPtr lookup_class = GetClassRoot(); if ((declaring_class == lookup_class || declaring_class->IsInSamePackage(lookup_class)) && !m->IsClassInitializer()) { return true; @@ -214,17 +215,9 @@ static jstring Class_getNameNative(JNIEnv* env, jobject javaThis) { return soa.AddLocalReference(mirror::Class::ComputeName(hs.NewHandle(c))); } -// TODO: Move this to mirror::Class ? Other mirror types that commonly appear -// as arrays have a GetArrayClass() method. -static ObjPtr GetClassArrayClass(Thread* self) - REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr class_class = mirror::Class::GetJavaLangClass(); - return Runtime::Current()->GetClassLinker()->FindArrayClass(self, &class_class); -} - static jobjectArray Class_getInterfacesInternal(JNIEnv* env, jobject javaThis) { ScopedFastNativeObjectAccess soa(env); - StackHandleScope<4> hs(soa.Self()); + StackHandleScope<1> hs(soa.Self()); Handle klass = hs.NewHandle(DecodeClass(soa, javaThis)); if (klass->IsProxyClass()) { @@ -236,10 +229,12 @@ static jobjectArray Class_getInterfacesInternal(JNIEnv* env, jobject javaThis) { return nullptr; } + ClassLinker* linker = Runtime::Current()->GetClassLinker(); const uint32_t num_ifaces = iface_list->Size(); - Handle class_array_class = hs.NewHandle(GetClassArrayClass(soa.Self())); - Handle> ifaces = hs.NewHandle( - mirror::ObjectArray::Alloc(soa.Self(), class_array_class.Get(), num_ifaces)); + ObjPtr class_array_class = + GetClassRoot>(linker); + ObjPtr> ifaces = + mirror::ObjectArray::Alloc(soa.Self(), class_array_class, num_ifaces); if (ifaces.IsNull()) { DCHECK(soa.Self()->IsExceptionPending()); return nullptr; @@ -249,20 +244,21 @@ static jobjectArray Class_getInterfacesInternal(JNIEnv* env, jobject javaThis) { // with kActiveTransaction == false. DCHECK(!Runtime::Current()->IsActiveTransaction()); - ClassLinker* linker = Runtime::Current()->GetClassLinker(); - MutableHandle interface(hs.NewHandle(nullptr)); for (uint32_t i = 0; i < num_ifaces; ++i) { const dex::TypeIndex type_idx = iface_list->GetTypeItem(i).type_idx_; - interface.Assign(linker->LookupResolvedType(type_idx, klass.Get())); - ifaces->SetWithoutChecks(i, interface.Get()); + ObjPtr interface = linker->LookupResolvedType(type_idx, klass.Get()); + DCHECK(interface != nullptr); + ifaces->SetWithoutChecks(i, interface); } - return soa.AddLocalReference(ifaces.Get()); + return soa.AddLocalReference(ifaces); } -static mirror::ObjectArray* GetDeclaredFields( - Thread* self, ObjPtr klass, bool public_only, bool force_resolve) - REQUIRES_SHARED(Locks::mutator_lock_) { +static ObjPtr> GetDeclaredFields( + Thread* self, + ObjPtr klass, + bool public_only, + bool force_resolve) REQUIRES_SHARED(Locks::mutator_lock_) { StackHandleScope<1> hs(self); IterationRange> ifields = klass->GetIFields(); IterationRange> sfields = klass->GetSFields(); @@ -281,7 +277,7 @@ static mirror::ObjectArray* GetDeclaredFields( } size_t array_idx = 0; auto object_array = hs.NewHandle(mirror::ObjectArray::Alloc( - self, mirror::Field::ArrayClass(), array_size)); + self, GetClassRoot>(), array_size)); if (object_array == nullptr) { return nullptr; } @@ -538,7 +534,7 @@ static jobjectArray Class_getDeclaredConstructorsInternal( constructor_count += MethodMatchesConstructor(&m, public_only, enforce_hidden_api) ? 1u : 0u; } auto h_constructors = hs.NewHandle(mirror::ObjectArray::Alloc( - soa.Self(), mirror::Constructor::ArrayClass(), constructor_count)); + soa.Self(), GetClassRoot>(), constructor_count)); if (UNLIKELY(h_constructors == nullptr)) { soa.Self()->AssertPendingException(); return nullptr; @@ -597,7 +593,7 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT } } auto ret = hs.NewHandle(mirror::ObjectArray::Alloc( - soa.Self(), mirror::Method::ArrayClass(), num_methods)); + soa.Self(), GetClassRoot>(), num_methods)); if (ret == nullptr) { soa.Self()->AssertPendingOOMException(); return nullptr; @@ -648,10 +644,10 @@ static jobjectArray Class_getDeclaredAnnotations(JNIEnv* env, jobject javaThis) // Return an empty array instead of a null pointer. ObjPtr annotation_array_class = soa.Decode(WellKnownClasses::java_lang_annotation_Annotation__array); - mirror::ObjectArray* empty_array = + ObjPtr> empty_array = mirror::ObjectArray::Alloc(soa.Self(), - annotation_array_class.Ptr(), - 0); + annotation_array_class, + /* length */ 0); return soa.AddLocalReference(empty_array); } return soa.AddLocalReference(annotations::GetAnnotationsForClass(klass)); @@ -661,7 +657,7 @@ static jobjectArray Class_getDeclaredClasses(JNIEnv* env, jobject javaThis) { ScopedFastNativeObjectAccess soa(env); StackHandleScope<1> hs(soa.Self()); Handle klass(hs.NewHandle(DecodeClass(soa, javaThis))); - mirror::ObjectArray* classes = nullptr; + ObjPtr> classes = nullptr; if (!klass->IsProxyClass() && klass->GetDexCache() != nullptr) { classes = annotations::GetDeclaredClasses(klass); } @@ -671,10 +667,8 @@ static jobjectArray Class_getDeclaredClasses(JNIEnv* env, jobject javaThis) { // Pending exception from GetDeclaredClasses. return nullptr; } - ObjPtr class_array_class = GetClassArrayClass(soa.Self()); - if (class_array_class == nullptr) { - return nullptr; - } + ObjPtr class_array_class = GetClassRoot>(); + DCHECK(class_array_class != nullptr); ObjPtr> empty_array = mirror::ObjectArray::Alloc(soa.Self(), class_array_class, 0); return soa.AddLocalReference(empty_array); @@ -701,8 +695,7 @@ static jobject Class_getEnclosingConstructorNative(JNIEnv* env, jobject javaThis } ObjPtr method = annotations::GetEnclosingMethod(klass); if (method != nullptr) { - if (soa.Decode(WellKnownClasses::java_lang_reflect_Constructor) == - method->GetClass()) { + if (GetClassRoot() == method->GetClass()) { return soa.AddLocalReference(method); } } @@ -718,8 +711,7 @@ static jobject Class_getEnclosingMethodNative(JNIEnv* env, jobject javaThis) { } ObjPtr method = annotations::GetEnclosingMethod(klass); if (method != nullptr) { - if (soa.Decode(WellKnownClasses::java_lang_reflect_Method) == - method->GetClass()) { + if (GetClassRoot() == method->GetClass()) { return soa.AddLocalReference(method); } } @@ -740,7 +732,7 @@ static jstring Class_getInnerClassName(JNIEnv* env, jobject javaThis) { if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) { return nullptr; } - mirror::String* class_name = nullptr; + ObjPtr class_name = nullptr; if (!annotations::GetInnerClass(klass, &class_name)) { return nullptr; } @@ -765,7 +757,7 @@ static jboolean Class_isAnonymousClass(JNIEnv* env, jobject javaThis) { if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) { return false; } - mirror::String* class_name = nullptr; + ObjPtr class_name = nullptr; if (!annotations::GetInnerClass(klass, &class_name)) { return false; } diff --git a/runtime/native/java_lang_Object.cc b/runtime/native/java_lang_Object.cc index d52bf0490bf3c479630cc21cfed4499d681af1c3..48540f877d69daf4ca5230e8dc41816df645b2c7 100644 --- a/runtime/native/java_lang_Object.cc +++ b/runtime/native/java_lang_Object.cc @@ -18,7 +18,7 @@ #include "nativehelper/jni_macros.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/object-inl.h" #include "native_util.h" #include "scoped_fast_native_object_access-inl.h" @@ -41,11 +41,6 @@ static void Object_notifyAll(JNIEnv* env, jobject java_this) { soa.Decode(java_this)->NotifyAll(soa.Self()); } -static void Object_wait(JNIEnv* env, jobject java_this) { - ScopedFastNativeObjectAccess soa(env); - soa.Decode(java_this)->Wait(soa.Self()); -} - static void Object_waitJI(JNIEnv* env, jobject java_this, jlong ms, jint ns) { ScopedFastNativeObjectAccess soa(env); soa.Decode(java_this)->Wait(soa.Self(), ms, ns); @@ -61,7 +56,6 @@ static JNINativeMethod gMethods[] = { FAST_NATIVE_METHOD(Object, internalClone, "()Ljava/lang/Object;"), FAST_NATIVE_METHOD(Object, notify, "()V"), FAST_NATIVE_METHOD(Object, notifyAll, "()V"), - OVERLOADED_FAST_NATIVE_METHOD(Object, wait, "()V", wait), OVERLOADED_FAST_NATIVE_METHOD(Object, wait, "(JI)V", waitJI), FAST_NATIVE_METHOD(Object, identityHashCodeNative, "(Ljava/lang/Object;)I"), }; diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc index b5aea7ca7c1b3a34321fa0733eac61d67c2cad42..8976058b53dcc716b76db762052c842464e0d7bc 100644 --- a/runtime/native/java_lang_String.cc +++ b/runtime/native/java_lang_String.cc @@ -19,7 +19,7 @@ #include "nativehelper/jni_macros.h" #include "common_throws.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/array.h" #include "mirror/object-inl.h" #include "mirror/string-inl.h" diff --git a/runtime/native/java_lang_StringFactory.cc b/runtime/native/java_lang_StringFactory.cc index 136a02f8f63ed810808e601a515fd4d39cda7871..3978ca8a3617e57baaa7f0726dec11c8f0f9babb 100644 --- a/runtime/native/java_lang_StringFactory.cc +++ b/runtime/native/java_lang_StringFactory.cc @@ -17,9 +17,9 @@ #include "java_lang_StringFactory.h" #include "common_throws.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/object-inl.h" -#include "mirror/string.h" +#include "mirror/string-inl.h" #include "native_util.h" #include "nativehelper/jni_macros.h" #include "nativehelper/scoped_local_ref.h" diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc index 390f026588a93914b81f6690b74460553f764b7a..2c4184c285b437eeaafb9e497d544983bea0e860 100644 --- a/runtime/native/java_lang_System.cc +++ b/runtime/native/java_lang_System.cc @@ -20,7 +20,7 @@ #include "common_throws.h" #include "gc/accounting/card_table-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/array.h" #include "mirror/class-inl.h" #include "mirror/class.h" diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc index 9a52f7002ba36ad40c492137546acac7bcba50ec..13871f7be7af785704418068b644d2bdd9cb892d 100644 --- a/runtime/native/java_lang_Thread.cc +++ b/runtime/native/java_lang_Thread.cc @@ -17,7 +17,7 @@ #include "java_lang_Thread.h" #include "common_throws.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/object.h" #include "monitor.h" #include "native_util.h" @@ -111,16 +111,15 @@ static jint Thread_nativeGetStatus(JNIEnv* env, jobject java_thread, jboolean ha return -1; // Unreachable. } -static jboolean Thread_nativeHoldsLock(JNIEnv* env, jobject java_thread, jobject java_object) { +static jboolean Thread_holdsLock(JNIEnv* env, jclass, jobject java_object) { ScopedObjectAccess soa(env); ObjPtr object = soa.Decode(java_object); if (object == nullptr) { ThrowNullPointerException("object == null"); return JNI_FALSE; } - MutexLock mu(soa.Self(), *Locks::thread_list_lock_); - Thread* thread = Thread::FromManagedThread(soa, java_thread); - return thread->HoldsLock(object.Ptr()); + Thread* thread = soa.Self(); + return thread->HoldsLock(object); } static void Thread_nativeInterrupt(JNIEnv* env, jobject java_thread) { @@ -200,7 +199,7 @@ static JNINativeMethod gMethods[] = { FAST_NATIVE_METHOD(Thread, isInterrupted, "()Z"), NATIVE_METHOD(Thread, nativeCreate, "(Ljava/lang/Thread;JZ)V"), NATIVE_METHOD(Thread, nativeGetStatus, "(Z)I"), - NATIVE_METHOD(Thread, nativeHoldsLock, "(Ljava/lang/Object;)Z"), + NATIVE_METHOD(Thread, holdsLock, "(Ljava/lang/Object;)Z"), FAST_NATIVE_METHOD(Thread, nativeInterrupt, "()V"), NATIVE_METHOD(Thread, nativeSetName, "(Ljava/lang/String;)V"), NATIVE_METHOD(Thread, nativeSetPriority, "(I)V"), diff --git a/runtime/native/java_lang_Throwable.cc b/runtime/native/java_lang_Throwable.cc index 03b7f9dfba244c01a0615e8c1cdb88049f3d3ddc..b5ef7d807b84e1967d297be7a03f64d457f702f2 100644 --- a/runtime/native/java_lang_Throwable.cc +++ b/runtime/native/java_lang_Throwable.cc @@ -18,7 +18,7 @@ #include "nativehelper/jni_macros.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "native_util.h" #include "scoped_fast_native_object_access-inl.h" #include "thread.h" diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc index 3a0d76032e3b83c922e261f12511e3536c910786..1ad233a6b29c299cd39f96331c63ce00ee0d78f8 100644 --- a/runtime/native/java_lang_VMClassLoader.cc +++ b/runtime/native/java_lang_VMClassLoader.cc @@ -16,10 +16,12 @@ #include "java_lang_VMClassLoader.h" +#include "base/zip_archive.h" #include "class_linker.h" #include "dex/descriptors_names.h" #include "dex/dex_file_loader.h" -#include "jni_internal.h" +#include "dex/utf.h" +#include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" #include "native_util.h" @@ -29,18 +31,17 @@ #include "obj_ptr.h" #include "scoped_fast_native_object_access-inl.h" #include "well_known_classes.h" -#include "zip_archive.h" namespace art { // A class so we can be friends with ClassLinker and access internal methods. class VMClassLoader { public: - static mirror::Class* LookupClass(ClassLinker* cl, - Thread* self, - const char* descriptor, - size_t hash, - ObjPtr class_loader) + static ObjPtr LookupClass(ClassLinker* cl, + Thread* self, + const char* descriptor, + size_t hash, + ObjPtr class_loader) REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { return cl->LookupClass(self, descriptor, hash, class_loader); @@ -85,7 +86,7 @@ static jclass VMClassLoader_findLoadedClass(JNIEnv* env, jclass, jobject javaLoa } // If class is erroneous, throw the earlier failure, wrapped in certain cases. See b/28787733. if (c != nullptr && c->IsErroneous()) { - cl->ThrowEarlierClassFailure(c.Ptr()); + cl->ThrowEarlierClassFailure(c); Thread* self = soa.Self(); ObjPtr iae_class = self->DecodeJObject(WellKnownClasses::java_lang_IllegalAccessError)->AsClass(); diff --git a/runtime/native/java_lang_invoke_MethodHandleImpl.cc b/runtime/native/java_lang_invoke_MethodHandleImpl.cc index 2e3b4d41efa642a91bd8a39651bdbc04f257a10b..1f2bf09f0e6ced62092210beab2cdf0e1437d861 100644 --- a/runtime/native/java_lang_invoke_MethodHandleImpl.cc +++ b/runtime/native/java_lang_invoke_MethodHandleImpl.cc @@ -20,7 +20,7 @@ #include "art_method.h" #include "handle_scope-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/field.h" #include "mirror/method.h" #include "mirror/method_handle_impl.h" diff --git a/runtime/native/java_lang_ref_FinalizerReference.cc b/runtime/native/java_lang_ref_FinalizerReference.cc index 72af5f7ea726c92efd7534211ccafd61f193c25e..c89188c99c21f65b38da0e28c1357ebf5f93447e 100644 --- a/runtime/native/java_lang_ref_FinalizerReference.cc +++ b/runtime/native/java_lang_ref_FinalizerReference.cc @@ -20,7 +20,7 @@ #include "gc/heap.h" #include "gc/reference_processor.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/object-inl.h" #include "mirror/reference-inl.h" #include "native_util.h" diff --git a/runtime/native/java_lang_ref_Reference.cc b/runtime/native/java_lang_ref_Reference.cc index 524a18ca20fe1394da92f49623137bf8bb73b7be..fc018d15c40da5e0a6f3d7d089cbe02ef7d83bf5 100644 --- a/runtime/native/java_lang_ref_Reference.cc +++ b/runtime/native/java_lang_ref_Reference.cc @@ -20,7 +20,7 @@ #include "gc/heap.h" #include "gc/reference_processor.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/object-inl.h" #include "mirror/reference-inl.h" #include "native_util.h" diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc index d28f74158e3331fb7911afcdd988362a2d488efd..452a66dca2c29d89790e8acee01fcecdf43567e1 100644 --- a/runtime/native/java_lang_reflect_Array.cc +++ b/runtime/native/java_lang_reflect_Array.cc @@ -22,7 +22,7 @@ #include "common_throws.h" #include "dex/dex_file-inl.h" #include "handle_scope-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "native_util.h" @@ -44,9 +44,8 @@ static jobject Array_createMultiArray( Primitive::kPrimInt); Handle dimensions_array( hs.NewHandle(ObjPtr::DownCast(dimensions_obj))); - mirror::Array* new_array = mirror::Array::CreateMultiArray(soa.Self(), - element_class, - dimensions_array); + ObjPtr new_array = + mirror::Array::CreateMultiArray(soa.Self(), element_class, dimensions_array); return soa.AddLocalReference(new_array); } @@ -57,16 +56,16 @@ static jobject Array_createObjectArray(JNIEnv* env, jclass, jclass javaElementCl ThrowNegativeArraySizeException(length); return nullptr; } - ObjPtr element_class = soa.Decode(javaElementClass); Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); - ObjPtr array_class = class_linker->FindArrayClass(soa.Self(), &element_class); + ObjPtr array_class = + class_linker->FindArrayClass(soa.Self(), soa.Decode(javaElementClass)); if (UNLIKELY(array_class == nullptr)) { CHECK(soa.Self()->IsExceptionPending()); return nullptr; } DCHECK(array_class->IsObjectArrayClass()); - ObjPtr new_array = mirror::ObjectArray::Alloc( + ObjPtr new_array = mirror::ObjectArray::Alloc( soa.Self(), array_class, length, diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc index 86124388bc9b949220ccd61cb9b5e1b84250aeff..e54674f72b24779d610b989e8931e91c518166be 100644 --- a/runtime/native/java_lang_reflect_Constructor.cc +++ b/runtime/native/java_lang_reflect_Constructor.cc @@ -20,10 +20,10 @@ #include "art_method-inl.h" #include "base/enums.h" -#include "class_linker-inl.h" #include "class_linker.h" +#include "class_root.h" #include "dex/dex_file_annotations.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/method.h" #include "mirror/object-inl.h" @@ -38,16 +38,12 @@ static jobjectArray Constructor_getExceptionTypes(JNIEnv* env, jobject javaMetho ScopedFastNativeObjectAccess soa(env); ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod) ->GetInterfaceMethodIfProxy(kRuntimePointerSize); - mirror::ObjectArray* result_array = + ObjPtr> result_array = annotations::GetExceptionTypesForMethod(method); if (result_array == nullptr) { // Return an empty array instead of a null pointer. - ObjPtr class_class = mirror::Class::GetJavaLangClass(); - ObjPtr class_array_class = - Runtime::Current()->GetClassLinker()->FindArrayClass(soa.Self(), &class_class); - if (class_array_class == nullptr) { - return nullptr; - } + ObjPtr class_array_class = GetClassRoot>(); + DCHECK(class_array_class != nullptr); ObjPtr> empty_array = mirror::ObjectArray::Alloc(soa.Self(), class_array_class, 0); return soa.AddLocalReference(empty_array); @@ -64,6 +60,7 @@ static jobjectArray Constructor_getExceptionTypes(JNIEnv* env, jobject javaMetho static jobject Constructor_newInstance0(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs) { ScopedFastNativeObjectAccess soa(env); ObjPtr m = soa.Decode(javaMethod); + ArtMethod* constructor_art_method = m->GetArtMethod(); StackHandleScope<1> hs(soa.Self()); Handle c(hs.NewHandle(m->GetDeclaringClass())); if (UNLIKELY(c->IsAbstract())) { @@ -104,18 +101,20 @@ static jobject Constructor_newInstance0(JNIEnv* env, jobject javaMethod, jobject } // String constructor is replaced by a StringFactory method in InvokeMethod. - if (c->IsStringClass()) { + if (UNLIKELY(c->IsStringClass())) { return InvokeMethod(soa, javaMethod, nullptr, javaArgs, 2); } ObjPtr receiver = movable ? c->AllocObject(soa.Self()) : c->AllocNonMovableObject(soa.Self()); - if (receiver == nullptr) { + if (UNLIKELY(receiver == nullptr)) { + DCHECK(soa.Self()->IsExceptionPending()); return nullptr; } jobject javaReceiver = soa.AddLocalReference(receiver); - InvokeMethod(soa, javaMethod, javaReceiver, javaArgs, 2); - // Constructors are ()V methods, so we shouldn't touch the result of InvokeMethod. + + InvokeConstructor(soa, constructor_art_method, receiver, javaArgs); + return javaReceiver; } diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc index b129c6675978ebe0ff53e5cd7e1ec8569170aa21..a10db9115f892d009ca840fa05022427ab7b9cf7 100644 --- a/runtime/native/java_lang_reflect_Executable.cc +++ b/runtime/native/java_lang_reflect_Executable.cc @@ -20,9 +20,10 @@ #include "nativehelper/jni_macros.h" #include "art_method-inl.h" +#include "class_root.h" #include "dex/dex_file_annotations.h" #include "handle.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/method.h" #include "mirror/object-inl.h" @@ -319,7 +320,7 @@ static jstring Executable_getMethodNameInternal(JNIEnv* env, jobject javaMethod) ScopedFastNativeObjectAccess soa(env); ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod); method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize); - return soa.AddLocalReference(method->GetNameAsString(soa.Self())); + return soa.AddLocalReference(method->ResolveNameString()); } static jclass Executable_getMethodReturnTypeInternal(JNIEnv* env, jobject javaMethod) { @@ -335,15 +336,6 @@ static jclass Executable_getMethodReturnTypeInternal(JNIEnv* env, jobject javaMe return soa.AddLocalReference(return_type); } -// TODO: Move this to mirror::Class ? Other mirror types that commonly appear -// as arrays have a GetArrayClass() method. This is duplicated in -// java_lang_Class.cc as well. -static ObjPtr GetClassArrayClass(Thread* self) - REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr class_class = mirror::Class::GetJavaLangClass(); - return Runtime::Current()->GetClassLinker()->FindArrayClass(self, &class_class); -} - static jobjectArray Executable_getParameterTypesInternal(JNIEnv* env, jobject javaMethod) { ScopedFastNativeObjectAccess soa(env); ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod); @@ -356,10 +348,10 @@ static jobjectArray Executable_getParameterTypesInternal(JNIEnv* env, jobject ja const uint32_t num_params = params->Size(); - StackHandleScope<3> hs(soa.Self()); - Handle class_array_class = hs.NewHandle(GetClassArrayClass(soa.Self())); + StackHandleScope<2> hs(soa.Self()); + ObjPtr class_array_class = GetClassRoot>(); Handle> ptypes = hs.NewHandle( - mirror::ObjectArray::Alloc(soa.Self(), class_array_class.Get(), num_params)); + mirror::ObjectArray::Alloc(soa.Self(), class_array_class, num_params)); if (ptypes.IsNull()) { DCHECK(soa.Self()->IsExceptionPending()); return nullptr; diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index 13275d92e4c29095a7c10271642b7a2d666fb1f1..895b2f9fd7fcb7e907666bef3afc44cee031703b 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -26,7 +26,8 @@ #include "common_throws.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_annotations.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" +#include "jvalue-inl.h" #include "mirror/class-inl.h" #include "mirror/field-inl.h" #include "native_util.h" @@ -463,8 +464,7 @@ static jlong Field_getArtField(JNIEnv* env, jobject javaField) { static jstring Field_getNameInternal(JNIEnv* env, jobject javaField) { ScopedFastNativeObjectAccess soa(env); ArtField* field = soa.Decode(javaField)->GetArtField(); - return soa.AddLocalReference( - field->GetStringName(soa.Self(), true /* resolve */)); + return soa.AddLocalReference(field->ResolveNameString()); } static jobjectArray Field_getDeclaredAnnotations(JNIEnv* env, jobject javaField) { @@ -475,7 +475,7 @@ static jobjectArray Field_getDeclaredAnnotations(JNIEnv* env, jobject javaField) ObjPtr annotation_array_class = soa.Decode(WellKnownClasses::java_lang_annotation_Annotation__array); ObjPtr> empty_array = - mirror::ObjectArray::Alloc(soa.Self(), annotation_array_class.Ptr(), 0); + mirror::ObjectArray::Alloc(soa.Self(), annotation_array_class, 0); return soa.AddLocalReference(empty_array); } return soa.AddLocalReference(annotations::GetAnnotationsForField(field)); diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc index 4355c06acd73417e742f6c7d386a0214c817cfcb..87fda6bf5de361e8e53142445ddd69e9258d1065 100644 --- a/runtime/native/java_lang_reflect_Method.cc +++ b/runtime/native/java_lang_reflect_Method.cc @@ -22,8 +22,9 @@ #include "base/enums.h" #include "class_linker-inl.h" #include "class_linker.h" +#include "class_root.h" #include "dex/dex_file_annotations.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" @@ -62,17 +63,13 @@ static jobjectArray Method_getExceptionTypes(JNIEnv* env, jobject javaMethod) { klass->GetProxyThrows()->Get(throws_index); return soa.AddLocalReference(declared_exceptions->Clone(soa.Self())); } else { - mirror::ObjectArray* result_array = + ObjPtr> result_array = annotations::GetExceptionTypesForMethod(method); if (result_array == nullptr) { // Return an empty array instead of a null pointer - ObjPtr class_class = mirror::Class::GetJavaLangClass(); - ObjPtr class_array_class = - Runtime::Current()->GetClassLinker()->FindArrayClass(soa.Self(), &class_class); - if (class_array_class == nullptr) { - return nullptr; - } - mirror::ObjectArray* empty_array = + ObjPtr class_array_class = GetClassRoot>(); + DCHECK(class_array_class != nullptr); + ObjPtr> empty_array = mirror::ObjectArray::Alloc(soa.Self(), class_array_class, 0); return soa.AddLocalReference(empty_array); } else { diff --git a/runtime/native/java_lang_reflect_Parameter.cc b/runtime/native/java_lang_reflect_Parameter.cc index b80b20cd8d3cad972a4e708f2e7521eb26f5a5e7..263a56796ff6f95988fe5c33abf285b29d049f94 100644 --- a/runtime/native/java_lang_reflect_Parameter.cc +++ b/runtime/native/java_lang_reflect_Parameter.cc @@ -24,7 +24,7 @@ #include "common_throws.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_annotations.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "native_util.h" #include "scoped_fast_native_object_access-inl.h" diff --git a/runtime/native/java_lang_reflect_Proxy.cc b/runtime/native/java_lang_reflect_Proxy.cc index 691ed28b0b5754ec297a497927f0c6a8009a05da..f723ed223d4bb779e4d84bd12be03bb38799b257 100644 --- a/runtime/native/java_lang_reflect_Proxy.cc +++ b/runtime/native/java_lang_reflect_Proxy.cc @@ -19,7 +19,7 @@ #include "nativehelper/jni_macros.h" #include "class_linker.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "mirror/object_array.h" #include "mirror/string.h" diff --git a/runtime/native/java_util_concurrent_atomic_AtomicLong.cc b/runtime/native/java_util_concurrent_atomic_AtomicLong.cc index c0032975ce476cfbbcface217e91db617292fd76..fa288edcb8f7d8d6ecc48bb646f61d86e4f79928 100644 --- a/runtime/native/java_util_concurrent_atomic_AtomicLong.cc +++ b/runtime/native/java_util_concurrent_atomic_AtomicLong.cc @@ -21,7 +21,7 @@ #include "arch/instruction_set.h" #include "base/atomic.h" #include "base/quasi_atomic.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "native_util.h" namespace art { diff --git a/runtime/native/libcore_util_CharsetUtils.cc b/runtime/native/libcore_util_CharsetUtils.cc index f3aba2575be81d3722264847e3fd3c85bbeec9ba..24298049ee2d4af6c94b064dc01f881e934797a7 100644 --- a/runtime/native/libcore_util_CharsetUtils.cc +++ b/runtime/native/libcore_util_CharsetUtils.cc @@ -18,7 +18,7 @@ #include -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/string-inl.h" #include "mirror/string.h" #include "native_util.h" diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc index 8f8fd71727de080d82733019b89bdc449d051913..419aed85787f1c9ee3f6fa65a3df7178e40772ca 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc @@ -20,7 +20,7 @@ #include "base/array_ref.h" #include "debugger.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "native_util.h" #include "nativehelper/jni_macros.h" #include "nativehelper/scoped_primitive_array.h" diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc index fbee7b31a3586d3987d820e1fdc30bcd16345ebe..028675d44811f382f662086f38eaeeacc0b64466 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc @@ -22,7 +22,7 @@ #include "base/mutex.h" #include "debugger.h" #include "gc/heap.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "native_util.h" #include "nativehelper/jni_macros.h" #include "nativehelper/scoped_local_ref.h" diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc index 25f984f6be44d91a1dfcb58074a77b153e93fc3b..46444808d7baaee7a727995aba2d3af9434a1bf2 100644 --- a/runtime/native/sun_misc_Unsafe.cc +++ b/runtime/native/sun_misc_Unsafe.cc @@ -27,7 +27,7 @@ #include "base/quasi_atomic.h" #include "common_throws.h" #include "gc/accounting/card_table-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/array.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" @@ -41,9 +41,11 @@ static jboolean Unsafe_compareAndSwapInt(JNIEnv* env, jobject, jobject javaObj, ScopedFastNativeObjectAccess soa(env); ObjPtr obj = soa.Decode(javaObj); // JNI must use non transactional mode. - bool success = obj->CasFieldStrongSequentiallyConsistent32(MemberOffset(offset), - expectedValue, - newValue); + bool success = obj->CasField32(MemberOffset(offset), + expectedValue, + newValue, + CASMode::kStrong, + std::memory_order_seq_cst); return success ? JNI_TRUE : JNI_FALSE; } @@ -78,9 +80,11 @@ static jboolean Unsafe_compareAndSwapObject(JNIEnv* env, jobject, jobject javaOb MemberOffset(offset), field_addr); } - bool success = obj->CasFieldStrongSequentiallyConsistentObject(MemberOffset(offset), - expectedValue, - newValue); + bool success = obj->CasFieldObject(MemberOffset(offset), + expectedValue, + newValue, + CASMode::kStrong, + std::memory_order_seq_cst); return success ? JNI_TRUE : JNI_FALSE; } @@ -116,7 +120,7 @@ static void Unsafe_putOrderedInt(JNIEnv* env, jobject, jobject javaObj, jlong of ScopedFastNativeObjectAccess soa(env); ObjPtr obj = soa.Decode(javaObj); // TODO: A release store is likely to be faster on future processors. - QuasiAtomic::ThreadFenceRelease(); + std::atomic_thread_fence(std::memory_order_release); // JNI must use non transactional mode. obj->SetField32(MemberOffset(offset), newValue); } @@ -152,7 +156,7 @@ static void Unsafe_putOrderedLong(JNIEnv* env, jobject, jobject javaObj, jlong o jlong newValue) { ScopedFastNativeObjectAccess soa(env); ObjPtr obj = soa.Decode(javaObj); - QuasiAtomic::ThreadFenceRelease(); + std::atomic_thread_fence(std::memory_order_release); // JNI must use non transactional mode. obj->SetField64(MemberOffset(offset), newValue); } @@ -194,7 +198,7 @@ static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong ScopedFastNativeObjectAccess soa(env); ObjPtr obj = soa.Decode(javaObj); ObjPtr newValue = soa.Decode(javaNewValue); - QuasiAtomic::ThreadFenceRelease(); + std::atomic_thread_fence(std::memory_order_release); // JNI must use non transactional mode. obj->SetFieldObject(MemberOffset(offset), newValue); } diff --git a/runtime/native_bridge_art_interface.cc b/runtime/native_bridge_art_interface.cc index 7d72805dc60752e93f4c9690aacd0803ddb16cf9..def48e8be932f053bd40d443a14c38a576b83e3d 100644 --- a/runtime/native_bridge_art_interface.cc +++ b/runtime/native_bridge_art_interface.cc @@ -25,7 +25,7 @@ #include "base/logging.h" // For VLOG. #include "base/macros.h" #include "dex/dex_file-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "scoped_thread_state_change-inl.h" #include "sigchain.h" diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc index 14f3f45f9e006cda2f24b46c2aa4b883fdc86a48..ce295aacdef86429dc4e1959c2acc60ebdd92ad6 100644 --- a/runtime/native_stack_dump.cc +++ b/runtime/native_stack_dump.cc @@ -289,10 +289,7 @@ void DumpNativeStack(std::ostream& os, ArtMethod* current_method, void* ucontext_ptr, bool skip_frames) { - // b/18119146 - if (RUNNING_ON_MEMORY_TOOL != 0) { - return; - } + // Historical note: This was disabled when running under Valgrind (b/18119146). BacktraceMap* map = existing_map; std::unique_ptr tmp_map; diff --git a/runtime/non_debuggable_classes.cc b/runtime/non_debuggable_classes.cc index 8484e2cde7e8d495acc76d7201888912bc1639b2..f42a2d67554a536be3e6b745f8b945e1b281fe8b 100644 --- a/runtime/non_debuggable_classes.cc +++ b/runtime/non_debuggable_classes.cc @@ -16,7 +16,7 @@ #include "non_debuggable_classes.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "nativehelper/scoped_local_ref.h" #include "obj_ptr-inl.h" diff --git a/runtime/oat.h b/runtime/oat.h index 292c9d6f413260afb6ea1f54fe04b91cfc9f7231..f8ec665683dbd0c1d80484d8766730f22849d1e1 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: Math.pow() intrinsic. - static constexpr uint8_t kOatVersion[] = { '1', '3', '8', '\0' }; + // Last oat version changed reason: Remove InvokeInfo from stack maps. + static constexpr uint8_t kOatVersion[] = { '1', '5', '4', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 03ea043c664271b54470a30e28fbcd0fe1d19454..58e16ed1b70e0c9392151d8f9e123f7c3e64fad7 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -40,6 +40,7 @@ #include "base/enums.h" #include "base/file_utils.h" #include "base/logging.h" // For VLOG_IS_ON. +#include "base/mem_map.h" #include "base/os.h" #include "base/stl_util.h" #include "base/systrace.h" @@ -49,19 +50,18 @@ #include "dex/dex_file_loader.h" #include "dex/dex_file_types.h" #include "dex/standard_dex_file.h" +#include "dex/type_lookup_table.h" #include "dex/utf-inl.h" #include "elf_file.h" #include "elf_utils.h" #include "gc_root.h" #include "gc/space/image_space.h" -#include "mem_map.h" #include "mirror/class.h" #include "mirror/object-inl.h" #include "oat.h" #include "oat_file-inl.h" #include "oat_file_manager.h" #include "runtime.h" -#include "type_lookup_table.h" #include "vdex_file.h" namespace art { @@ -211,12 +211,12 @@ OatFileBase* OatFileBase::OpenOatFile(int zip_fd, return nullptr; } + ret->PreSetup(elf_filename); + if (!ret->LoadVdex(vdex_filename, writable, low_4gb, error_msg)) { return nullptr; } - ret->PreSetup(elf_filename); - if (!ret->Setup(zip_fd, abs_dex_location, error_msg)) { return nullptr; } @@ -252,12 +252,12 @@ OatFileBase* OatFileBase::OpenOatFile(int zip_fd, return nullptr; } + ret->PreSetup(oat_location); + if (!ret->LoadVdex(vdex_fd, vdex_location, writable, low_4gb, error_msg)) { return nullptr; } - ret->PreSetup(oat_location); - if (!ret->Setup(zip_fd, abs_dex_location, error_msg)) { return nullptr; } @@ -347,6 +347,19 @@ bool OatFileBase::ComputeFields(uint8_t* requested_base, // Readjust to be non-inclusive upper bound. end_ += sizeof(uint32_t); + data_bimg_rel_ro_begin_ = FindDynamicSymbolAddress("oatdatabimgrelro", &symbol_error_msg); + if (data_bimg_rel_ro_begin_ != nullptr) { + data_bimg_rel_ro_end_ = + FindDynamicSymbolAddress("oatdatabimgrelrolastword", &symbol_error_msg); + if (data_bimg_rel_ro_end_ == nullptr) { + *error_msg = + StringPrintf("Failed to find oatdatabimgrelrolastword symbol in '%s'", file_path.c_str()); + return false; + } + // Readjust to be non-inclusive upper bound. + data_bimg_rel_ro_end_ += sizeof(uint32_t); + } + bss_begin_ = const_cast(FindDynamicSymbolAddress("oatbss", &symbol_error_msg)); if (bss_begin_ == nullptr) { // No .bss section. @@ -403,37 +416,6 @@ inline static bool ReadOatDexFileData(const OatFile& oat_file, return true; } -static inline bool MapConstantTables(const gc::space::ImageSpace* space, - uint8_t* address) { - // If MREMAP_DUP is ever merged to Linux kernel, use it to avoid the unnecessary open()/close(). - // Note: The current approach relies on the filename still referencing the same inode. - - File file(space->GetImageFilename(), O_RDONLY, /* checkUsage */ false); - if (!file.IsOpened()) { - LOG(ERROR) << "Failed to open boot image file " << space->GetImageFilename(); - return false; - } - - uint32_t offset = space->GetImageHeader().GetBootImageConstantTablesOffset(); - uint32_t size = space->GetImageHeader().GetBootImageConstantTablesSize(); - std::string error_msg; - std::unique_ptr mem_map(MemMap::MapFileAtAddress(address, - size, - PROT_READ, - MAP_PRIVATE, - file.Fd(), - offset, - /* low_4gb */ false, - /* reuse */ true, - file.GetPath().c_str(), - &error_msg)); - if (mem_map == nullptr) { - LOG(ERROR) << "Failed to mmap boot image tables from file " << space->GetImageFilename(); - return false; - } - return true; -} - static bool ReadIndexBssMapping(OatFile* oat_file, /*inout*/const uint8_t** oat, size_t dex_file_index, @@ -540,6 +522,17 @@ bool OatFileBase::Setup(int zip_fd, const char* abs_dex_location, std::string* e } const uint8_t* oat = Begin() + oat_dex_files_offset; // Jump to the OatDexFile records. + if (!IsAligned(data_bimg_rel_ro_begin_) || + !IsAligned(data_bimg_rel_ro_end_) || + data_bimg_rel_ro_begin_ > data_bimg_rel_ro_end_) { + *error_msg = StringPrintf("In oat file '%s' found unaligned or unordered databimgrelro " + "symbol(s): begin = %p, end = %p", + GetLocation().c_str(), + data_bimg_rel_ro_begin_, + data_bimg_rel_ro_end_); + return false; + } + DCHECK_GE(static_cast(pointer_size), alignof(GcRoot)); if (!IsAligned(bss_begin_) || !IsAlignedParam(bss_methods_, static_cast(pointer_size)) || @@ -568,12 +561,15 @@ bool OatFileBase::Setup(int zip_fd, const char* abs_dex_location, std::string* e return false; } - uint8_t* after_tables = - (bss_methods_ != nullptr) ? bss_methods_ : bss_roots_; // May be null. - uint8_t* boot_image_tables = (bss_begin_ == after_tables) ? nullptr : bss_begin_; - uint8_t* boot_image_tables_end = - (bss_begin_ == after_tables) ? nullptr : (after_tables != nullptr) ? after_tables : bss_end_; - DCHECK_EQ(boot_image_tables != nullptr, boot_image_tables_end != nullptr); + if (bss_methods_ != nullptr && bss_methods_ != bss_begin_) { + *error_msg = StringPrintf("In oat file '%s' found unexpected .bss gap before 'oatbssmethods': " + "begin = %p, methods = %p", + GetLocation().c_str(), + bss_begin_, + bss_methods_); + return false; + } + uint32_t dex_file_count = GetOatHeader().GetDexFileCount(); oat_dex_files_storage_.reserve(dex_file_count); for (size_t i = 0; i < dex_file_count; i++) { @@ -690,7 +686,7 @@ bool OatFileBase::Setup(int zip_fd, const char* abs_dex_location, std::string* e return false; } } - dex_file_pointer = uncompressed_dex_files_.get()->at(i)->Begin(); + dex_file_pointer = (*uncompressed_dex_files_)[i]->Begin(); } else { // Do not support mixed-mode oat files. if (uncompressed_dex_files_ != nullptr) { @@ -864,39 +860,28 @@ bool OatFileBase::Setup(int zip_fd, const char* abs_dex_location, std::string* e } } - if (boot_image_tables != nullptr) { - Runtime* runtime = Runtime::Current(); + Runtime* runtime = Runtime::Current(); + + if (DataBimgRelRoBegin() != nullptr) { + // Make .data.bimg.rel.ro read only. ClassLinker shall make it writable for relocation. + uint8_t* reloc_begin = const_cast(DataBimgRelRoBegin()); + CheckedCall(mprotect, "protect relocations", reloc_begin, DataBimgRelRoSize(), PROT_READ); if (UNLIKELY(runtime == nullptr)) { - // This must be oatdump without boot image. Make sure the .bss is inaccessible. - CheckedCall(mprotect, "protect bss", const_cast(BssBegin()), BssSize(), PROT_NONE); + // This must be oatdump without boot image. } else if (!IsExecutable()) { - // Do not try to mmap boot image tables into .bss if the oat file is not executable. + // Do not check whether we have a boot image if the oat file is not executable. + } else if (UNLIKELY(runtime->GetHeap()->GetBootImageSpaces().empty())) { + *error_msg = StringPrintf("Cannot load oat file '%s' with .data.bimg.rel.ro as executable " + "without boot image.", + GetLocation().c_str()); + return false; } else { - // Map boot image tables into the .bss. The reserved size must match size of the tables. - size_t reserved_size = static_cast(boot_image_tables_end - boot_image_tables); - size_t tables_size = 0u; - for (gc::space::ImageSpace* space : runtime->GetHeap()->GetBootImageSpaces()) { - tables_size += space->GetImageHeader().GetBootImageConstantTablesSize(); - DCHECK_ALIGNED(tables_size, kPageSize); - } - if (tables_size != reserved_size) { - *error_msg = StringPrintf("In oat file '%s' found unexpected boot image table sizes, " - " %zu bytes, should be %zu.", - GetLocation().c_str(), - reserved_size, - tables_size); - return false; - } - for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) { - uint32_t current_tables_size = space->GetImageHeader().GetBootImageConstantTablesSize(); - if (current_tables_size != 0u && !MapConstantTables(space, boot_image_tables)) { - return false; - } - boot_image_tables += current_tables_size; - } - DCHECK(boot_image_tables == boot_image_tables_end); + // ClassLinker shall perform the relocation when we register a dex file from + // this oat file. We do not do the relocation here to avoid dirtying the pages + // if the code is never actually ready to be executed. } } + return true; } @@ -1078,7 +1063,8 @@ bool DlOpenOatFile::Dlopen(const std::string& elf_filename, dlopen_handle_ = android_dlopen_ext(absolute_path.get(), RTLD_NOW, &extinfo); #else UNUSED(oat_file_begin); - static_assert(!kIsTargetBuild || kIsTargetLinux, "host_dlopen_handles_ will leak handles"); + static_assert(!kIsTargetBuild || kIsTargetLinux || kIsTargetFuchsia, + "host_dlopen_handles_ will leak handles"); MutexLock mu(Thread::Current(), *Locks::host_dlopen_handles_lock_); dlopen_handle_ = dlopen(absolute_path.get(), RTLD_NOW); if (dlopen_handle_ != nullptr) { @@ -1542,6 +1528,8 @@ OatFile::OatFile(const std::string& location, bool is_executable) vdex_(nullptr), begin_(nullptr), end_(nullptr), + data_bimg_rel_ro_begin_(nullptr), + data_bimg_rel_ro_end_(nullptr), bss_begin_(nullptr), bss_end_(nullptr), bss_methods_(nullptr), @@ -1571,22 +1559,6 @@ const uint8_t* OatFile::End() const { return end_; } -const uint8_t* OatFile::BssBegin() const { - return bss_begin_; -} - -const uint8_t* OatFile::BssEnd() const { - return bss_end_; -} - -const uint8_t* OatFile::VdexBegin() const { - return vdex_begin_; -} - -const uint8_t* OatFile::VdexEnd() const { - return vdex_end_; -} - const uint8_t* OatFile::DexBegin() const { return vdex_->Begin(); } @@ -1595,6 +1567,16 @@ const uint8_t* OatFile::DexEnd() const { return vdex_->End(); } +ArrayRef OatFile::GetBootImageRelocations() const { + if (data_bimg_rel_ro_begin_ != nullptr) { + const uint32_t* relocations = reinterpret_cast(data_bimg_rel_ro_begin_); + const uint32_t* relocations_end = reinterpret_cast(data_bimg_rel_ro_end_); + return ArrayRef(relocations, relocations_end - relocations); + } else { + return ArrayRef(); + } +} + ArrayRef OatFile::GetBssMethods() const { if (bss_methods_ != nullptr) { ArtMethod** methods = reinterpret_cast(bss_methods_); @@ -1616,9 +1598,9 @@ ArrayRef> OatFile::GetBssGcRoots() const { } } -const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location, - const uint32_t* dex_location_checksum, - std::string* error_msg) const { +const OatDexFile* OatFile::GetOatDexFile(const char* dex_location, + const uint32_t* dex_location_checksum, + std::string* error_msg) const { // NOTE: We assume here that the canonical location for a given dex_location never // changes. If it does (i.e. some symlink used by the filename changes) we may return // an incorrect OatDexFile. As long as we have a checksum to check, we shall return @@ -1627,7 +1609,7 @@ const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location, // TODO: Additional analysis of usage patterns to see if this can be simplified // without any performance loss, for example by not doing the first lock-free lookup. - const OatFile::OatDexFile* oat_dex_file = nullptr; + const OatDexFile* oat_dex_file = nullptr; StringPiece key(dex_location); // Try to find the key cheaply in the oat_dex_files_ map which holds dex locations // directly mentioned in the oat file and doesn't require locking. @@ -1685,17 +1667,17 @@ const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location, return oat_dex_file; } -OatFile::OatDexFile::OatDexFile(const OatFile* oat_file, - const std::string& dex_file_location, - const std::string& canonical_dex_file_location, - uint32_t dex_file_location_checksum, - const uint8_t* dex_file_pointer, - const uint8_t* lookup_table_data, - const IndexBssMapping* method_bss_mapping_data, - const IndexBssMapping* type_bss_mapping_data, - const IndexBssMapping* string_bss_mapping_data, - const uint32_t* oat_class_offsets_pointer, - const DexLayoutSections* dex_layout_sections) +OatDexFile::OatDexFile(const OatFile* oat_file, + const std::string& dex_file_location, + const std::string& canonical_dex_file_location, + uint32_t dex_file_location_checksum, + const uint8_t* dex_file_pointer, + const uint8_t* lookup_table_data, + const IndexBssMapping* method_bss_mapping_data, + const IndexBssMapping* type_bss_mapping_data, + const IndexBssMapping* string_bss_mapping_data, + const uint32_t* oat_class_offsets_pointer, + const DexLayoutSections* dex_layout_sections) : oat_file_(oat_file), dex_file_location_(dex_file_location), canonical_dex_file_location_(canonical_dex_file_location), @@ -1706,6 +1688,7 @@ OatFile::OatDexFile::OatDexFile(const OatFile* oat_file, type_bss_mapping_(type_bss_mapping_data), string_bss_mapping_(string_bss_mapping_data), oat_class_offsets_pointer_(oat_class_offsets_pointer), + lookup_table_(), dex_layout_sections_(dex_layout_sections) { // Initialize TypeLookupTable. if (lookup_table_data_ != nullptr) { @@ -1725,16 +1708,15 @@ OatFile::OatDexFile::OatDexFile(const OatFile* oat_file, } } -OatFile::OatDexFile::OatDexFile(std::unique_ptr&& lookup_table) - : lookup_table_(std::move(lookup_table)) {} +OatDexFile::OatDexFile(TypeLookupTable&& lookup_table) : lookup_table_(std::move(lookup_table)) {} -OatFile::OatDexFile::~OatDexFile() {} +OatDexFile::~OatDexFile() {} -size_t OatFile::OatDexFile::FileSize() const { +size_t OatDexFile::FileSize() const { return reinterpret_cast(dex_file_pointer_)->file_size_; } -std::unique_ptr OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const { +std::unique_ptr OatDexFile::OpenDexFile(std::string* error_msg) const { ScopedTrace trace(__PRETTY_FUNCTION__); static constexpr bool kVerify = false; static constexpr bool kVerifyChecksum = false; @@ -1749,11 +1731,11 @@ std::unique_ptr OatFile::OatDexFile::OpenDexFile(std::string* err error_msg); } -uint32_t OatFile::OatDexFile::GetOatClassOffset(uint16_t class_def_index) const { +uint32_t OatDexFile::GetOatClassOffset(uint16_t class_def_index) const { return oat_class_offsets_pointer_[class_def_index]; } -OatFile::OatClass OatFile::OatDexFile::GetOatClass(uint16_t class_def_index) const { +OatFile::OatClass OatDexFile::GetOatClass(uint16_t class_def_index) const { uint32_t oat_class_offset = GetOatClassOffset(class_def_index); const uint8_t* oat_class_pointer = oat_file_->Begin() + oat_class_offset; @@ -1795,16 +1777,16 @@ OatFile::OatClass OatFile::OatDexFile::GetOatClass(uint16_t class_def_index) con reinterpret_cast(methods_pointer)); } -const DexFile::ClassDef* OatFile::OatDexFile::FindClassDef(const DexFile& dex_file, - const char* descriptor, - size_t hash) { - const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); +const DexFile::ClassDef* OatDexFile::FindClassDef(const DexFile& dex_file, + const char* descriptor, + size_t hash) { + const OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); DCHECK_EQ(ComputeModifiedUtf8Hash(descriptor), hash); bool used_lookup_table = false; const DexFile::ClassDef* lookup_table_classdef = nullptr; - if (LIKELY((oat_dex_file != nullptr) && (oat_dex_file->GetTypeLookupTable() != nullptr))) { + if (LIKELY((oat_dex_file != nullptr) && oat_dex_file->GetTypeLookupTable().Valid())) { used_lookup_table = true; - const uint32_t class_def_idx = oat_dex_file->GetTypeLookupTable()->Lookup(descriptor, hash); + const uint32_t class_def_idx = oat_dex_file->GetTypeLookupTable().Lookup(descriptor, hash); lookup_table_classdef = (class_def_idx != dex::kDexNoIndex) ? &dex_file.GetClassDef(class_def_idx) : nullptr; @@ -1815,6 +1797,7 @@ const DexFile::ClassDef* OatFile::OatDexFile::FindClassDef(const DexFile& dex_fi // Fast path for rare no class defs case. const uint32_t num_class_defs = dex_file.NumClassDefs(); if (num_class_defs == 0) { + DCHECK(!used_lookup_table); return nullptr; } const DexFile::TypeId* type_id = dex_file.FindTypeId(descriptor); @@ -1845,7 +1828,7 @@ void OatDexFile::MadviseDexFile(const DexFile& dex_file, MadviseState state) { dex_file.Begin() + dex_file.Size(), MADV_RANDOM); } - const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); + const OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); if (oat_dex_file != nullptr) { // Should always be there. const DexLayoutSections* const sections = oat_dex_file->GetDexLayoutSections(); @@ -1963,7 +1946,7 @@ OatFile::OatClass OatFile::FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, bool* found) { DCHECK_NE(class_def_idx, DexFile::kDexNoIndex16); - const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); + const OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); if (oat_dex_file == nullptr || oat_dex_file->GetOatFile() == nullptr) { *found = false; return OatFile::OatClass::Invalid(); @@ -1972,7 +1955,7 @@ OatFile::OatClass OatFile::FindOatClass(const DexFile& dex_file, return oat_dex_file->GetOatClass(class_def_idx); } -void OatFile::OatDexFile::AssertAotCompiler() { +void OatDexFile::AssertAotCompiler() { CHECK(Runtime::Current()->IsAotCompiler()); } diff --git a/runtime/oat_file.h b/runtime/oat_file.h index acd06a2752d6bbb38839494a0ec022a4052ee0ae..5f87bf0f99b9afac032016153538583d5aa702b1 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -32,11 +32,11 @@ #include "compiler_filter.h" #include "dex/dex_file.h" #include "dex/dex_file_layout.h" +#include "dex/type_lookup_table.h" #include "dex/utf.h" #include "index_bss_mapping.h" #include "mirror/object.h" #include "oat.h" -#include "type_lookup_table.h" namespace art { @@ -70,8 +70,6 @@ class OatFile { // Special classpath that skips shared library check. static constexpr const char* kSpecialSharedLibrary = "&"; - typedef art::OatDexFile OatDexFile; - // Opens an oat file contained within the given elf file. This is always opened as // non-executable at the moment. static OatFile* OpenWithElfFile(int zip_fd, @@ -283,6 +281,10 @@ class OatFile { return p >= Begin() && p < End(); } + size_t DataBimgRelRoSize() const { + return DataBimgRelRoEnd() - DataBimgRelRoBegin(); + } + size_t BssSize() const { return BssEnd() - BssBegin(); } @@ -308,15 +310,19 @@ class OatFile { const uint8_t* Begin() const; const uint8_t* End() const; - const uint8_t* BssBegin() const; - const uint8_t* BssEnd() const; + const uint8_t* DataBimgRelRoBegin() const { return data_bimg_rel_ro_begin_; } + const uint8_t* DataBimgRelRoEnd() const { return data_bimg_rel_ro_end_; } - const uint8_t* VdexBegin() const; - const uint8_t* VdexEnd() const; + const uint8_t* BssBegin() const { return bss_begin_; } + const uint8_t* BssEnd() const { return bss_end_; } + + const uint8_t* VdexBegin() const { return vdex_begin_; } + const uint8_t* VdexEnd() const { return vdex_end_; } const uint8_t* DexBegin() const; const uint8_t* DexEnd() const; + ArrayRef GetBootImageRelocations() const; ArrayRef GetBssMethods() const; ArrayRef> GetBssGcRoots() const; @@ -363,6 +369,12 @@ class OatFile { // Pointer to end of oat region for bounds checking. const uint8_t* end_; + // Pointer to the .data.bimg.rel.ro section, if present, otherwise null. + const uint8_t* data_bimg_rel_ro_begin_; + + // Pointer to the end of the .data.bimg.rel.ro section, if present, otherwise null. + const uint8_t* data_bimg_rel_ro_end_; + // Pointer to the .bss section, if present, otherwise null. uint8_t* bss_begin_; @@ -500,14 +512,14 @@ class OatDexFile FINAL { // Madvise the dex file based on the state we are moving to. static void MadviseDexFile(const DexFile& dex_file, MadviseState state); - TypeLookupTable* GetTypeLookupTable() const { - return lookup_table_.get(); + const TypeLookupTable& GetTypeLookupTable() const { + return lookup_table_; } ~OatDexFile(); // Create only with a type lookup table, used by the compiler to speed up compilation. - explicit OatDexFile(std::unique_ptr&& lookup_table); + explicit OatDexFile(TypeLookupTable&& lookup_table); // Return the dex layout sections. const DexLayoutSections* GetDexLayoutSections() const { @@ -539,7 +551,7 @@ class OatDexFile FINAL { const IndexBssMapping* const type_bss_mapping_ = nullptr; const IndexBssMapping* const string_bss_mapping_ = nullptr; const uint32_t* const oat_class_offsets_pointer_ = 0u; - mutable std::unique_ptr lookup_table_; + TypeLookupTable lookup_table_; const DexLayoutSections* const dex_layout_sections_ = nullptr; friend class OatFile; diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 2c96c116aaaa231ab944f6574df4375a2f5916df..f7c74cc23b674c1ce9a6855a76e6a67a9fe62c81 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -354,7 +354,7 @@ bool OatFileAssistant::LoadDexFiles( std::vector>* out_dex_files) { // Load the main dex file. std::string error_msg; - const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile( + const OatDexFile* oat_dex_file = oat_file.GetOatDexFile( dex_location.c_str(), nullptr, &error_msg); if (oat_dex_file == nullptr) { LOG(WARNING) << error_msg; @@ -453,7 +453,7 @@ bool OatFileAssistant::DexChecksumUpToDate(const OatFile& file, std::string* err for (uint32_t i = 0; i < number_of_dex_files; i++) { std::string dex = DexFileLoader::GetMultiDexLocation(i, dex_location_.c_str()); uint32_t expected_checksum = (*required_dex_checksums)[i]; - const OatFile::OatDexFile* oat_dex_file = file.GetOatDexFile(dex.c_str(), nullptr); + const OatDexFile* oat_dex_file = file.GetOatDexFile(dex.c_str(), nullptr); if (oat_dex_file == nullptr) { *error_msg = StringPrintf("failed to find %s in %s", dex.c_str(), file.GetLocation().c_str()); return false; @@ -866,6 +866,13 @@ bool OatFileAssistant::DexLocationToOatFilename(const std::string& location, CHECK(oat_filename != nullptr); CHECK(error_msg != nullptr); + // If ANDROID_DATA is not set, return false instead of aborting. + // This can occur for preopt when using a class loader context. + if (GetAndroidDataSafe(error_msg) == nullptr) { + *error_msg = "GetAndroidDataSafe failed: " + *error_msg; + return false; + } + std::string cache_dir = GetDalvikCache(GetInstructionSetString(isa)); if (cache_dir.empty()) { *error_msg = "Dalvik cache directory does not exist"; @@ -914,7 +921,7 @@ const std::vector* OatFileAssistant::GetRequiredDexChecksums() { required_dex_checksums_found_ = true; for (size_t i = 0; i < odex_file->GetOatHeader().GetDexFileCount(); i++) { std::string dex = DexFileLoader::GetMultiDexLocation(i, dex_location_.c_str()); - const OatFile::OatDexFile* odex_dex_file = odex_file->GetOatDexFile(dex.c_str(), nullptr); + const OatDexFile* odex_dex_file = odex_file->GetOatDexFile(dex.c_str(), nullptr); if (odex_dex_file == nullptr) { required_dex_checksums_found_ = false; break; @@ -1223,7 +1230,9 @@ bool OatFileAssistant::OatFileInfo::ClassLoaderContextIsOkay(ClassLoaderContext* return false; } - bool result = context->VerifyClassLoaderContextMatch(file->GetClassLoaderContext()); + + const bool result = context->VerifyClassLoaderContextMatch(file->GetClassLoaderContext()) != + ClassLoaderContext::VerificationResult::kMismatch; if (!result) { VLOG(oat) << "ClassLoaderContext check failed. Context was " << file->GetClassLoaderContext() diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 65481fc58a5b86c6c6f5fe5af33d4921b84e71bc..0b3c61d474eb8d2bf78583cc7bd933e420811950 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -1530,7 +1530,7 @@ TEST_F(OatFileAssistantTest, SystemOdex) { true, false); int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg); - EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable()); } @@ -1540,7 +1540,7 @@ TEST_F(OatFileAssistantTest, SystemOdex) { true, true); int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg); - EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; EXPECT_FALSE(oat_file_assistant.GetBestOatFile()->IsExecutable()); } @@ -1553,7 +1553,7 @@ TEST_F(OatFileAssistantTest, SystemOdex) { true, false); int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg); - EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable()); } @@ -1563,7 +1563,7 @@ TEST_F(OatFileAssistantTest, SystemOdex) { true, true); int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg); - EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable()); } } diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index f6fb9ded870a97a3b1e8d9697776d6f71fd87559..59a1045ba2cb9173e75629a6eb583cb6ba21895f 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -38,7 +38,7 @@ #include "gc/scoped_gc_critical_section.h" #include "gc/space/image_space.h" #include "handle_scope-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" #include "oat_file.h" @@ -276,9 +276,19 @@ static void AddNext(/*inout*/DexFileAndClassPair& original, } } -static bool CollisionCheck(std::vector& dex_files_loaded, - std::vector& dex_files_unloaded, - std::string* error_msg /*out*/) { +static bool CheckClassCollision(const OatFile* oat_file, + const ClassLoaderContext* context, + std::string* error_msg /*out*/) { + std::vector dex_files_loaded = context->FlattenOpenedDexFiles(); + + // Vector that holds the newly opened dex files live, this is done to prevent leaks. + std::vector> opened_dex_files; + + ScopedTrace st("Collision check"); + // Add dex files from the oat file to check. + std::vector dex_files_unloaded; + AddDexFilesFromOat(oat_file, &dex_files_unloaded, &opened_dex_files); + // Generate type index information for each dex file. std::vector loaded_types; for (const DexFile* dex_file : dex_files_loaded) { @@ -355,9 +365,10 @@ static bool CollisionCheck(std::vector& dex_files_loaded, // against the following top element. If the descriptor is the same, it is now checked whether // the two elements agree on whether their dex file was from an already-loaded oat-file or the // new oat file. Any disagreement indicates a collision. -bool OatFileManager::HasCollisions(const OatFile* oat_file, - const ClassLoaderContext* context, - std::string* error_msg /*out*/) const { +OatFileManager::CheckCollisionResult OatFileManager::CheckCollision( + const OatFile* oat_file, + const ClassLoaderContext* context, + /*out*/ std::string* error_msg) const { DCHECK(oat_file != nullptr); DCHECK(error_msg != nullptr); @@ -367,28 +378,59 @@ bool OatFileManager::HasCollisions(const OatFile* oat_file, // Note that this has correctness implications as we cannot guarantee that the class resolution // used during compilation is OK (b/37777332). if (context == nullptr) { - LOG(WARNING) << "Skipping duplicate class check due to unsupported classloader"; - return false; + LOG(WARNING) << "Skipping duplicate class check due to unsupported classloader"; + return CheckCollisionResult::kSkippedUnsupportedClassLoader; } - // If the pat file loading context matches the context used during compilation then we accept + // If the oat file loading context matches the context used during compilation then we accept // the oat file without addition checks - if (context->VerifyClassLoaderContextMatch(oat_file->GetClassLoaderContext())) { - return false; + ClassLoaderContext::VerificationResult result = context->VerifyClassLoaderContextMatch( + oat_file->GetClassLoaderContext(), + /*verify_names*/ true, + /*verify_checksums*/ true); + switch (result) { + case ClassLoaderContext::VerificationResult::kForcedToSkipChecks: + return CheckCollisionResult::kSkippedClassLoaderContextSharedLibrary; + case ClassLoaderContext::VerificationResult::kMismatch: + // Mismatched context, do the actual collision check. + break; + case ClassLoaderContext::VerificationResult::kVerifies: + return CheckCollisionResult::kNoCollisions; } // The class loader context does not match. Perform a full duplicate classes check. + return CheckClassCollision(oat_file, context, error_msg) + ? CheckCollisionResult::kPerformedHasCollisions : CheckCollisionResult::kNoCollisions; +} - std::vector dex_files_loaded = context->FlattenOpenedDexFiles(); - - // Vector that holds the newly opened dex files live, this is done to prevent leaks. - std::vector> opened_dex_files; +bool OatFileManager::AcceptOatFile(CheckCollisionResult result) const { + // Take the file only if it has no collisions, or we must take it because of preopting. + // Also accept oat files for shared libraries and unsupported class loaders. + return result != CheckCollisionResult::kPerformedHasCollisions; +} - ScopedTrace st("Collision check"); - // Add dex files from the oat file to check. - std::vector dex_files_unloaded; - AddDexFilesFromOat(oat_file, &dex_files_unloaded, &opened_dex_files); - return CollisionCheck(dex_files_loaded, dex_files_unloaded, error_msg); +bool OatFileManager::ShouldLoadAppImage(CheckCollisionResult check_collision_result, + const OatFile* source_oat_file, + ClassLoaderContext* context, + std::string* error_msg) { + Runtime* const runtime = Runtime::Current(); + if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) { + // If we verified the class loader context (skipping due to the special marker doesn't + // count), then also avoid the collision check. + bool load_image = check_collision_result == CheckCollisionResult::kNoCollisions; + // If we skipped the collision check, we need to reverify to be sure its OK to load the + // image. + if (!load_image && + check_collision_result == + CheckCollisionResult::kSkippedClassLoaderContextSharedLibrary) { + // We can load the app image only if there are no collisions. If we know the + // class loader but didn't do the full collision check in HasCollisions(), + // do it now. b/77342775 + load_image = !CheckClassCollision(source_oat_file, context, error_msg); + } + return load_image; + } + return false; } std::vector> OatFileManager::OpenDexFilesFromOat( @@ -473,16 +515,17 @@ std::vector> OatFileManager::OpenDexFilesFromOat( << reinterpret_cast(oat_file.get()) << " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false) << ")"; + CheckCollisionResult check_collision_result = CheckCollisionResult::kPerformedHasCollisions; if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) { // Prevent oat files from being loaded if no class_loader or dex_elements are provided. // This can happen when the deprecated DexFile.(String) is called directly, and it // could load oat files without checking the classpath, which would be incorrect. // Take the file only if it has no collisions, or we must take it because of preopting. - bool accept_oat_file = - !HasCollisions(oat_file.get(), context.get(), /*out*/ &error_msg); + check_collision_result = CheckCollision(oat_file.get(), context.get(), /*out*/ &error_msg); + bool accept_oat_file = AcceptOatFile(check_collision_result); if (!accept_oat_file) { // Failed the collision check. Print warning. - if (Runtime::Current()->IsDexFileFallbackEnabled()) { + if (runtime->IsDexFileFallbackEnabled()) { if (!oat_file_assistant.HasOriginalDexFiles()) { // We need to fallback but don't have original dex files. We have to // fallback to opening the existing oat file. This is potentially @@ -529,10 +572,11 @@ std::vector> OatFileManager::OpenDexFilesFromOat( // We need to throw away the image space if we are debuggable but the oat-file source of the // image is not otherwise we might get classes with inlined methods or other such things. std::unique_ptr image_space; - if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) { + if (ShouldLoadAppImage(check_collision_result, + source_oat_file, + context.get(), + &error_msg)) { image_space = oat_file_assistant.OpenImageSpace(source_oat_file); - } else { - image_space = nullptr; } if (image_space != nullptr) { ScopedObjectAccess soa(self); diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h index 038474e31fb493d5d3504a99a132486189a77b2b..80456e9b751261455b24d06859a602f10eb7fb94 100644 --- a/runtime/oat_file_manager.h +++ b/runtime/oat_file_manager.h @@ -108,23 +108,39 @@ class OatFileManager { void SetOnlyUseSystemOatFiles(); private: + enum class CheckCollisionResult { + kSkippedUnsupportedClassLoader, + kSkippedClassLoaderContextSharedLibrary, + kNoCollisions, + kPerformedHasCollisions, + }; + // Check that the class loader context of the given oat file matches the given context. // This will perform a check that all class loaders in the chain have the same type and // classpath. // If the context is null (which means the initial class loader was null or unsupported) - // this returns false. + // this returns kSkippedUnsupportedClassLoader. // If the context does not validate the method will check for duplicate class definitions of // the given oat file against the oat files (either from the class loaders if possible or all // non-boot oat files otherwise). - // Return true if there are any class definition collisions in the oat_file. - bool HasCollisions(const OatFile* oat_file, - const ClassLoaderContext* context, - /*out*/ std::string* error_msg) const + // Return kPerformedHasCollisions if there are any class definition collisions in the oat_file. + CheckCollisionResult CheckCollision(const OatFile* oat_file, + const ClassLoaderContext* context, + /*out*/ std::string* error_msg) const REQUIRES(!Locks::oat_file_manager_lock_); const OatFile* FindOpenedOatFileFromOatLocationLocked(const std::string& oat_location) const REQUIRES(Locks::oat_file_manager_lock_); + // Return true if we should accept the oat file. + bool AcceptOatFile(CheckCollisionResult result) const; + + // Return true if we should attempt to load the app image. + bool ShouldLoadAppImage(CheckCollisionResult check_collision_result, + const OatFile* source_oat_file, + ClassLoaderContext* context, + std::string* error_msg); + std::set> oat_files_ GUARDED_BY(Locks::oat_file_manager_lock_); bool have_non_pic_oat_file_; diff --git a/runtime/oat_quick_method_header.cc b/runtime/oat_quick_method_header.cc index 98238e56009667d5e22ec5dd020f9c40f4940f4b..0b239c191990e4abc94df7fca96787910d8ef1dd 100644 --- a/runtime/oat_quick_method_header.cc +++ b/runtime/oat_quick_method_header.cc @@ -19,6 +19,7 @@ #include "art_method.h" #include "dex/dex_file_types.h" #include "scoped_thread_state_change-inl.h" +#include "stack_map.h" #include "thread.h" namespace art { @@ -34,23 +35,20 @@ OatQuickMethodHeader::OatQuickMethodHeader(uint32_t vmap_table_offset, frame_info_(frame_size_in_bytes, core_spill_mask, fp_spill_mask), code_size_(code_size) {} -OatQuickMethodHeader::~OatQuickMethodHeader() {} - uint32_t OatQuickMethodHeader::ToDexPc(ArtMethod* method, const uintptr_t pc, bool abort_on_failure) const { const void* entry_point = GetEntryPoint(); uint32_t sought_offset = pc - reinterpret_cast(entry_point); - if (IsOptimized()) { - CodeInfo code_info = GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(sought_offset, encoding); + if (method->IsNative()) { + return dex::kDexNoIndex; + } else { + DCHECK(IsOptimized()); + CodeInfo code_info(this); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(sought_offset); if (stack_map.IsValid()) { - return stack_map.GetDexPc(encoding.stack_map.encoding); + return stack_map.GetDexPc(); } - } else { - DCHECK(method->IsNative()); - return dex::kDexNoIndex; } if (abort_on_failure) { ScopedObjectAccess soa(Thread::Current()); @@ -71,18 +69,17 @@ uintptr_t OatQuickMethodHeader::ToNativeQuickPc(ArtMethod* method, DCHECK(!method->IsNative()); DCHECK(IsOptimized()); // Search for the dex-to-pc mapping in stack maps. - CodeInfo code_info = GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); + CodeInfo code_info(this); // All stack maps are stored in the same CodeItem section, safepoint stack // maps first, then catch stack maps. We use `is_for_catch_handler` to select // the order of iteration. StackMap stack_map = - LIKELY(is_for_catch_handler) ? code_info.GetCatchStackMapForDexPc(dex_pc, encoding) - : code_info.GetStackMapForDexPc(dex_pc, encoding); + LIKELY(is_for_catch_handler) ? code_info.GetCatchStackMapForDexPc(dex_pc) + : code_info.GetStackMapForDexPc(dex_pc); if (stack_map.IsValid()) { return reinterpret_cast(entry_point) + - stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA); + stack_map.GetNativePcOffset(kRuntimeISA); } if (abort_on_failure) { ScopedObjectAccess soa(Thread::Current()); diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h index f0966b7bfaf722819147dd86f555c27c4c5e9f99..1e4ca3e450cede6a76645ded9ed4296366edc0c2 100644 --- a/runtime/oat_quick_method_header.h +++ b/runtime/oat_quick_method_header.h @@ -39,8 +39,6 @@ class PACKED(4) OatQuickMethodHeader { uint32_t fp_spill_mask, uint32_t code_size); - ~OatQuickMethodHeader(); - static OatQuickMethodHeader* FromCodePointer(const void* code_ptr) { uintptr_t code = reinterpret_cast(code_ptr); uintptr_t header = code - OFFSETOF_MEMBER(OatQuickMethodHeader, code_); @@ -65,9 +63,9 @@ class PACKED(4) OatQuickMethodHeader { return GetCodeSize() != 0 && vmap_table_offset_ != 0; } - const void* GetOptimizedCodeInfoPtr() const { + const uint8_t* GetOptimizedCodeInfoPtr() const { DCHECK(IsOptimized()); - return reinterpret_cast(code_ - vmap_table_offset_); + return code_ - vmap_table_offset_; } uint8_t* GetOptimizedCodeInfoPtr() { @@ -75,10 +73,6 @@ class PACKED(4) OatQuickMethodHeader { return code_ - vmap_table_offset_; } - CodeInfo GetOptimizedCodeInfo() const { - return CodeInfo(GetOptimizedCodeInfoPtr()); - } - const void* GetOptimizedMethodInfoPtr() const { DCHECK(IsOptimized()); return reinterpret_cast(code_ - method_info_offset_); @@ -165,7 +159,12 @@ class PACKED(4) OatQuickMethodHeader { } QuickMethodFrameInfo GetFrameInfo() const { - return frame_info_; + DCHECK(IsOptimized()); + QuickMethodFrameInfo frame_info = CodeInfo::DecodeFrameInfo(GetOptimizedCodeInfoPtr()); + DCHECK_EQ(frame_info.FrameSizeInBytes(), frame_info_.FrameSizeInBytes()); + DCHECK_EQ(frame_info.CoreSpillMask(), frame_info_.CoreSpillMask()); + DCHECK_EQ(frame_info.FpSpillMask(), frame_info_.FpSpillMask()); + return frame_info; } uintptr_t ToNativeQuickPc(ArtMethod* method, diff --git a/runtime/obj_ptr.h b/runtime/obj_ptr.h index 14fdba31d943b5feeec56284286eb0ceb2a69c4a..e421d878fff84b282c9d0fa731827922fd3b82f4 100644 --- a/runtime/obj_ptr.h +++ b/runtime/obj_ptr.h @@ -20,9 +20,9 @@ #include #include +#include "base/globals.h" #include "base/macros.h" #include "base/mutex.h" // For Locks::mutator_lock_. -#include "globals.h" namespace art { diff --git a/runtime/offsets.h b/runtime/offsets.h index aaf5c0c1209e5a930ba227bbc9d8225ed2228f08..4df9b27f61ab64c60d720ee488a5805c7987c8d1 100644 --- a/runtime/offsets.h +++ b/runtime/offsets.h @@ -20,7 +20,7 @@ #include #include "base/enums.h" -#include "globals.h" +#include "base/globals.h" namespace art { diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 5518eb2c49b1e449ab29026a4018961f524bdbe5..7383d477bb323a44896bc1c3df789408631e7342 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -194,6 +194,9 @@ std::unique_ptr ParsedOptions::MakeParser(bool ignore_unrecognize .Define("-Xjittransitionweight:_") .WithType() .IntoKey(M::JITInvokeTransitionWeight) + .Define("-Xjitpthreadpriority:_") + .WithType() + .IntoKey(M::JITPoolThreadPthreadPriority) .Define("-Xjitsaveprofilinginfo") .WithType() .AppendValues() @@ -252,12 +255,6 @@ std::unique_ptr ParsedOptions::MakeParser(bool ignore_unrecognize .Define("-Xstackdumplockprofthreshold:_") .WithType() .IntoKey(M::StackDumpLockProfThreshold) - .Define("-Xusetombstonedtraces") - .WithValue(true) - .IntoKey(M::UseTombstonedTraces) - .Define("-Xstacktracefile:_") - .WithType() - .IntoKey(M::StackTraceFile) .Define("-Xmethod-trace") .IntoKey(M::MethodTrace) .Define("-Xmethod-trace-file:_") @@ -699,7 +696,6 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, "The following Dalvik options are supported:\n"); UsageMessage(stream, " -Xzygote\n"); UsageMessage(stream, " -Xjnitrace:substring (eg NativeClass or nativeMethod)\n"); - UsageMessage(stream, " -Xstacktracefile:\n"); UsageMessage(stream, " -Xgc:[no]preverify\n"); UsageMessage(stream, " -Xgc:[no]postverify\n"); UsageMessage(stream, " -XX:HeapGrowthLimit=N\n"); diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h index 0f8555a8293b05f2d49f4a690314b8fcf8da12c9..8c77d39f7b12cdd96eb5f2b514b815620afd105a 100644 --- a/runtime/parsed_options.h +++ b/runtime/parsed_options.h @@ -23,9 +23,9 @@ #include #include "arch/instruction_set.h" +#include "base/globals.h" #include "gc/collector_type.h" #include "gc/space/large_object_space.h" -#include "globals.h" // #include "jit/profile_saver_options.h" #include "runtime_options.h" diff --git a/runtime/parsed_options_test.cc b/runtime/parsed_options_test.cc index 8948c710db3879d1007e73613dcf04e760a43fca..9e5d9abe3b5fb8d8a81d4a8aff8fa5dc2184dd74 100644 --- a/runtime/parsed_options_test.cc +++ b/runtime/parsed_options_test.cc @@ -112,7 +112,7 @@ TEST_F(ParsedOptionsTest, ParsedOptions) { TEST_F(ParsedOptionsTest, ParsedOptionsGc) { RuntimeOptions options; - options.push_back(std::make_pair("-Xgc:MC", nullptr)); + options.push_back(std::make_pair("-Xgc:SS", nullptr)); RuntimeArgumentMap map; bool parsed = ParsedOptions::Parse(options, false, &map); @@ -124,7 +124,7 @@ TEST_F(ParsedOptionsTest, ParsedOptionsGc) { EXPECT_TRUE(map.Exists(Opt::GcOption)); XGcOption xgc = map.GetOrDefault(Opt::GcOption); - EXPECT_EQ(gc::kCollectorTypeMC, xgc.collector_type_); + EXPECT_EQ(gc::kCollectorTypeSS, xgc.collector_type_); } TEST_F(ParsedOptionsTest, ParsedOptionsInstructionSet) { diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc index 4e0bf890db2dfcba7178b451aecdc675e29ba78a..36dea60367e6724745f600c8db1b77fda77ff871 100644 --- a/runtime/proxy_test.cc +++ b/runtime/proxy_test.cc @@ -23,11 +23,24 @@ #include "mirror/field-inl.h" #include "proxy_test.h" #include "scoped_thread_state_change-inl.h" +#include "well_known_classes.h" namespace art { namespace proxy_test { -class ProxyTest : public CommonRuntimeTest {}; +class ProxyTest : public CommonRuntimeTest { + protected: + void SetUp() OVERRIDE { + CommonRuntimeTest::SetUp(); + // The creation of a Proxy class uses WellKnownClasses. These are not normally initialized by + // CommonRuntimeTest so we need to do that now. + WellKnownClasses::Clear(); + WellKnownClasses::Init(art::Thread::Current()->GetJniEnv()); + // Since we aren't actually calling any of the native functions we can just immediately call + // LateInit after calling Init. + WellKnownClasses::LateInit(art::Thread::Current()->GetJniEnv()); + } +}; // Creates a proxy class and check ClassHelper works correctly. TEST_F(ProxyTest, ProxyClassHelper) { @@ -44,9 +57,9 @@ TEST_F(ProxyTest, ProxyClassHelper) { ASSERT_TRUE(I != nullptr); ASSERT_TRUE(J != nullptr); - std::vector interfaces; - interfaces.push_back(I.Get()); - interfaces.push_back(J.Get()); + std::vector> interfaces; + interfaces.push_back(I); + interfaces.push_back(J); Handle proxy_class(hs.NewHandle( GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy1234", interfaces))); interfaces.clear(); // Don't least possibly stale objects in the array as good practice. @@ -80,9 +93,9 @@ TEST_F(ProxyTest, ProxyFieldHelper) { Handle proxyClass; { - std::vector interfaces; - interfaces.push_back(I.Get()); - interfaces.push_back(J.Get()); + std::vector> interfaces; + interfaces.push_back(I); + interfaces.push_back(J); proxyClass = hs.NewHandle( GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy1234", interfaces)); } @@ -131,7 +144,7 @@ TEST_F(ProxyTest, CheckArtMirrorFieldsOfProxyStaticFields) { Handle proxyClass0; Handle proxyClass1; { - std::vector interfaces; + std::vector> interfaces; proxyClass0 = hs.NewHandle( GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy0", interfaces)); proxyClass1 = hs.NewHandle( diff --git a/runtime/proxy_test.h b/runtime/proxy_test.h index b5598232573eecd21dde204f8f3dfcc740573adc..411dc7af829606ebf5f502dd467bbe8518b61dae 100644 --- a/runtime/proxy_test.h +++ b/runtime/proxy_test.h @@ -22,8 +22,10 @@ #include "art_method-inl.h" #include "class_linker-inl.h" +#include "class_root.h" #include "mirror/class-inl.h" #include "mirror/method.h" +#include "obj_ptr-inl.h" namespace art { namespace proxy_test { @@ -31,33 +33,36 @@ namespace proxy_test { // Generate a proxy class with the given name and interfaces. This is a simplification from what // libcore does to fit to our test needs. We do not check for duplicated interfaces or methods and // we do not declare exceptions. -mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa, - jobject jclass_loader, - ClassLinker* class_linker, - const char* className, - const std::vector& interfaces) +ObjPtr GenerateProxyClass(ScopedObjectAccess& soa, + jobject jclass_loader, + ClassLinker* class_linker, + const char* className, + const std::vector>& interfaces) REQUIRES_SHARED(Locks::mutator_lock_) { - mirror::Class* javaLangObject = class_linker->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); + StackHandleScope<1> hs(soa.Self()); + Handle javaLangObject = hs.NewHandle(GetClassRoot()); CHECK(javaLangObject != nullptr); - jclass javaLangClass = soa.AddLocalReference(mirror::Class::GetJavaLangClass()); + jclass javaLangClass = soa.AddLocalReference(GetClassRoot()); // Builds the interfaces array. - jobjectArray proxyClassInterfaces = soa.Env()->NewObjectArray(interfaces.size(), javaLangClass, - nullptr); + jobjectArray proxyClassInterfaces = + soa.Env()->NewObjectArray(interfaces.size(), javaLangClass, /* initialElement */ nullptr); soa.Self()->AssertNoPendingException(); for (size_t i = 0; i < interfaces.size(); ++i) { soa.Env()->SetObjectArrayElement(proxyClassInterfaces, i, - soa.AddLocalReference(interfaces[i])); + soa.AddLocalReference(interfaces[i].Get())); } // Builds the method array. jsize methods_count = 3; // Object.equals, Object.hashCode and Object.toString. - for (mirror::Class* interface : interfaces) { + for (Handle interface : interfaces) { methods_count += interface->NumVirtualMethods(); } jobjectArray proxyClassMethods = soa.Env()->NewObjectArray( - methods_count, soa.AddLocalReference(mirror::Method::StaticClass()), nullptr); + methods_count, + soa.AddLocalReference(GetClassRoot()), + /* initialElement */ nullptr); soa.Self()->AssertNoPendingException(); jsize array_index = 0; @@ -67,7 +72,7 @@ mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa, "equals", "(Ljava/lang/Object;)Z", kRuntimePointerSize); CHECK(method != nullptr); CHECK(!method->IsDirect()); - CHECK(method->GetDeclaringClass() == javaLangObject); + CHECK(method->GetDeclaringClass() == javaLangObject.Get()); DCHECK(!Runtime::Current()->IsActiveTransaction()); soa.Env()->SetObjectArrayElement( proxyClassMethods, array_index++, soa.AddLocalReference( @@ -75,7 +80,7 @@ mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa, method = javaLangObject->FindClassMethod("hashCode", "()I", kRuntimePointerSize); CHECK(method != nullptr); CHECK(!method->IsDirect()); - CHECK(method->GetDeclaringClass() == javaLangObject); + CHECK(method->GetDeclaringClass() == javaLangObject.Get()); soa.Env()->SetObjectArrayElement( proxyClassMethods, array_index++, soa.AddLocalReference( mirror::Method::CreateFromArtMethod(soa.Self(), method))); @@ -83,12 +88,12 @@ mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa, "toString", "()Ljava/lang/String;", kRuntimePointerSize); CHECK(method != nullptr); CHECK(!method->IsDirect()); - CHECK(method->GetDeclaringClass() == javaLangObject); + CHECK(method->GetDeclaringClass() == javaLangObject.Get()); soa.Env()->SetObjectArrayElement( proxyClassMethods, array_index++, soa.AddLocalReference( mirror::Method::CreateFromArtMethod(soa.Self(), method))); // Now adds all interfaces virtual methods. - for (mirror::Class* interface : interfaces) { + for (Handle interface : interfaces) { for (auto& m : interface->GetDeclaredVirtualMethods(kRuntimePointerSize)) { soa.Env()->SetObjectArrayElement( proxyClassMethods, array_index++, soa.AddLocalReference( @@ -101,9 +106,13 @@ mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa, jobjectArray proxyClassThrows = soa.Env()->NewObjectArray(0, javaLangClass, nullptr); soa.Self()->AssertNoPendingException(); - mirror::Class* proxyClass = class_linker->CreateProxyClass( - soa, soa.Env()->NewStringUTF(className), proxyClassInterfaces, jclass_loader, - proxyClassMethods, proxyClassThrows); + ObjPtr proxyClass = class_linker->CreateProxyClass( + soa, + soa.Env()->NewStringUTF(className), + proxyClassInterfaces, + jclass_loader, + proxyClassMethods, + proxyClassThrows); soa.Self()->AssertNoPendingException(); return proxyClass; } diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 077aa33925ae45e7d65370255aa9a78aa975bafc..7f5717f736ceaf6e76902eae0840a575c7314bd7 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -26,6 +26,7 @@ #include "entrypoints/quick/quick_entrypoints_enum.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "handle_scope-inl.h" +#include "interpreter/shadow_frame-inl.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "mirror/class-inl.h" @@ -59,18 +60,24 @@ QuickExceptionHandler::QuickExceptionHandler(Thread* self, bool is_deoptimizatio // Finds catch handler. class CatchBlockStackVisitor FINAL : public StackVisitor { public: - CatchBlockStackVisitor(Thread* self, Context* context, Handle* exception, - QuickExceptionHandler* exception_handler) + CatchBlockStackVisitor(Thread* self, + Context* context, + Handle* exception, + QuickExceptionHandler* exception_handler, + uint32_t skip_frames) REQUIRES_SHARED(Locks::mutator_lock_) : StackVisitor(self, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), exception_(exception), - exception_handler_(exception_handler) { + exception_handler_(exception_handler), + skip_frames_(skip_frames) { } bool VisitFrame() OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { ArtMethod* method = GetMethod(); exception_handler_->SetHandlerFrameDepth(GetFrameDepth()); if (method == nullptr) { + DCHECK_EQ(skip_frames_, 0u) + << "We tried to skip an upcall! We should have returned to the upcall to finish delivery"; // This is the upcall, we remember the frame and last pc so that we may long jump to them. exception_handler_->SetHandlerQuickFramePc(GetCurrentQuickFramePc()); exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame()); @@ -89,6 +96,10 @@ class CatchBlockStackVisitor FINAL : public StackVisitor { } return false; // End stack walk. } + if (skip_frames_ != 0) { + skip_frames_--; + return true; + } if (method->IsRuntimeMethod()) { // Ignore callee save method. DCHECK(method->IsCalleeSaveMethod()); @@ -137,48 +148,115 @@ class CatchBlockStackVisitor FINAL : public StackVisitor { Handle* exception_; // The quick exception handler we're visiting for. QuickExceptionHandler* const exception_handler_; + // The number of frames to skip searching for catches in. + uint32_t skip_frames_; DISALLOW_COPY_AND_ASSIGN(CatchBlockStackVisitor); }; +// Counts instrumentation stack frame prior to catch handler or upcall. +class InstrumentationStackVisitor : public StackVisitor { + public: + InstrumentationStackVisitor(Thread* self, size_t frame_depth) + REQUIRES_SHARED(Locks::mutator_lock_) + : StackVisitor(self, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + frame_depth_(frame_depth), + instrumentation_frames_to_pop_(0) { + CHECK_NE(frame_depth_, kInvalidFrameDepth); + } + + bool VisitFrame() REQUIRES_SHARED(Locks::mutator_lock_) { + size_t current_frame_depth = GetFrameDepth(); + if (current_frame_depth < frame_depth_) { + CHECK(GetMethod() != nullptr); + if (UNLIKELY(reinterpret_cast(GetQuickInstrumentationExitPc()) == GetReturnPc())) { + if (!IsInInlinedFrame()) { + // We do not count inlined frames, because we do not instrument them. The reason we + // include them in the stack walking is the check against `frame_depth_`, which is + // given to us by a visitor that visits inlined frames. + ++instrumentation_frames_to_pop_; + } + } + return true; + } else { + // We reached the frame of the catch handler or the upcall. + return false; + } + } + + size_t GetInstrumentationFramesToPop() const { + return instrumentation_frames_to_pop_; + } + + private: + const size_t frame_depth_; + size_t instrumentation_frames_to_pop_; + + DISALLOW_COPY_AND_ASSIGN(InstrumentationStackVisitor); +}; + +// Finds the appropriate exception catch after calling all method exit instrumentation functions. +// Note that this might change the exception being thrown. void QuickExceptionHandler::FindCatch(ObjPtr exception) { DCHECK(!is_deoptimization_); + instrumentation::InstrumentationStackPopper popper(self_); + // The number of total frames we have so far popped. + uint32_t already_popped = 0; + bool popped_to_top = true; StackHandleScope<1> hs(self_); - Handle exception_ref(hs.NewHandle(exception)); - if (kDebugExceptionDelivery) { - ObjPtr msg = exception_ref->GetDetailMessage(); - std::string str_msg(msg != nullptr ? msg->ToModifiedUtf8() : ""); - self_->DumpStack(LOG_STREAM(INFO) << "Delivering exception: " << exception_ref->PrettyTypeOf() - << ": " << str_msg << "\n"); - } - - // Walk the stack to find catch handler. - CatchBlockStackVisitor visitor(self_, context_, &exception_ref, this); - visitor.WalkStack(true); + MutableHandle exception_ref(hs.NewHandle(exception)); + // Sending the instrumentation events (done by the InstrumentationStackPopper) can cause new + // exceptions to be thrown which will override the current exception. Therefore we need to perform + // the search for a catch in a loop until we have successfully popped all the way to a catch or + // the top of the stack. + do { + if (kDebugExceptionDelivery) { + ObjPtr msg = exception_ref->GetDetailMessage(); + std::string str_msg(msg != nullptr ? msg->ToModifiedUtf8() : ""); + self_->DumpStack(LOG_STREAM(INFO) << "Delivering exception: " << exception_ref->PrettyTypeOf() + << ": " << str_msg << "\n"); + } - if (kDebugExceptionDelivery) { - if (*handler_quick_frame_ == nullptr) { - LOG(INFO) << "Handler is upcall"; + // Walk the stack to find catch handler. + CatchBlockStackVisitor visitor(self_, context_, &exception_ref, this, /*skip*/already_popped); + visitor.WalkStack(true); + uint32_t new_pop_count = handler_frame_depth_; + DCHECK_GE(new_pop_count, already_popped); + already_popped = new_pop_count; + + // Figure out how many of those frames have instrumentation we need to remove (Should be the + // exact same as number of new_pop_count if there aren't inlined frames). + InstrumentationStackVisitor instrumentation_visitor(self_, handler_frame_depth_); + instrumentation_visitor.WalkStack(true); + size_t instrumentation_frames_to_pop = instrumentation_visitor.GetInstrumentationFramesToPop(); + + if (kDebugExceptionDelivery) { + if (*handler_quick_frame_ == nullptr) { + LOG(INFO) << "Handler is upcall"; + } + if (handler_method_ != nullptr) { + const DexFile* dex_file = handler_method_->GetDeclaringClass()->GetDexCache()->GetDexFile(); + int line_number = annotations::GetLineNumFromPC(dex_file, handler_method_, handler_dex_pc_); + LOG(INFO) << "Handler: " << handler_method_->PrettyMethod() << " (line: " + << line_number << ")"; + } + LOG(INFO) << "Will attempt to pop " << instrumentation_frames_to_pop + << " off of the instrumentation stack"; } - if (handler_method_ != nullptr) { - const DexFile* dex_file = handler_method_->GetDeclaringClass()->GetDexCache()->GetDexFile(); - int line_number = annotations::GetLineNumFromPC(dex_file, handler_method_, handler_dex_pc_); - LOG(INFO) << "Handler: " << handler_method_->PrettyMethod() << " (line: " - << line_number << ")"; + // Exception was cleared as part of delivery. + DCHECK(!self_->IsExceptionPending()); + // If the handler is in optimized code, we need to set the catch environment. + if (*handler_quick_frame_ != nullptr && + handler_method_header_ != nullptr && + handler_method_header_->IsOptimized()) { + SetCatchEnvironmentForOptimizedHandler(&visitor); } - } - // Exception was cleared as part of delivery. - DCHECK(!self_->IsExceptionPending()); + popped_to_top = popper.PopFramesTo(instrumentation_frames_to_pop, exception_ref); + } while (!popped_to_top); if (!clear_exception_) { // Put exception back in root set with clear throw location. self_->SetException(exception_ref.Get()); } - // If the handler is in optimized code, we need to set the catch environment. - if (*handler_quick_frame_ != nullptr && - handler_method_header_ != nullptr && - handler_method_header_->IsOptimized()) { - SetCatchEnvironmentForOptimizedHandler(&visitor); - } } static VRegKind ToVRegKind(DexRegisterLocation::Kind kind) { @@ -224,30 +302,27 @@ void QuickExceptionHandler::SetCatchEnvironmentForOptimizedHandler(StackVisitor* CodeItemDataAccessor accessor(handler_method_->DexInstructionData()); const size_t number_of_vregs = accessor.RegistersSize(); - CodeInfo code_info = handler_method_header_->GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); + CodeInfo code_info(handler_method_header_); // Find stack map of the catch block. - StackMap catch_stack_map = code_info.GetCatchStackMapForDexPc(GetHandlerDexPc(), encoding); + StackMap catch_stack_map = code_info.GetCatchStackMapForDexPc(GetHandlerDexPc()); DCHECK(catch_stack_map.IsValid()); - DexRegisterMap catch_vreg_map = - code_info.GetDexRegisterMapOf(catch_stack_map, encoding, number_of_vregs); - if (!catch_vreg_map.IsValid()) { + DexRegisterMap catch_vreg_map = code_info.GetDexRegisterMapOf(catch_stack_map); + if (!catch_vreg_map.HasAnyLiveDexRegisters()) { return; } + DCHECK_EQ(catch_vreg_map.size(), number_of_vregs); // Find stack map of the throwing instruction. StackMap throw_stack_map = - code_info.GetStackMapForNativePcOffset(stack_visitor->GetNativePcOffset(), encoding); + code_info.GetStackMapForNativePcOffset(stack_visitor->GetNativePcOffset()); DCHECK(throw_stack_map.IsValid()); - DexRegisterMap throw_vreg_map = - code_info.GetDexRegisterMapOf(throw_stack_map, encoding, number_of_vregs); - DCHECK(throw_vreg_map.IsValid()); + DexRegisterMap throw_vreg_map = code_info.GetDexRegisterMapOf(throw_stack_map); + DCHECK_EQ(throw_vreg_map.size(), number_of_vregs); // Copy values between them. for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) { - DexRegisterLocation::Kind catch_location = - catch_vreg_map.GetLocationKind(vreg, number_of_vregs, code_info, encoding); + DexRegisterLocation::Kind catch_location = catch_vreg_map[vreg].GetKind(); if (catch_location == DexRegisterLocation::Kind::kNone) { continue; } @@ -255,10 +330,7 @@ void QuickExceptionHandler::SetCatchEnvironmentForOptimizedHandler(StackVisitor* // Get vreg value from its current location. uint32_t vreg_value; - VRegKind vreg_kind = ToVRegKind(throw_vreg_map.GetLocationKind(vreg, - number_of_vregs, - code_info, - encoding)); + VRegKind vreg_kind = ToVRegKind(throw_vreg_map[vreg].GetKind()); bool get_vreg_success = stack_visitor->GetVReg(stack_visitor->GetMethod(), vreg, vreg_kind, @@ -269,10 +341,7 @@ void QuickExceptionHandler::SetCatchEnvironmentForOptimizedHandler(StackVisitor* << "native_pc_offset=" << stack_visitor->GetNativePcOffset() << ")"; // Copy value to the catch phi's stack slot. - int32_t slot_offset = catch_vreg_map.GetStackOffsetInBytes(vreg, - number_of_vregs, - code_info, - encoding); + int32_t slot_offset = catch_vreg_map[vreg].GetStackOffsetInBytes(); ArtMethod** frame_top = stack_visitor->GetCurrentQuickFrame(); uint8_t* slot_address = reinterpret_cast(frame_top) + slot_offset; uint32_t* slot_ptr = reinterpret_cast(slot_address); @@ -404,24 +473,20 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { const bool* updated_vregs) REQUIRES_SHARED(Locks::mutator_lock_) { const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader(); - CodeInfo code_info = method_header->GetOptimizedCodeInfo(); + CodeInfo code_info(method_header); uintptr_t native_pc_offset = method_header->NativeQuickPcOffset(GetCurrentQuickFramePc()); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); CodeItemDataAccessor accessor(m->DexInstructionData()); const size_t number_of_vregs = accessor.RegistersSize(); - uint32_t register_mask = code_info.GetRegisterMaskOf(encoding, stack_map); - BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, stack_map); + uint32_t register_mask = code_info.GetRegisterMaskOf(stack_map); + BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map); DexRegisterMap vreg_map = IsInInlinedFrame() - ? code_info.GetDexRegisterMapAtDepth(GetCurrentInliningDepth() - 1, - code_info.GetInlineInfoOf(stack_map, encoding), - encoding, - number_of_vregs) - : code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_vregs); - - if (!vreg_map.IsValid()) { + ? code_info.GetInlineDexRegisterMapOf(stack_map, GetCurrentInlinedFrame()) + : code_info.GetDexRegisterMapOf(stack_map); + if (vreg_map.empty()) { return; } + DCHECK_EQ(vreg_map.size(), number_of_vregs); for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) { if (updated_vregs != nullptr && updated_vregs[vreg]) { @@ -429,22 +494,18 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { continue; } - DexRegisterLocation::Kind location = - vreg_map.GetLocationKind(vreg, number_of_vregs, code_info, encoding); + DexRegisterLocation::Kind location = vreg_map[vreg].GetKind(); static constexpr uint32_t kDeadValue = 0xEBADDE09; uint32_t value = kDeadValue; bool is_reference = false; switch (location) { case DexRegisterLocation::Kind::kInStack: { - const int32_t offset = vreg_map.GetStackOffsetInBytes(vreg, - number_of_vregs, - code_info, - encoding); + const int32_t offset = vreg_map[vreg].GetStackOffsetInBytes(); const uint8_t* addr = reinterpret_cast(GetCurrentQuickFrame()) + offset; value = *reinterpret_cast(addr); uint32_t bit = (offset >> 2); - if (bit < encoding.stack_mask.encoding.BitSize() && stack_mask.LoadBit(bit)) { + if (bit < stack_mask.size_in_bits() && stack_mask.LoadBit(bit)) { is_reference = true; } break; @@ -453,7 +514,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { case DexRegisterLocation::Kind::kInRegisterHigh: case DexRegisterLocation::Kind::kInFpuRegister: case DexRegisterLocation::Kind::kInFpuRegisterHigh: { - uint32_t reg = vreg_map.GetMachineRegister(vreg, number_of_vregs, code_info, encoding); + uint32_t reg = vreg_map[vreg].GetMachineRegister(); bool result = GetRegisterIfAccessible(reg, ToVRegKind(location), &value); CHECK(result); if (location == DexRegisterLocation::Kind::kInRegister) { @@ -464,7 +525,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { break; } case DexRegisterLocation::Kind::kConstant: { - value = vreg_map.GetConstant(vreg, number_of_vregs, code_info, encoding); + value = vreg_map[vreg].GetConstant(); if (value == 0) { // Make it a reference for extra safety. is_reference = true; @@ -475,12 +536,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { break; } default: { - LOG(FATAL) - << "Unexpected location kind " - << vreg_map.GetLocationInternalKind(vreg, - number_of_vregs, - code_info, - encoding); + LOG(FATAL) << "Unexpected location kind " << vreg_map[vreg].GetKind(); UNREACHABLE(); } } @@ -493,7 +549,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { } static VRegKind GetVRegKind(uint16_t reg, const std::vector& kinds) { - return static_cast(kinds.at(reg * 2)); + return static_cast(kinds[reg * 2]); } QuickExceptionHandler* const exception_handler_; @@ -582,48 +638,8 @@ void QuickExceptionHandler::DeoptimizePartialFragmentFixup(uintptr_t return_pc) } } -// Unwinds all instrumentation stack frame prior to catch handler or upcall. -class InstrumentationStackVisitor : public StackVisitor { - public: - InstrumentationStackVisitor(Thread* self, size_t frame_depth) - REQUIRES_SHARED(Locks::mutator_lock_) - : StackVisitor(self, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), - frame_depth_(frame_depth), - instrumentation_frames_to_pop_(0) { - CHECK_NE(frame_depth_, kInvalidFrameDepth); - } - - bool VisitFrame() REQUIRES_SHARED(Locks::mutator_lock_) { - size_t current_frame_depth = GetFrameDepth(); - if (current_frame_depth < frame_depth_) { - CHECK(GetMethod() != nullptr); - if (UNLIKELY(reinterpret_cast(GetQuickInstrumentationExitPc()) == GetReturnPc())) { - if (!IsInInlinedFrame()) { - // We do not count inlined frames, because we do not instrument them. The reason we - // include them in the stack walking is the check against `frame_depth_`, which is - // given to us by a visitor that visits inlined frames. - ++instrumentation_frames_to_pop_; - } - } - return true; - } else { - // We reached the frame of the catch handler or the upcall. - return false; - } - } - - size_t GetInstrumentationFramesToPop() const { - return instrumentation_frames_to_pop_; - } - - private: - const size_t frame_depth_; - size_t instrumentation_frames_to_pop_; - - DISALLOW_COPY_AND_ASSIGN(InstrumentationStackVisitor); -}; - uintptr_t QuickExceptionHandler::UpdateInstrumentationStack() { + DCHECK(is_deoptimization_) << "Non-deoptimization handlers should use FindCatch"; uintptr_t return_pc = 0; if (method_tracing_active_) { InstrumentationStackVisitor visitor(self_, handler_frame_depth_); @@ -631,9 +647,7 @@ uintptr_t QuickExceptionHandler::UpdateInstrumentationStack() { size_t instrumentation_frames_to_pop = visitor.GetInstrumentationFramesToPop(); instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); - for (size_t i = 0; i < instrumentation_frames_to_pop; ++i) { - return_pc = instrumentation->PopMethodForUnwind(self_, is_deoptimization_); - } + return_pc = instrumentation->PopFramesForDeoptimization(self_, instrumentation_frames_to_pop); } return return_pc; } diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h index 1103dab52ceec5f002e60c1ab2b02510a2a6aed0..5579d368a3be54e0d66b03af53394da6f5b5b3a9 100644 --- a/runtime/quick_exception_handler.h +++ b/runtime/quick_exception_handler.h @@ -47,7 +47,8 @@ class QuickExceptionHandler { UNREACHABLE(); } - // Find the catch handler for the given exception. + // Find the catch handler for the given exception and call all required Instrumentation methods. + // Note this might result in the exception being caught being different from 'exception'. void FindCatch(ObjPtr exception) REQUIRES_SHARED(Locks::mutator_lock_); // Deoptimize the stack to the upcall/some code that's not deoptimizeable. For diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h index 58f6c04c3e8f9f1c092567f61d4c385863c87b0f..640fa7e39340ee02d99ececacc4ece404ecd45a8 100644 --- a/runtime/read_barrier-inl.h +++ b/runtime/read_barrier-inl.h @@ -64,8 +64,11 @@ inline MirrorType* ReadBarrier::Barrier( // If kAlwaysUpdateField is true, update the field atomically. This may fail if mutator // updates before us, but it's OK. if (kAlwaysUpdateField && ref != old_ref) { - obj->CasFieldStrongReleaseObjectWithoutWriteBarrier( - offset, old_ref, ref); + obj->CasFieldObjectWithoutWriteBarrier(offset, + old_ref, + ref, + CASMode::kStrong, + std::memory_order_release); } } AssertToSpaceInvariant(obj, offset, ref); @@ -82,8 +85,11 @@ inline MirrorType* ReadBarrier::Barrier( ref = reinterpret_cast(Mark(old_ref)); // Update the field atomically. This may fail if mutator updates before us, but it's ok. if (ref != old_ref) { - obj->CasFieldStrongReleaseObjectWithoutWriteBarrier( - offset, old_ref, ref); + obj->CasFieldObjectWithoutWriteBarrier(offset, + old_ref, + ref, + CASMode::kStrong, + std::memory_order_release); } } AssertToSpaceInvariant(obj, offset, ref); @@ -130,7 +136,7 @@ inline MirrorType* ReadBarrier::BarrierForRoot(MirrorType** root, ref = reinterpret_cast(Mark(old_ref)); // Update the field atomically. This may fail if mutator updates before us, but it's ok. if (ref != old_ref) { - Atomic* atomic_root = reinterpret_cast*>(root); + Atomic* atomic_root = reinterpret_cast*>(root); atomic_root->CompareAndSetStrongRelaxed(old_ref, ref); } } diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc index 06ea384aa3f8298455edbc80bd30fc0b5ce1ddae..1d54d21187ef80e8d82173e1ef22f2ae86710047 100644 --- a/runtime/reference_table_test.cc +++ b/runtime/reference_table_test.cc @@ -106,7 +106,7 @@ TEST_F(ReferenceTableTest, Basics) { } // Add a second object 10 times and check dumping is sane. - mirror::Object* o2 = mirror::ShortArray::Alloc(soa.Self(), 0); + ObjPtr o2 = mirror::ShortArray::Alloc(soa.Self(), 0); for (size_t i = 0; i < 10; ++i) { rt.Add(o2); EXPECT_EQ(i + 2, rt.Size()); @@ -276,23 +276,26 @@ TEST_F(ReferenceTableTest, SummaryOrder) { ReferenceTable rt("test", 0, 20); { - mirror::Object* s1 = mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello"); - mirror::Object* s2 = mirror::String::AllocFromModifiedUtf8(soa.Self(), "world"); + StackHandleScope<1> hs(soa.Self()); + Handle s1 = + hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello")); + ObjPtr s2 = mirror::String::AllocFromModifiedUtf8(soa.Self(), "world"); // 3 copies of s1, 2 copies of s2, interleaved. for (size_t i = 0; i != 2; ++i) { - rt.Add(s1); + rt.Add(s1.Get()); rt.Add(s2); } - rt.Add(s1); + rt.Add(s1.Get()); } { - // Differently sized byte arrays. Should be sorted by identical (non-unique cound). - mirror::Object* b1_1 = mirror::ByteArray::Alloc(soa.Self(), 1); - rt.Add(b1_1); + // Differently sized byte arrays. Should be sorted by identical (non-unique count). + StackHandleScope<1> hs(soa.Self()); + Handle b1_1 = hs.NewHandle(mirror::ByteArray::Alloc(soa.Self(), 1)); + rt.Add(b1_1.Get()); rt.Add(mirror::ByteArray::Alloc(soa.Self(), 2)); - rt.Add(b1_1); + rt.Add(b1_1.Get()); rt.Add(mirror::ByteArray::Alloc(soa.Self(), 2)); rt.Add(mirror::ByteArray::Alloc(soa.Self(), 1)); rt.Add(mirror::ByteArray::Alloc(soa.Self(), 2)); diff --git a/runtime/reflection-inl.h b/runtime/reflection-inl.h index 26fb021903f3b834208ab25d546a11b8a61277f8..9fe4bca9dd02799c59564313a481720e34fb8a8b 100644 --- a/runtime/reflection-inl.h +++ b/runtime/reflection-inl.h @@ -121,7 +121,7 @@ inline bool VerifyObjectIsClass(ObjPtr o, ObjPtr if (UNLIKELY(o == nullptr)) { ThrowNullPointerException("null receiver"); return false; - } else if (UNLIKELY(!o->InstanceOf(c.Ptr()))) { + } else if (UNLIKELY(!o->InstanceOf(c))) { InvalidReceiverError(o, c); return false; } diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 068bc285e521579404c5b17b2c9eea1fd679a47b..646de757e0a54a7cd69b98779ac954a1e64c50f2 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -23,8 +23,9 @@ #include "common_throws.h" #include "dex/dex_file-inl.h" #include "indirect_reference_table-inl.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" +#include "jvalue-inl.h" #include "mirror/class-inl.h" #include "mirror/executable.h" #include "mirror/object_array-inl.h" @@ -137,7 +138,7 @@ class ArgArray { } void BuildArgArrayFromJValues(const ScopedObjectAccessAlreadyRunnable& soa, - ObjPtr receiver, jvalue* args) + ObjPtr receiver, const jvalue* args) REQUIRES_SHARED(Locks::mutator_lock_) { // Set receiver if non-null (method is not static) if (receiver != nullptr) { @@ -242,7 +243,7 @@ class ArgArray { // we've seen cases where it's not b/34440020. ObjPtr dst_class( m->ResolveClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_)); - if (dst_class.Ptr() == nullptr) { + if (dst_class == nullptr) { CHECK(self->IsExceptionPending()); return false; } @@ -456,6 +457,64 @@ void InvokeWithArgArray(const ScopedObjectAccessAlreadyRunnable& soa, method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty); } +ALWAYS_INLINE +bool CheckArgsForInvokeMethod(ArtMethod* np_method, + ObjPtr> objects) + REQUIRES_SHARED(Locks::mutator_lock_) { + const DexFile::TypeList* classes = np_method->GetParameterTypeList(); + uint32_t classes_size = (classes == nullptr) ? 0 : classes->Size(); + uint32_t arg_count = (objects == nullptr) ? 0 : objects->GetLength(); + if (UNLIKELY(arg_count != classes_size)) { + ThrowIllegalArgumentException(StringPrintf("Wrong number of arguments; expected %d, got %d", + classes_size, arg_count).c_str()); + return false; + } + return true; +} + +ALWAYS_INLINE +bool InvokeMethodImpl(const ScopedObjectAccessAlreadyRunnable& soa, + ArtMethod* m, + ArtMethod* np_method, + ObjPtr receiver, + ObjPtr> objects, + const char** shorty, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { + // Invoke the method. + uint32_t shorty_len = 0; + *shorty = np_method->GetShorty(&shorty_len); + ArgArray arg_array(*shorty, shorty_len); + if (!arg_array.BuildArgArrayFromObjectArray(receiver, objects, np_method, soa.Self())) { + CHECK(soa.Self()->IsExceptionPending()); + return false; + } + + InvokeWithArgArray(soa, m, &arg_array, result, *shorty); + + // Wrap any exception with "Ljava/lang/reflect/InvocationTargetException;" and return early. + if (soa.Self()->IsExceptionPending()) { + // If we get another exception when we are trying to wrap, then just use that instead. + ScopedLocalRef th(soa.Env(), soa.Env()->ExceptionOccurred()); + soa.Self()->ClearException(); + jclass exception_class = soa.Env()->FindClass("java/lang/reflect/InvocationTargetException"); + if (exception_class == nullptr) { + soa.Self()->AssertPendingException(); + return false; + } + jmethodID mid = soa.Env()->GetMethodID(exception_class, "", "(Ljava/lang/Throwable;)V"); + CHECK(mid != nullptr); + jobject exception_instance = soa.Env()->NewObject(exception_class, mid, th.get()); + if (exception_instance == nullptr) { + soa.Self()->AssertPendingException(); + return false; + } + soa.Env()->Throw(reinterpret_cast(exception_instance)); + return false; + } + + return true; +} + } // anonymous namespace JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid, @@ -491,7 +550,7 @@ JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, jobject o } JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid, - jvalue* args) { + const jvalue* args) { // We want to make sure that the stack is not within a small distance from the // protected region in case we are calling into a leaf function whose stack // check has been elided. @@ -522,7 +581,7 @@ JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, jobject o } JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, - jobject obj, jmethodID mid, jvalue* args) { + jobject obj, jmethodID mid, const jvalue* args) { // We want to make sure that the stack is not within a small distance from the // protected region in case we are calling into a leaf function whose stack // check has been elided. @@ -631,12 +690,7 @@ jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaM ObjPtr> objects = soa.Decode>(javaArgs); auto* np_method = m->GetInterfaceMethodIfProxy(kRuntimePointerSize); - const DexFile::TypeList* classes = np_method->GetParameterTypeList(); - uint32_t classes_size = (classes == nullptr) ? 0 : classes->Size(); - uint32_t arg_count = (objects != nullptr) ? objects->GetLength() : 0; - if (arg_count != classes_size) { - ThrowIllegalArgumentException(StringPrintf("Wrong number of arguments; expected %d, got %d", - classes_size, arg_count).c_str()); + if (!CheckArgsForInvokeMethod(np_method, objects)) { return nullptr; } @@ -660,39 +714,54 @@ jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaM // Invoke the method. JValue result; - uint32_t shorty_len = 0; - const char* shorty = np_method->GetShorty(&shorty_len); - ArgArray arg_array(shorty, shorty_len); - if (!arg_array.BuildArgArrayFromObjectArray(receiver, objects, np_method, soa.Self())) { - CHECK(soa.Self()->IsExceptionPending()); + const char* shorty; + if (!InvokeMethodImpl(soa, m, np_method, receiver, objects, &shorty, &result)) { return nullptr; } + return soa.AddLocalReference(BoxPrimitive(Primitive::GetType(shorty[0]), result)); +} - InvokeWithArgArray(soa, m, &arg_array, &result, shorty); +void InvokeConstructor(const ScopedObjectAccessAlreadyRunnable& soa, + ArtMethod* constructor, + ObjPtr receiver, + jobject javaArgs) { + // We want to make sure that the stack is not within a small distance from the + // protected region in case we are calling into a leaf function whose stack + // check has been elided. + if (UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEndForInterpreter(true))) { + ThrowStackOverflowError(soa.Self()); + return; + } - // Wrap any exception with "Ljava/lang/reflect/InvocationTargetException;" and return early. - if (soa.Self()->IsExceptionPending()) { - // If we get another exception when we are trying to wrap, then just use that instead. - ScopedLocalRef th(soa.Env(), soa.Env()->ExceptionOccurred()); - soa.Self()->ClearException(); - jclass exception_class = soa.Env()->FindClass("java/lang/reflect/InvocationTargetException"); - if (exception_class == nullptr) { - soa.Self()->AssertPendingException(); - return nullptr; - } - jmethodID mid = soa.Env()->GetMethodID(exception_class, "", "(Ljava/lang/Throwable;)V"); - CHECK(mid != nullptr); - jobject exception_instance = soa.Env()->NewObject(exception_class, mid, th.get()); - if (exception_instance == nullptr) { - soa.Self()->AssertPendingException(); - return nullptr; - } - soa.Env()->Throw(reinterpret_cast(exception_instance)); - return nullptr; + if (kIsDebugBuild) { + CHECK(constructor->IsConstructor()); + + ObjPtr declaring_class = constructor->GetDeclaringClass(); + CHECK(declaring_class->IsInitialized()); + + // Calls to String. should have been repplaced with with equivalent StringFactory calls. + CHECK(!declaring_class->IsStringClass()); + + // Check that the receiver is non-null and an instance of the field's declaring class. + CHECK(receiver != nullptr); + CHECK(VerifyObjectIsClass(receiver, declaring_class)); + CHECK_EQ(constructor, + receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(constructor, + kRuntimePointerSize)); } - // Box if necessary and return. - return soa.AddLocalReference(BoxPrimitive(Primitive::GetType(shorty[0]), result)); + // Get our arrays of arguments and their types, and check they're the same size. + ObjPtr> objects = + soa.Decode>(javaArgs); + ArtMethod* np_method = constructor->GetInterfaceMethodIfProxy(kRuntimePointerSize); + if (!CheckArgsForInvokeMethod(np_method, objects)) { + return; + } + + // Invoke the constructor. + JValue result; + const char* shorty; + InvokeMethodImpl(soa, constructor, np_method, receiver, objects, &shorty, &result); } ObjPtr BoxPrimitive(Primitive::Type src_class, const JValue& value) { diff --git a/runtime/reflection.h b/runtime/reflection.h index 4560a3969efaaa9dc09efa954aca2b2cd7a09e20..74580a21e0e0c026405a8082c8b6c57df9ea4f59 100644 --- a/runtime/reflection.h +++ b/runtime/reflection.h @@ -69,13 +69,13 @@ JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid, - jvalue* args) + const jvalue* args) REQUIRES_SHARED(Locks::mutator_lock_); JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid, - jvalue* args) + const jvalue* args) REQUIRES_SHARED(Locks::mutator_lock_); JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, @@ -92,6 +92,14 @@ jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, size_t num_frames = 1) REQUIRES_SHARED(Locks::mutator_lock_); +// Special-casing of the above. Assumes that the method is the correct constructor, the class is +// initialized, and that the receiver is an instance of the class. +void InvokeConstructor(const ScopedObjectAccessAlreadyRunnable& soa, + ArtMethod* constructor, + ObjPtr receiver, + jobject args) + REQUIRES_SHARED(Locks::mutator_lock_); + ALWAYS_INLINE bool VerifyObjectIsClass(ObjPtr o, ObjPtr c) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/reflection_test.cc b/runtime/reflection_test.cc index 7b36c7368e6ae44df53d52f0c74ff5c51e24a2fa..424ee0681a19a1c2feb6955e18e22ce17dcb1cde 100644 --- a/runtime/reflection_test.cc +++ b/runtime/reflection_test.cc @@ -23,8 +23,8 @@ #include "base/enums.h" #include "common_compiler_test.h" #include "dex/descriptors_names.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "nativehelper/scoped_local_ref.h" #include "scoped_thread_state_change-inl.h" @@ -80,7 +80,7 @@ class ReflectionTest : public CommonCompilerTest { jclass GetPrimitiveClass(char descriptor) { ScopedObjectAccess soa(env_); - mirror::Class* c = class_linker_->FindPrimitiveClass(descriptor); + ObjPtr c = class_linker_->FindPrimitiveClass(descriptor); CHECK(c != nullptr); return soa.AddLocalReference(c); } @@ -518,7 +518,7 @@ TEST_F(ReflectionTest, StaticMainMethod) { hs.NewHandle(soa.Decode(jclass_loader))); CompileDirectMethod(class_loader, "Main", "main", "([Ljava/lang/String;)V"); - mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader); + ObjPtr klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader); ASSERT_TRUE(klass != nullptr); ArtMethod* method = klass->FindClassMethod("main", diff --git a/runtime/runtime-inl.h b/runtime/runtime-inl.h index 4584351a6a28c9045ebb1216b570a28ce571ee2f..374591eeb0e559dac9867844ecd0d2f3465ba9fc 100644 --- a/runtime/runtime-inl.h +++ b/runtime/runtime-inl.h @@ -19,8 +19,10 @@ #include "runtime.h" +#include "arch/instruction_set.h" #include "art_method.h" #include "base/callee_save_type.h" +#include "entrypoints/quick/callee_save_frame.h" #include "gc_root-inl.h" #include "obj_ptr-inl.h" @@ -38,21 +40,22 @@ inline mirror::Object* Runtime::GetClearedJniWeakGlobal() { inline QuickMethodFrameInfo Runtime::GetRuntimeMethodFrameInfo(ArtMethod* method) { DCHECK(method != nullptr); + DCHECK_EQ(instruction_set_, kRuntimeISA); // Cannot be imt-conflict-method or resolution-method. DCHECK_NE(method, GetImtConflictMethod()); DCHECK_NE(method, GetResolutionMethod()); // Don't use GetCalleeSaveMethod(), some tests don't set all callee save methods. if (method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveRefsAndArgs)) { - return GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); + return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); } else if (method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveAllCalleeSaves)) { - return GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveAllCalleeSaves); + return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveAllCalleeSaves); } else if (method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveRefsOnly)) { - return GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsOnly); + return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveRefsOnly); } else { DCHECK(method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveEverything) || method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveEverythingForClinit) || method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveEverythingForSuspendCheck)); - return GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveEverything); + return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveEverything); } } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 7696729cddca0dc3900cd93112550d6b1357d7f4..a81c4d051834e5eb8b9e49f3d3a5ce4d070e97c0 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -39,18 +39,12 @@ #include "android-base/strings.h" #include "aot_class_linker.h" -#include "arch/arm/quick_method_frame_info_arm.h" #include "arch/arm/registers_arm.h" -#include "arch/arm64/quick_method_frame_info_arm64.h" #include "arch/arm64/registers_arm64.h" #include "arch/instruction_set_features.h" -#include "arch/mips/quick_method_frame_info_mips.h" #include "arch/mips/registers_mips.h" -#include "arch/mips64/quick_method_frame_info_mips64.h" #include "arch/mips64/registers_mips64.h" -#include "arch/x86/quick_method_frame_info_x86.h" #include "arch/x86/registers_x86.h" -#include "arch/x86_64/quick_method_frame_info_x86_64.h" #include "arch/x86_64/registers_x86_64.h" #include "art_field-inl.h" #include "art_method-inl.h" @@ -62,6 +56,8 @@ #include "base/dumpable.h" #include "base/enums.h" #include "base/file_utils.h" +#include "base/malloc_arena_pool.h" +#include "base/mem_map_arena_pool.h" #include "base/memory_tool.h" #include "base/mutex.h" #include "base/os.h" @@ -71,6 +67,7 @@ #include "base/unix_file/fd_file.h" #include "base/utils.h" #include "class_linker-inl.h" +#include "class_root.h" #include "compiler_callbacks.h" #include "debugger.h" #include "dex/art_dex_file_loader.h" @@ -91,11 +88,11 @@ #include "instrumentation.h" #include "intern_table.h" #include "interpreter/interpreter.h" -#include "java_vm_ext.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "jit/profile_saver.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "linear_alloc.h" #include "memory_representation.h" #include "mirror/array.h" @@ -230,7 +227,6 @@ Runtime::Runtime() intern_table_(nullptr), class_linker_(nullptr), signal_catcher_(nullptr), - use_tombstoned_traces_(false), java_vm_(nullptr), fault_message_lock_("Fault message lock"), fault_message_(""), @@ -244,7 +240,7 @@ Runtime::Runtime() 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), @@ -574,19 +570,7 @@ void Runtime::Abort(const char* msg) { LOG(FATAL_WITHOUT_ABORT) << "Unexpectedly returned from abort hook!"; } -#if defined(__GLIBC__) - // TODO: we ought to be able to use pthread_kill(3) here (or abort(3), - // which POSIX defines in terms of raise(3), which POSIX defines in terms - // of pthread_kill(3)). On Linux, though, libcorkscrew can't unwind through - // libpthread, which means the stacks we dump would be useless. Calling - // tgkill(2) directly avoids that. - syscall(__NR_tgkill, getpid(), GetTid(), SIGABRT); - // TODO: LLVM installs it's own SIGABRT handler so exit to be safe... Can we disable that in LLVM? - // If not, we could use sigaction(3) before calling tgkill(2) and lose this call to exit(3). - exit(1); -#else abort(); -#endif // notreached } @@ -729,6 +713,23 @@ std::string Runtime::GetCompilerExecutable() const { return compiler_executable; } +void Runtime::RunRootClinits(Thread* self) { + class_linker_->RunRootClinits(self); + + GcRoot* exceptions[] = { + &pre_allocated_OutOfMemoryError_when_throwing_exception_, + // &pre_allocated_OutOfMemoryError_when_throwing_oome_, // Same class as above. + // &pre_allocated_OutOfMemoryError_when_handling_stack_overflow_, // Same class as above. + &pre_allocated_NoClassDefFoundError_, + }; + for (GcRoot* exception : exceptions) { + StackHandleScope<1> hs(self); + Handle klass = hs.NewHandle(exception->Read()->GetClass()); + class_linker_->EnsureInitialized(self, klass, true, true); + self->AssertNoPendingException(); + } +} + bool Runtime::Start() { VLOG(startup) << "Runtime::Start entering"; @@ -753,12 +754,15 @@ bool Runtime::Start() { ScopedObjectAccess soa(self); StackHandleScope<2> hs(soa.Self()); - auto class_class(hs.NewHandle(mirror::Class::GetJavaLangClass())); - auto field_class(hs.NewHandle(mirror::Field::StaticClass())); + ObjPtr> class_roots = GetClassLinker()->GetClassRoots(); + auto class_class(hs.NewHandle(GetClassRoot(class_roots))); + auto field_class(hs.NewHandle(GetClassRoot(class_roots))); class_linker_->EnsureInitialized(soa.Self(), class_class, true, true); + self->AssertNoPendingException(); // Field class is needed for register_java_net_InetAddress in libcore, b/28153851. class_linker_->EnsureInitialized(soa.Self(), field_class, true, true); + self->AssertNoPendingException(); } // InitNativeMethods needs to be after started_ so that the classes @@ -841,7 +845,6 @@ bool Runtime::Start() { if (trace_config_.get() != nullptr && trace_config_->trace_file != "") { ScopedThreadStateChange tsc(self, kWaitingForMethodTracingStart); Trace::Start(trace_config_->trace_file.c_str(), - -1, static_cast(trace_config_->trace_file_size), 0, trace_config_->trace_output_mode, @@ -927,7 +930,7 @@ void Runtime::InitNonZygoteOrPostFork( void Runtime::StartSignalCatcher() { if (!is_zygote_) { - signal_catcher_ = new SignalCatcher(stack_trace_file_, use_tombstoned_traces_); + signal_catcher_ = new SignalCatcher(); } } @@ -1036,7 +1039,7 @@ static bool OpenDexFilesFromImage(const std::string& image_location, return false; } - for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) { + for (const OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) { if (oat_dex_file == nullptr) { *failures += 1; continue; @@ -1106,6 +1109,32 @@ void Runtime::SetSentinel(mirror::Object* sentinel) { sentinel_ = GcRoot(sentinel); } +static inline void CreatePreAllocatedException(Thread* self, + Runtime* runtime, + GcRoot* exception, + const char* exception_class_descriptor, + const char* msg) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_EQ(self, Thread::Current()); + ClassLinker* class_linker = runtime->GetClassLinker(); + // Allocate an object without initializing the class to allow non-trivial Throwable.(). + ObjPtr klass = class_linker->FindSystemClass(self, exception_class_descriptor); + CHECK(klass != nullptr); + gc::AllocatorType allocator_type = runtime->GetHeap()->GetCurrentAllocator(); + ObjPtr exception_object = ObjPtr::DownCast( + klass->Alloc(self, allocator_type)); + CHECK(exception_object != nullptr); + *exception = GcRoot(exception_object); + // Initialize the "detailMessage" field. + ObjPtr message = mirror::String::AllocFromModifiedUtf8(self, msg); + CHECK(message != nullptr); + ObjPtr throwable = GetClassRoot(class_linker); + ArtField* detailMessageField = + throwable->FindDeclaredInstanceField("detailMessage", "Ljava/lang/String;"); + CHECK(detailMessageField != nullptr); + detailMessageField->SetObject(exception->Read(), message); +} + bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // (b/30160149): protect subprocesses from modifications to LD_LIBRARY_PATH, etc. // Take a snapshot of the environment at the time the runtime was created, for use by Exec, etc. @@ -1175,12 +1204,6 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { abort_ = runtime_options.GetOrDefault(Opt::HookAbort); default_stack_size_ = runtime_options.GetOrDefault(Opt::StackSize); - use_tombstoned_traces_ = runtime_options.GetOrDefault(Opt::UseTombstonedTraces); -#if !defined(ART_TARGET_ANDROID) - CHECK(!use_tombstoned_traces_) - << "-Xusetombstonedtraces is only supported in an Android environment"; -#endif - stack_trace_file_ = runtime_options.ReleaseOrDefault(Opt::StackTraceFile); compiler_executable_ = runtime_options.ReleaseOrDefault(Opt::Compiler); compiler_options_ = runtime_options.ReleaseOrDefault(Opt::CompilerOptions); @@ -1294,7 +1317,8 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { dump_gc_performance_on_shutdown_ = runtime_options.Exists(Opt::DumpGCPerformanceOnShutdown); jdwp_options_ = runtime_options.GetOrDefault(Opt::JdwpOptions); - jdwp_provider_ = runtime_options.GetOrDefault(Opt::JdwpProvider); + jdwp_provider_ = CanonicalizeJdwpProvider(runtime_options.GetOrDefault(Opt::JdwpProvider), + IsJavaDebuggable()); switch (jdwp_provider_) { case JdwpProvider::kNone: { VLOG(jdwp) << "Disabling all JDWP support."; @@ -1328,6 +1352,11 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { constexpr const char* plugin_name = kIsDebugBuild ? "libadbconnectiond.so" : "libadbconnection.so"; plugins_.push_back(Plugin::Create(plugin_name)); + break; + } + case JdwpProvider::kUnset: { + LOG(FATAL) << "Illegal jdwp provider " << jdwp_provider_ << " was not filtered out!"; + break; } } callbacks_->AddThreadLifecycleCallback(Dbg::GetThreadLifecycleCallback()); @@ -1346,13 +1375,17 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // Use MemMap arena pool for jit, malloc otherwise. Malloc arenas are faster to allocate but // can't be trimmed as easily. const bool use_malloc = IsAotCompiler(); - arena_pool_.reset(new ArenaPool(use_malloc, /* low_4gb */ false)); - jit_arena_pool_.reset( - new ArenaPool(/* use_malloc */ false, /* low_4gb */ false, "CompilerMetadata")); + if (use_malloc) { + arena_pool_.reset(new MallocArenaPool()); + jit_arena_pool_.reset(new MallocArenaPool()); + } else { + arena_pool_.reset(new MemMapArenaPool(/* low_4gb */ false)); + jit_arena_pool_.reset(new MemMapArenaPool(/* low_4gb */ false, "CompilerMetadata")); + } if (IsAotCompiler() && Is64BitInstructionSet(kRuntimeISA)) { // 4gb, no malloc. Explanation in header. - low_4gb_arena_pool_.reset(new ArenaPool(/* use_malloc */ false, /* low_4gb */ true)); + low_4gb_arena_pool_.reset(new MemMapArenaPool(/* low_4gb */ true)); } linear_alloc_.reset(CreateLinearAlloc()); @@ -1369,8 +1402,8 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { 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. @@ -1385,8 +1418,8 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // 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); } @@ -1523,19 +1556,58 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // TODO: move this to just be an Trace::Start argument Trace::SetDefaultClockSource(runtime_options.GetOrDefault(Opt::ProfileClock)); - // Pre-allocate an OutOfMemoryError for the double-OOME case. - self->ThrowNewException("Ljava/lang/OutOfMemoryError;", - "OutOfMemoryError thrown while trying to throw OutOfMemoryError; " - "no stack trace available"); - pre_allocated_OutOfMemoryError_ = GcRoot(self->GetException()); - self->ClearException(); - - // Pre-allocate a NoClassDefFoundError for the common case of failing to find a system class - // ahead of checking the application's class loader. - self->ThrowNewException("Ljava/lang/NoClassDefFoundError;", - "Class not found using the boot class loader; no stack trace available"); - pre_allocated_NoClassDefFoundError_ = GcRoot(self->GetException()); - self->ClearException(); + if (GetHeap()->HasBootImageSpace()) { + const ImageHeader& image_header = GetHeap()->GetBootImageSpaces()[0]->GetImageHeader(); + pre_allocated_OutOfMemoryError_when_throwing_exception_ = GcRoot( + image_header.GetImageRoot(ImageHeader::kOomeWhenThrowingException)->AsThrowable()); + DCHECK(pre_allocated_OutOfMemoryError_when_throwing_exception_.Read()->GetClass() + ->DescriptorEquals("Ljava/lang/OutOfMemoryError;")); + pre_allocated_OutOfMemoryError_when_throwing_oome_ = GcRoot( + image_header.GetImageRoot(ImageHeader::kOomeWhenThrowingOome)->AsThrowable()); + DCHECK(pre_allocated_OutOfMemoryError_when_throwing_oome_.Read()->GetClass() + ->DescriptorEquals("Ljava/lang/OutOfMemoryError;")); + pre_allocated_OutOfMemoryError_when_handling_stack_overflow_ = GcRoot( + image_header.GetImageRoot(ImageHeader::kOomeWhenHandlingStackOverflow)->AsThrowable()); + DCHECK(pre_allocated_OutOfMemoryError_when_handling_stack_overflow_.Read()->GetClass() + ->DescriptorEquals("Ljava/lang/OutOfMemoryError;")); + pre_allocated_NoClassDefFoundError_ = GcRoot( + image_header.GetImageRoot(ImageHeader::kNoClassDefFoundError)->AsThrowable()); + DCHECK(pre_allocated_NoClassDefFoundError_.Read()->GetClass() + ->DescriptorEquals("Ljava/lang/NoClassDefFoundError;")); + } else { + // Pre-allocate an OutOfMemoryError for the case when we fail to + // allocate the exception to be thrown. + CreatePreAllocatedException(self, + this, + &pre_allocated_OutOfMemoryError_when_throwing_exception_, + "Ljava/lang/OutOfMemoryError;", + "OutOfMemoryError thrown while trying to throw an exception; " + "no stack trace available"); + // Pre-allocate an OutOfMemoryError for the double-OOME case. + CreatePreAllocatedException(self, + this, + &pre_allocated_OutOfMemoryError_when_throwing_oome_, + "Ljava/lang/OutOfMemoryError;", + "OutOfMemoryError thrown while trying to throw OutOfMemoryError; " + "no stack trace available"); + // Pre-allocate an OutOfMemoryError for the case when we fail to + // allocate while handling a stack overflow. + CreatePreAllocatedException(self, + this, + &pre_allocated_OutOfMemoryError_when_handling_stack_overflow_, + "Ljava/lang/OutOfMemoryError;", + "OutOfMemoryError thrown while trying to handle a stack overflow; " + "no stack trace available"); + + // Pre-allocate a NoClassDefFoundError for the common case of failing to find a system class + // ahead of checking the application's class loader. + CreatePreAllocatedException(self, + this, + &pre_allocated_NoClassDefFoundError_, + "Ljava/lang/NoClassDefFoundError;", + "Class not found using the boot class loader; " + "no stack trace available"); + } // Runtime initialization is largely done now. // We load plugins first since that can modify the runtime state slightly. @@ -1936,10 +2008,26 @@ void Runtime::DetachCurrentThread() { thread_list_->Unregister(self); } -mirror::Throwable* Runtime::GetPreAllocatedOutOfMemoryError() { - mirror::Throwable* oome = pre_allocated_OutOfMemoryError_.Read(); +mirror::Throwable* Runtime::GetPreAllocatedOutOfMemoryErrorWhenThrowingException() { + mirror::Throwable* oome = pre_allocated_OutOfMemoryError_when_throwing_exception_.Read(); + if (oome == nullptr) { + LOG(ERROR) << "Failed to return pre-allocated OOME-when-throwing-exception"; + } + return oome; +} + +mirror::Throwable* Runtime::GetPreAllocatedOutOfMemoryErrorWhenThrowingOOME() { + mirror::Throwable* oome = pre_allocated_OutOfMemoryError_when_throwing_oome_.Read(); if (oome == nullptr) { - LOG(ERROR) << "Failed to return pre-allocated OOME"; + LOG(ERROR) << "Failed to return pre-allocated OOME-when-throwing-OOME"; + } + return oome; +} + +mirror::Throwable* Runtime::GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow() { + mirror::Throwable* oome = pre_allocated_OutOfMemoryError_when_handling_stack_overflow_.Read(); + if (oome == nullptr) { + LOG(ERROR) << "Failed to return pre-allocated OOME-when-handling-stack-overflow"; } return oome; } @@ -1953,36 +2041,6 @@ mirror::Throwable* Runtime::GetPreAllocatedNoClassDefFoundError() { } void Runtime::VisitConstantRoots(RootVisitor* visitor) { - // Visit the classes held as static in mirror classes, these can be visited concurrently and only - // need to be visited once per GC since they never change. - mirror::Class::VisitRoots(visitor); - mirror::Constructor::VisitRoots(visitor); - mirror::Reference::VisitRoots(visitor); - mirror::Method::VisitRoots(visitor); - mirror::StackTraceElement::VisitRoots(visitor); - mirror::String::VisitRoots(visitor); - mirror::Throwable::VisitRoots(visitor); - mirror::Field::VisitRoots(visitor); - mirror::MethodType::VisitRoots(visitor); - mirror::MethodHandleImpl::VisitRoots(visitor); - mirror::MethodHandlesLookup::VisitRoots(visitor); - mirror::EmulatedStackFrame::VisitRoots(visitor); - mirror::ClassExt::VisitRoots(visitor); - mirror::CallSite::VisitRoots(visitor); - mirror::VarHandle::VisitRoots(visitor); - mirror::FieldVarHandle::VisitRoots(visitor); - mirror::ArrayElementVarHandle::VisitRoots(visitor); - mirror::ByteArrayViewVarHandle::VisitRoots(visitor); - mirror::ByteBufferViewVarHandle::VisitRoots(visitor); - // Visit all the primitive array types classes. - mirror::PrimitiveArray::VisitRoots(visitor); // BooleanArray - mirror::PrimitiveArray::VisitRoots(visitor); // ByteArray - mirror::PrimitiveArray::VisitRoots(visitor); // CharArray - mirror::PrimitiveArray::VisitRoots(visitor); // DoubleArray - mirror::PrimitiveArray::VisitRoots(visitor); // FloatArray - mirror::PrimitiveArray::VisitRoots(visitor); // IntArray - mirror::PrimitiveArray::VisitRoots(visitor); // LongArray - mirror::PrimitiveArray::VisitRoots(visitor); // ShortArray // Visiting the roots of these ArtMethods is not currently required since all the GcRoots are // null. BufferedRootVisitor<16> buffered_visitor(visitor, RootInfo(kRootVMInternal)); @@ -2024,8 +2082,14 @@ void Runtime::VisitTransactionRoots(RootVisitor* visitor) { void Runtime::VisitNonThreadRoots(RootVisitor* visitor) { java_vm_->VisitRoots(visitor); sentinel_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); - pre_allocated_OutOfMemoryError_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); + pre_allocated_OutOfMemoryError_when_throwing_exception_ + .VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); + pre_allocated_OutOfMemoryError_when_throwing_oome_ + .VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); + 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); } @@ -2050,9 +2114,10 @@ void Runtime::VisitImageRoots(RootVisitor* visitor) { auto* image_space = space->AsImageSpace(); const auto& image_header = image_space->GetImageHeader(); for (int32_t i = 0, size = image_header.GetImageRoots()->GetLength(); i != size; ++i) { - auto* obj = image_header.GetImageRoot(static_cast(i)); + mirror::Object* obj = + image_header.GetImageRoot(static_cast(i)).Ptr(); if (obj != nullptr) { - auto* after_obj = obj; + mirror::Object* after_obj = obj; visitor->VisitRoot(&after_obj, RootInfo(kRootStickyClass)); CHECK_EQ(after_obj, obj); } @@ -2171,38 +2236,21 @@ void Runtime::BroadcastForNewSystemWeaks(bool broadcast_for_checkpoint) { void Runtime::SetInstructionSet(InstructionSet instruction_set) { instruction_set_ = instruction_set; - if ((instruction_set_ == InstructionSet::kThumb2) || (instruction_set_ == InstructionSet::kArm)) { - for (int i = 0; i != kCalleeSaveSize; ++i) { - CalleeSaveType type = static_cast(i); - callee_save_method_frame_infos_[i] = arm::ArmCalleeSaveMethodFrameInfo(type); - } - } else if (instruction_set_ == InstructionSet::kMips) { - for (int i = 0; i != kCalleeSaveSize; ++i) { - CalleeSaveType type = static_cast(i); - callee_save_method_frame_infos_[i] = mips::MipsCalleeSaveMethodFrameInfo(type); - } - } else if (instruction_set_ == InstructionSet::kMips64) { - for (int i = 0; i != kCalleeSaveSize; ++i) { - CalleeSaveType type = static_cast(i); - callee_save_method_frame_infos_[i] = mips64::Mips64CalleeSaveMethodFrameInfo(type); - } - } else if (instruction_set_ == InstructionSet::kX86) { - for (int i = 0; i != kCalleeSaveSize; ++i) { - CalleeSaveType type = static_cast(i); - callee_save_method_frame_infos_[i] = x86::X86CalleeSaveMethodFrameInfo(type); - } - } else if (instruction_set_ == InstructionSet::kX86_64) { - for (int i = 0; i != kCalleeSaveSize; ++i) { - CalleeSaveType type = static_cast(i); - callee_save_method_frame_infos_[i] = x86_64::X86_64CalleeSaveMethodFrameInfo(type); - } - } else if (instruction_set_ == InstructionSet::kArm64) { - for (int i = 0; i != kCalleeSaveSize; ++i) { - CalleeSaveType type = static_cast(i); - callee_save_method_frame_infos_[i] = arm64::Arm64CalleeSaveMethodFrameInfo(type); - } - } else { - UNIMPLEMENTED(FATAL) << instruction_set_; + switch (instruction_set) { + case InstructionSet::kThumb2: + // kThumb2 is the same as kArm, use the canonical value. + instruction_set_ = InstructionSet::kArm; + break; + case InstructionSet::kArm: + case InstructionSet::kArm64: + case InstructionSet::kMips: + case InstructionSet::kMips64: + case InstructionSet::kX86: + case InstructionSet::kX86_64: + break; + default: + UNIMPLEMENTED(FATAL) << instruction_set_; + UNREACHABLE(); } } diff --git a/runtime/runtime.h b/runtime/runtime.h index c0ff6b3444401c9c7035b666f0550dbf76984089..5f8a4eaefbc598732e1e61d35cc6cd45f677156f 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -212,6 +212,8 @@ class Runtime { return finished_starting_; } + void RunRootClinits(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); + static Runtime* Current() { return instance_; } @@ -291,7 +293,12 @@ class Runtime { // Get the special object used to mark a cleared JNI weak global. mirror::Object* GetClearedJniWeakGlobal() REQUIRES_SHARED(Locks::mutator_lock_); - mirror::Throwable* GetPreAllocatedOutOfMemoryError() REQUIRES_SHARED(Locks::mutator_lock_); + mirror::Throwable* GetPreAllocatedOutOfMemoryErrorWhenThrowingException() + REQUIRES_SHARED(Locks::mutator_lock_); + mirror::Throwable* GetPreAllocatedOutOfMemoryErrorWhenThrowingOOME() + REQUIRES_SHARED(Locks::mutator_lock_); + mirror::Throwable* GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow() + REQUIRES_SHARED(Locks::mutator_lock_); mirror::Throwable* GetPreAllocatedNoClassDefFoundError() REQUIRES_SHARED(Locks::mutator_lock_); @@ -394,10 +401,6 @@ class Runtime { ArtMethod* GetCalleeSaveMethodUnchecked(CalleeSaveType type) REQUIRES_SHARED(Locks::mutator_lock_); - QuickMethodFrameInfo GetCalleeSaveMethodFrameInfo(CalleeSaveType type) const { - return callee_save_method_frame_infos_[static_cast(type)]; - } - QuickMethodFrameInfo GetRuntimeMethodFrameInfo(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); @@ -817,7 +820,10 @@ class Runtime { // 64 bit so that we can share the same asm offsets for both 32 and 64 bits. uint64_t callee_save_methods_[kCalleeSaveSize]; - GcRoot pre_allocated_OutOfMemoryError_; + // Pre-allocated exceptions (see Runtime::Init). + GcRoot pre_allocated_OutOfMemoryError_when_throwing_exception_; + GcRoot pre_allocated_OutOfMemoryError_when_throwing_oome_; + GcRoot pre_allocated_OutOfMemoryError_when_handling_stack_overflow_; GcRoot pre_allocated_NoClassDefFoundError_; ArtMethod* resolution_method_; ArtMethod* imt_conflict_method_; @@ -830,7 +836,6 @@ class Runtime { GcRoot sentinel_; InstructionSet instruction_set_; - QuickMethodFrameInfo callee_save_method_frame_infos_[kCalleeSaveSize]; CompilerCallbacks* compiler_callbacks_; bool is_zygote_; @@ -882,14 +887,6 @@ class Runtime { SignalCatcher* signal_catcher_; - // If true, the runtime will connect to tombstoned via a socket to - // request an open file descriptor to write its traces to. - bool use_tombstoned_traces_; - - // Location to which traces must be written on SIGQUIT. Only used if - // tombstoned_traces_ == false. - std::string stack_trace_file_; - std::unique_ptr java_vm_; std::unique_ptr jit_; diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc index 560352617729e823e5853e53d2cf008f1d4b6a9c..794ac19c4b5e4b48e3473c4019aec69229455e1e 100644 --- a/runtime/runtime_callbacks_test.cc +++ b/runtime/runtime_callbacks_test.cc @@ -28,13 +28,13 @@ #include "jni.h" #include "art_method-inl.h" +#include "base/mem_map.h" #include "base/mutex.h" #include "class_linker.h" #include "common_runtime_test.h" #include "dex/class_reference.h" #include "handle.h" #include "handle_scope-inl.h" -#include "mem_map.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "monitor.h" @@ -339,9 +339,6 @@ class RuntimeSigQuitCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest { }; TEST_F(RuntimeSigQuitCallbackRuntimeCallbacksTest, SigQuit) { - // SigQuit induces a dump. ASAN isn't happy with libunwind reading memory. - TEST_DISABLED_FOR_MEMORY_TOOL_ASAN(); - // The runtime needs to be started for the signal handler. Thread* self = Thread::Current(); diff --git a/runtime/runtime_common.cc b/runtime/runtime_common.cc index 59af9187f9ba71330588c1270312fdbc029bc0b0..eae2505ce92b11cbd2782646b919775db5a3480e 100644 --- a/runtime/runtime_common.cc +++ b/runtime/runtime_common.cc @@ -371,30 +371,11 @@ static bool IsTimeoutSignal(int signal_number) { #pragma GCC diagnostic ignored "-Wframe-larger-than=" #endif -void HandleUnexpectedSignalCommon(int signal_number, - siginfo_t* info, - void* raw_context, - bool handle_timeout_signal, - bool dump_on_stderr) { - static bool handling_unexpected_signal = false; - if (handling_unexpected_signal) { - LogHelper::LogLineLowStack(__FILE__, - __LINE__, - ::android::base::FATAL_WITHOUT_ABORT, - "HandleUnexpectedSignal reentered\n"); - if (handle_timeout_signal) { - if (IsTimeoutSignal(signal_number)) { - // Ignore a recursive timeout. - return; - } - } - _exit(1); - } - handling_unexpected_signal = true; - - gAborting++; // set before taking any locks - MutexLock mu(Thread::Current(), *Locks::unexpected_signal_lock_); - +static void HandleUnexpectedSignalCommonDump(int signal_number, + siginfo_t* info, + void* raw_context, + bool handle_timeout_signal, + bool dump_on_stderr) { auto logger = [&](auto& stream) { bool has_address = (signal_number == SIGILL || signal_number == SIGBUS || signal_number == SIGFPE || signal_number == SIGSEGV); @@ -453,6 +434,71 @@ void HandleUnexpectedSignalCommon(int signal_number, } } +void HandleUnexpectedSignalCommon(int signal_number, + siginfo_t* info, + void* raw_context, + bool handle_timeout_signal, + bool dump_on_stderr) { + // Local _static_ storing the currently handled signal (or -1). + static int handling_unexpected_signal = -1; + + // Whether the dump code should be run under the unexpected-signal lock. For diagnostics we + // allow recursive unexpected-signals in certain cases - avoid a deadlock. + bool grab_lock = true; + + if (handling_unexpected_signal != -1) { + LogHelper::LogLineLowStack(__FILE__, + __LINE__, + ::android::base::FATAL_WITHOUT_ABORT, + "HandleUnexpectedSignal reentered\n"); + // Print the signal number. Don't use any standard functions, just some arithmetic. Just best + // effort, with a minimal buffer. + if (0 < signal_number && signal_number < 100) { + char buf[] = { ' ', + 'S', + static_cast('0' + (signal_number / 10)), + static_cast('0' + (signal_number % 10)), + '\n', + 0 }; + LogHelper::LogLineLowStack(__FILE__, + __LINE__, + ::android::base::FATAL_WITHOUT_ABORT, + buf); + } + if (handle_timeout_signal) { + if (IsTimeoutSignal(signal_number)) { + // Ignore a recursive timeout. + return; + } + } + // If we were handling a timeout signal, try to go on. Otherwise hard-exit. + // This relies on the expectation that we'll only ever get one timeout signal. + if (!handle_timeout_signal || handling_unexpected_signal != GetTimeoutSignal()) { + _exit(1); + } + grab_lock = false; // The "outer" handling instance already holds the lock. + } + handling_unexpected_signal = signal_number; + + gAborting++; // set before taking any locks + + if (grab_lock) { + MutexLock mu(Thread::Current(), *Locks::unexpected_signal_lock_); + + HandleUnexpectedSignalCommonDump(signal_number, + info, + raw_context, + handle_timeout_signal, + dump_on_stderr); + } else { + HandleUnexpectedSignalCommonDump(signal_number, + info, + raw_context, + handle_timeout_signal, + dump_on_stderr); + } +} + #if defined(__APPLE__) #pragma GCC diagnostic pop #endif diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 4121ad69edc10681e9d1cec6152663cb83049aa9..3f9a3229ca647e13ddbb19dd3a5a6ec8b042082c 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -44,7 +44,7 @@ RUNTIME_OPTIONS_KEY (std::string, Image) RUNTIME_OPTIONS_KEY (Unit, CheckJni) RUNTIME_OPTIONS_KEY (Unit, JniOptsForceCopy) RUNTIME_OPTIONS_KEY (std::string, JdwpOptions, "") -RUNTIME_OPTIONS_KEY (JdwpProvider, JdwpProvider, JdwpProvider::kNone) +RUNTIME_OPTIONS_KEY (JdwpProvider, JdwpProvider, JdwpProvider::kUnset) RUNTIME_OPTIONS_KEY (MemoryKiB, MemoryMaximumSize, gc::Heap::kDefaultMaximumSize) // -Xmx RUNTIME_OPTIONS_KEY (MemoryKiB, MemoryInitialSize, gc::Heap::kDefaultInitialSize) // -Xms RUNTIME_OPTIONS_KEY (MemoryKiB, HeapGrowthLimit) // Default is 0 for unlimited @@ -77,6 +77,7 @@ RUNTIME_OPTIONS_KEY (unsigned int, JITWarmupThreshold) RUNTIME_OPTIONS_KEY (unsigned int, JITOsrThreshold) RUNTIME_OPTIONS_KEY (unsigned int, JITPriorityThreadWeight) RUNTIME_OPTIONS_KEY (unsigned int, JITInvokeTransitionWeight) +RUNTIME_OPTIONS_KEY (int, JITPoolThreadPthreadPriority, jit::kJitPoolThreadPthreadDefaultPriority) RUNTIME_OPTIONS_KEY (MemoryKiB, JITCodeCacheInitialCapacity, jit::JitCodeCache::kInitialCapacity) RUNTIME_OPTIONS_KEY (MemoryKiB, JITCodeCacheMaxCapacity, jit::JitCodeCache::kMaxCapacity) RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \ @@ -103,8 +104,6 @@ RUNTIME_OPTIONS_KEY (Unit, ForceNativeBridge) RUNTIME_OPTIONS_KEY (LogVerbosity, Verbose) RUNTIME_OPTIONS_KEY (unsigned int, LockProfThreshold) RUNTIME_OPTIONS_KEY (unsigned int, StackDumpLockProfThreshold) -RUNTIME_OPTIONS_KEY (bool, UseTombstonedTraces, false) -RUNTIME_OPTIONS_KEY (std::string, StackTraceFile) RUNTIME_OPTIONS_KEY (Unit, MethodTrace) RUNTIME_OPTIONS_KEY (std::string, MethodTraceFile, "/data/misc/trace/method-trace-file.bin") RUNTIME_OPTIONS_KEY (unsigned int, MethodTraceFileSize, 10 * MB) diff --git a/runtime/scoped_thread_state_change-inl.h b/runtime/scoped_thread_state_change-inl.h index f95593209b00ac44d960c169394ddf198bf2ba1d..3089c24c59238dde0639e072553e087727a8db2a 100644 --- a/runtime/scoped_thread_state_change-inl.h +++ b/runtime/scoped_thread_state_change-inl.h @@ -22,7 +22,7 @@ #include #include "base/casts.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "obj_ptr-inl.h" #include "runtime.h" #include "thread-inl.h" diff --git a/runtime/scoped_thread_state_change.cc b/runtime/scoped_thread_state_change.cc index 6a86cc64111b2664acb74246683a31b0d6303494..edbce053251f4595fc93e1297f269c25e70ba9d4 100644 --- a/runtime/scoped_thread_state_change.cc +++ b/runtime/scoped_thread_state_change.cc @@ -19,7 +19,7 @@ #include #include "base/casts.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "obj_ptr-inl.h" #include "runtime-inl.h" diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc index d590ad5cc625920ab95cab03037c2d13f9486246..f4a27b8397500dfe163b2e0b02a2d3b64c851dd6 100644 --- a/runtime/signal_catcher.cc +++ b/runtime/signal_catcher.cc @@ -73,19 +73,10 @@ static void DumpCmdLine(std::ostream& os) { #endif } -SignalCatcher::SignalCatcher(const std::string& stack_trace_file, - bool use_tombstoned_stack_trace_fd) - : stack_trace_file_(stack_trace_file), - use_tombstoned_stack_trace_fd_(use_tombstoned_stack_trace_fd), - lock_("SignalCatcher lock"), +SignalCatcher::SignalCatcher() + : lock_("SignalCatcher lock"), cond_("SignalCatcher::cond_", lock_), thread_(nullptr) { -#if !defined(ART_TARGET_ANDROID) - // We're not running on Android, so we can't communicate with tombstoned - // to ask for an open file. - CHECK(!use_tombstoned_stack_trace_fd_); -#endif - SetHaltFlag(false); // Create a raw pthread; its start routine will attach to the runtime. @@ -116,37 +107,11 @@ bool SignalCatcher::ShouldHalt() { return halt_; } -bool SignalCatcher::OpenStackTraceFile(android::base::unique_fd* tombstone_fd, - android::base::unique_fd* output_fd) { - if (use_tombstoned_stack_trace_fd_) { -#if defined(ART_TARGET_ANDROID) - return tombstoned_connect(getpid(), tombstone_fd, output_fd, kDebuggerdJavaBacktrace); -#else - UNUSED(tombstone_fd); - UNUSED(output_fd); -#endif - } - - // The runtime is not configured to dump traces to a file, will LOG(INFO) - // instead. - if (stack_trace_file_.empty()) { - return false; - } - - int fd = open(stack_trace_file_.c_str(), O_APPEND | O_CREAT | O_WRONLY, 0666); - if (fd == -1) { - PLOG(ERROR) << "Unable to open stack trace file '" << stack_trace_file_ << "'"; - return false; - } - - output_fd->reset(fd); - return true; -} - void SignalCatcher::Output(const std::string& s) { +#if defined(ART_TARGET_ANDROID) android::base::unique_fd tombstone_fd; android::base::unique_fd output_fd; - if (!OpenStackTraceFile(&tombstone_fd, &output_fd)) { + if (!tombstoned_connect(getpid(), &tombstone_fd, &output_fd, kDebuggerdJavaBacktrace)) { LOG(INFO) << s; return; } @@ -161,19 +126,16 @@ void SignalCatcher::Output(const std::string& s) { file->Erase(); } - const std::string output_path_msg = (use_tombstoned_stack_trace_fd_) ? - "[tombstoned]" : stack_trace_file_; - if (success) { - LOG(INFO) << "Wrote stack traces to '" << output_path_msg << "'"; + LOG(INFO) << "Wrote stack traces to tombstoned"; } else { - PLOG(ERROR) << "Failed to write stack traces to '" << output_path_msg << "'"; + PLOG(ERROR) << "Failed to write stack traces to tombstoned"; } - -#if defined(ART_TARGET_ANDROID) - if (use_tombstoned_stack_trace_fd_ && !tombstoned_notify_completion(tombstone_fd)) { + if (!tombstoned_notify_completion(tombstone_fd)) { PLOG(WARNING) << "Unable to notify tombstoned of dump completion"; } +#else + LOG(INFO) << s; #endif } diff --git a/runtime/signal_catcher.h b/runtime/signal_catcher.h index 8a2a7289de57fd8753431315d830e51924f1151e..46eae7e50eec3d0404739369dd7eb508bfef9726 100644 --- a/runtime/signal_catcher.h +++ b/runtime/signal_catcher.h @@ -33,17 +33,7 @@ class Thread; */ class SignalCatcher { public: - // If |use_tombstoned_stack_trace_fd| is |true|, traces will be - // written to a file descriptor provided by tombstoned. The process - // will communicate with tombstoned via a unix domain socket. This - // mode of stack trace dumping is only supported in an Android - // environment. - // - // If false, all traces will be dumped to |stack_trace_file| if it's - // non-empty. If |stack_trace_file| is empty, all traces will be written - // to the log buffer. - SignalCatcher(const std::string& stack_trace_file, - const bool use_tombstoned_stack_trace_fd); + SignalCatcher(); ~SignalCatcher(); void HandleSigQuit() REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_, @@ -54,19 +44,12 @@ class SignalCatcher { // NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock. static void* Run(void* arg) NO_THREAD_SAFETY_ANALYSIS; - // NOTE: We're using android::base::unique_fd here for easier - // interoperability with tombstoned client APIs. - bool OpenStackTraceFile(android::base::unique_fd* tombstone_fd, - android::base::unique_fd* output_fd); void HandleSigUsr1(); void Output(const std::string& s); void SetHaltFlag(bool new_value) REQUIRES(!lock_); bool ShouldHalt() REQUIRES(!lock_); int WaitForSignal(Thread* self, SignalSet& signals) REQUIRES(!lock_); - std::string stack_trace_file_; - const bool use_tombstoned_stack_trace_fd_; - mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; ConditionVariable cond_ GUARDED_BY(lock_); bool halt_ GUARDED_BY(lock_); diff --git a/runtime/stack.cc b/runtime/stack.cc index 229238e0f7fe620a3162c89279765a75f62a8f6b..f58fc3b56413ae997a8f2624020570355a586e0a 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -25,10 +25,11 @@ #include "base/hex_dump.h" #include "dex/dex_file_types.h" #include "entrypoints/entrypoint_utils-inl.h" +#include "entrypoints/quick/callee_save_frame.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/space/image_space.h" #include "gc/space/space-inl.h" -#include "interpreter/shadow_frame.h" +#include "interpreter/shadow_frame-inl.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "linear_alloc.h" @@ -67,7 +68,6 @@ StackVisitor::StackVisitor(Thread* thread, cur_oat_quick_method_header_(nullptr), num_frames_(num_frames), cur_depth_(0), - current_inlining_depth_(0), context_(context), check_suspended_(check_suspended) { if (check_suspended_) { @@ -75,34 +75,15 @@ StackVisitor::StackVisitor(Thread* thread, } } -static InlineInfo GetCurrentInlineInfo(const OatQuickMethodHeader* method_header, - uintptr_t cur_quick_frame_pc) - REQUIRES_SHARED(Locks::mutator_lock_) { - uint32_t native_pc_offset = method_header->NativeQuickPcOffset(cur_quick_frame_pc); - CodeInfo code_info = method_header->GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); - DCHECK(stack_map.IsValid()); - return code_info.GetInlineInfoOf(stack_map, encoding); -} - ArtMethod* StackVisitor::GetMethod() const { if (cur_shadow_frame_ != nullptr) { return cur_shadow_frame_->GetMethod(); } else if (cur_quick_frame_ != nullptr) { if (IsInInlinedFrame()) { - size_t depth_in_stack_map = current_inlining_depth_ - 1; - InlineInfo inline_info = GetCurrentInlineInfo(GetCurrentOatQuickMethodHeader(), - cur_quick_frame_pc_); const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader(); - CodeInfoEncoding encoding = method_header->GetOptimizedCodeInfo().ExtractEncoding(); MethodInfo method_info = method_header->GetOptimizedMethodInfo(); DCHECK(walk_kind_ != StackWalkKind::kSkipInlinedFrames); - return GetResolvedMethod(*GetCurrentQuickFrame(), - method_info, - inline_info, - encoding.inline_info.encoding, - depth_in_stack_map); + return GetResolvedMethod(*GetCurrentQuickFrame(), method_info, current_inline_frames_); } else { return *cur_quick_frame_; } @@ -115,11 +96,7 @@ uint32_t StackVisitor::GetDexPc(bool abort_on_failure) const { return cur_shadow_frame_->GetDexPC(); } else if (cur_quick_frame_ != nullptr) { if (IsInInlinedFrame()) { - size_t depth_in_stack_map = current_inlining_depth_ - 1; - const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader(); - CodeInfoEncoding encoding = method_header->GetOptimizedCodeInfo().ExtractEncoding(); - return GetCurrentInlineInfo(GetCurrentOatQuickMethodHeader(), cur_quick_frame_pc_). - GetDexPcAtDepth(encoding.inline_info.encoding, depth_in_stack_map); + return current_inline_frames_.back().GetDexPc(); } else if (cur_oat_quick_method_header_ == nullptr) { return dex::kDexNoIndex; } else { @@ -229,32 +206,23 @@ bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKin uint16_t number_of_dex_registers = accessor.RegistersSize(); DCHECK_LT(vreg, number_of_dex_registers); const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader(); - CodeInfo code_info = method_header->GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); + CodeInfo code_info(method_header); uint32_t native_pc_offset = method_header->NativeQuickPcOffset(cur_quick_frame_pc_); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); DCHECK(stack_map.IsValid()); - size_t depth_in_stack_map = current_inlining_depth_ - 1; DexRegisterMap dex_register_map = IsInInlinedFrame() - ? code_info.GetDexRegisterMapAtDepth(depth_in_stack_map, - code_info.GetInlineInfoOf(stack_map, encoding), - encoding, - number_of_dex_registers) - : code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); - - if (!dex_register_map.IsValid()) { + ? code_info.GetInlineDexRegisterMapOf(stack_map, current_inline_frames_.back()) + : code_info.GetDexRegisterMapOf(stack_map); + if (dex_register_map.empty()) { return false; } - DexRegisterLocation::Kind location_kind = - dex_register_map.GetLocationKind(vreg, number_of_dex_registers, code_info, encoding); + DCHECK_EQ(dex_register_map.size(), number_of_dex_registers); + DexRegisterLocation::Kind location_kind = dex_register_map[vreg].GetKind(); switch (location_kind) { case DexRegisterLocation::Kind::kInStack: { - const int32_t offset = dex_register_map.GetStackOffsetInBytes(vreg, - number_of_dex_registers, - code_info, - encoding); + const int32_t offset = dex_register_map[vreg].GetStackOffsetInBytes(); const uint8_t* addr = reinterpret_cast(cur_quick_frame_) + offset; *val = *reinterpret_cast(addr); return true; @@ -263,22 +231,16 @@ bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKin case DexRegisterLocation::Kind::kInRegisterHigh: case DexRegisterLocation::Kind::kInFpuRegister: case DexRegisterLocation::Kind::kInFpuRegisterHigh: { - uint32_t reg = - dex_register_map.GetMachineRegister(vreg, number_of_dex_registers, code_info, encoding); + uint32_t reg = dex_register_map[vreg].GetMachineRegister(); return GetRegisterIfAccessible(reg, kind, val); } case DexRegisterLocation::Kind::kConstant: - *val = dex_register_map.GetConstant(vreg, number_of_dex_registers, code_info, encoding); + *val = dex_register_map[vreg].GetConstant(); return true; case DexRegisterLocation::Kind::kNone: return false; default: - LOG(FATAL) - << "Unexpected location kind " - << dex_register_map.GetLocationInternalKind(vreg, - number_of_dex_registers, - code_info, - encoding); + LOG(FATAL) << "Unexpected location kind " << dex_register_map[vreg].GetKind(); UNREACHABLE(); } } @@ -718,7 +680,7 @@ QuickMethodFrameInfo StackVisitor::GetCurrentQuickFrameInfo() const { Runtime* runtime = Runtime::Current(); if (method->IsAbstract()) { - return runtime->GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); + return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); } // This goes before IsProxyMethod since runtime methods have a null declaring class. @@ -732,7 +694,7 @@ QuickMethodFrameInfo StackVisitor::GetCurrentQuickFrameInfo() const { // compiled method without any stubs. Therefore the method must have a OatQuickMethodHeader. DCHECK(!method->IsDirect() && !method->IsConstructor()) << "Constructors of proxy classes must have a OatQuickMethodHeader"; - return runtime->GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); + return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); } // The only remaining case is if the method is native and uses the generic JNI stub, @@ -751,8 +713,8 @@ QuickMethodFrameInfo StackVisitor::GetCurrentQuickFrameInfo() const { // Generic JNI frame. uint32_t handle_refs = GetNumberOfReferenceArgsWithoutReceiver(method) + 1; size_t scope_size = HandleScope::SizeOf(handle_refs); - QuickMethodFrameInfo callee_info = - runtime->GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); + constexpr QuickMethodFrameInfo callee_info = + RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); // Callee saves + handle scope + method ref + alignment // Note: -sizeof(void*) since callee-save frame stores a whole method pointer. @@ -829,18 +791,19 @@ void StackVisitor::WalkStack(bool include_transitions) { if ((walk_kind_ == StackWalkKind::kIncludeInlinedFrames) && (cur_oat_quick_method_header_ != nullptr) - && cur_oat_quick_method_header_->IsOptimized()) { - CodeInfo code_info = cur_oat_quick_method_header_->GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); + && cur_oat_quick_method_header_->IsOptimized() + // JNI methods cannot have any inlined frames. + && !method->IsNative()) { + DCHECK_NE(cur_quick_frame_pc_, 0u); + CodeInfo code_info(cur_oat_quick_method_header_); uint32_t native_pc_offset = cur_oat_quick_method_header_->NativeQuickPcOffset(cur_quick_frame_pc_); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); - if (stack_map.IsValid() && stack_map.HasInlineInfo(encoding.stack_map.encoding)) { - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); - DCHECK_EQ(current_inlining_depth_, 0u); - for (current_inlining_depth_ = inline_info.GetDepth(encoding.inline_info.encoding); - current_inlining_depth_ != 0; - --current_inlining_depth_) { + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); + if (stack_map.IsValid() && stack_map.HasInlineInfo()) { + DCHECK_EQ(current_inline_frames_.size(), 0u); + for (current_inline_frames_ = code_info.GetInlineInfosOf(stack_map); + !current_inline_frames_.empty(); + current_inline_frames_.pop_back()) { bool should_continue = VisitFrame(); if (UNLIKELY(!should_continue)) { return; @@ -866,13 +829,14 @@ void StackVisitor::WalkStack(bool include_transitions) { uint8_t* return_pc_addr = reinterpret_cast(cur_quick_frame_) + return_pc_offset; uintptr_t return_pc = *reinterpret_cast(return_pc_addr); - if (UNLIKELY(exit_stubs_installed)) { + if (UNLIKELY(exit_stubs_installed || + reinterpret_cast(GetQuickInstrumentationExitPc()) == return_pc)) { // While profiling, the return pc is restored from the side stack, except when walking // the stack for an exception where the side stack will be unwound in VisitFrame. if (reinterpret_cast(GetQuickInstrumentationExitPc()) == return_pc) { CHECK_LT(instrumentation_stack_depth, thread_->GetInstrumentationStack()->size()); const instrumentation::InstrumentationStackFrame& instrumentation_frame = - thread_->GetInstrumentationStack()->at(instrumentation_stack_depth); + (*thread_->GetInstrumentationStack())[instrumentation_stack_depth]; instrumentation_stack_depth++; if (GetMethod() == Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveAllCalleeSaves)) { diff --git a/runtime/stack.h b/runtime/stack.h index a16930bba05f44a83ec51354c7e9811eff61fabc..02578d25b7631c740b68811f72815ca3d6f9f4e7 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -23,6 +23,7 @@ #include "base/macros.h" #include "base/mutex.h" #include "quick/quick_method_frame_info.h" +#include "stack_map.h" namespace art { @@ -219,11 +220,11 @@ class StackVisitor { void SetReturnPc(uintptr_t new_ret_pc) REQUIRES_SHARED(Locks::mutator_lock_); bool IsInInlinedFrame() const { - return current_inlining_depth_ != 0; + return !current_inline_frames_.empty(); } - size_t GetCurrentInliningDepth() const { - return current_inlining_depth_; + InlineInfo GetCurrentInlinedFrame() const { + return current_inline_frames_.back(); } uintptr_t GetCurrentQuickFramePc() const { @@ -309,9 +310,9 @@ class StackVisitor { size_t num_frames_; // Depth of the frame we're currently at. size_t cur_depth_; - // Current inlining depth of the method we are currently at. - // 0 if there is no inlined frame. - size_t current_inlining_depth_; + // Current inlined frames of the method we are currently at. + // We keep poping frames from the end as we visit the frames. + BitTableRange current_inline_frames_; protected: Context* const context_; diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index 250ff2af1a13acd2ebfb0bf877bf5607a9916306..9fa9d8499376e403665358d9f578abddf9daf565 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -16,250 +16,270 @@ #include "stack_map.h" +#include #include #include "art_method.h" -#include "indenter.h" +#include "base/indenter.h" +#include "base/stats.h" +#include "oat_quick_method_header.h" #include "scoped_thread_state_change-inl.h" namespace art { -constexpr size_t DexRegisterLocationCatalog::kNoLocationEntryIndex; -constexpr uint32_t StackMap::kNoDexRegisterMap; -constexpr uint32_t StackMap::kNoInlineInfo; +CodeInfo::CodeInfo(const OatQuickMethodHeader* header) + : CodeInfo(header->GetOptimizedCodeInfoPtr()) { +} + +void CodeInfo::Decode(const uint8_t* data) { + const uint8_t* begin = data; + frame_size_in_bytes_ = DecodeUnsignedLeb128(&data); + core_spill_mask_ = DecodeUnsignedLeb128(&data); + fp_spill_mask_ = DecodeUnsignedLeb128(&data); + number_of_dex_registers_ = DecodeUnsignedLeb128(&data); + BitMemoryReader reader(data, /* bit_offset */ 0); + stack_maps_.Decode(reader); + register_masks_.Decode(reader); + stack_masks_.Decode(reader); + inline_infos_.Decode(reader); + dex_register_masks_.Decode(reader); + dex_register_maps_.Decode(reader); + dex_register_catalog_.Decode(reader); + size_in_bits_ = (data - begin) * kBitsPerByte + reader.GetBitOffset(); +} -std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation::Kind& kind) { - using Kind = DexRegisterLocation::Kind; - switch (kind) { - case Kind::kNone: - return stream << "none"; - case Kind::kInStack: - return stream << "in stack"; - case Kind::kInRegister: - return stream << "in register"; - case Kind::kInRegisterHigh: - return stream << "in register high"; - case Kind::kInFpuRegister: - return stream << "in fpu register"; - case Kind::kInFpuRegisterHigh: - return stream << "in fpu register high"; - case Kind::kConstant: - return stream << "as constant"; - case Kind::kInStackLargeOffset: - return stream << "in stack (large offset)"; - case Kind::kConstantLargeValue: - return stream << "as constant (large value)"; +BitTable::const_iterator CodeInfo::BinarySearchNativePc(uint32_t packed_pc) const { + return std::partition_point( + stack_maps_.begin(), + stack_maps_.end(), + [packed_pc](const StackMap& sm) { + return sm.GetPackedNativePc() < packed_pc && sm.GetKind() != StackMap::Kind::Catch; + }); +} + +StackMap CodeInfo::GetStackMapForNativePcOffset(uint32_t pc, InstructionSet isa) const { + auto it = BinarySearchNativePc(StackMap::PackNativePc(pc, isa)); + // Start at the lower bound and iterate over all stack maps with the given native pc. + for (; it != stack_maps_.end() && (*it).GetNativePcOffset(isa) == pc; ++it) { + StackMap::Kind kind = static_cast((*it).GetKind()); + if (kind == StackMap::Kind::Default || kind == StackMap::Kind::OSR) { + return *it; + } } - return stream << "Kind<" << static_cast(kind) << ">"; + return stack_maps_.GetInvalidRow(); } -DexRegisterLocation::Kind DexRegisterMap::GetLocationInternalKind( - uint16_t dex_register_number, - uint16_t number_of_dex_registers, - const CodeInfo& code_info, - const CodeInfoEncoding& enc) const { - DexRegisterLocationCatalog dex_register_location_catalog = - code_info.GetDexRegisterLocationCatalog(enc); - size_t location_catalog_entry_index = GetLocationCatalogEntryIndex( - dex_register_number, - number_of_dex_registers, - code_info.GetNumberOfLocationCatalogEntries(enc)); - return dex_register_location_catalog.GetLocationInternalKind(location_catalog_entry_index); +// Scan backward to determine dex register locations at given stack map. +// All registers for a stack map are combined - inlined registers are just appended, +// therefore 'first_dex_register' allows us to select a sub-range to decode. +void CodeInfo::DecodeDexRegisterMap(uint32_t stack_map_index, + uint32_t first_dex_register, + /*out*/ DexRegisterMap* map) const { + // Count remaining work so we know when we have finished. + uint32_t remaining_registers = map->size(); + + // Keep scanning backwards and collect the most recent location of each register. + for (int32_t s = stack_map_index; s >= 0 && remaining_registers != 0; s--) { + StackMap stack_map = GetStackMapAt(s); + DCHECK_LE(stack_map_index - s, kMaxDexRegisterMapSearchDistance) << "Unbounded search"; + + // The mask specifies which registers where modified in this stack map. + // NB: the mask can be shorter than expected if trailing zero bits were removed. + uint32_t mask_index = stack_map.GetDexRegisterMaskIndex(); + if (mask_index == StackMap::kNoValue) { + continue; // Nothing changed at this stack map. + } + BitMemoryRegion mask = dex_register_masks_.GetBitMemoryRegion(mask_index); + if (mask.size_in_bits() <= first_dex_register) { + continue; // Nothing changed after the first register we are interested in. + } + + // The map stores one catalogue index per each modified register location. + uint32_t map_index = stack_map.GetDexRegisterMapIndex(); + DCHECK_NE(map_index, StackMap::kNoValue); + + // Skip initial registers which we are not interested in (to get to inlined registers). + map_index += mask.PopCount(0, first_dex_register); + mask = mask.Subregion(first_dex_register, mask.size_in_bits() - first_dex_register); + + // Update registers that we see for first time (i.e. most recent value). + DexRegisterLocation* regs = map->data(); + const uint32_t end = std::min(map->size(), mask.size_in_bits()); + const size_t kNumBits = BitSizeOf(); + for (uint32_t reg = 0; reg < end; reg += kNumBits) { + // Process the mask in chunks of kNumBits for performance. + uint32_t bits = mask.LoadBits(reg, std::min(end - reg, kNumBits)); + while (bits != 0) { + uint32_t bit = CTZ(bits); + if (regs[reg + bit].GetKind() == DexRegisterLocation::Kind::kInvalid) { + regs[reg + bit] = GetDexRegisterCatalogEntry(dex_register_maps_.Get(map_index)); + remaining_registers--; + } + map_index++; + bits ^= 1u << bit; // Clear the bit. + } + } + } + + // Set any remaining registers to None (which is the default state at first stack map). + if (remaining_registers != 0) { + DexRegisterLocation* regs = map->data(); + for (uint32_t r = 0; r < map->size(); r++) { + if (regs[r].GetKind() == DexRegisterLocation::Kind::kInvalid) { + regs[r] = DexRegisterLocation::None(); + } + } + } } -DexRegisterLocation DexRegisterMap::GetDexRegisterLocation(uint16_t dex_register_number, - uint16_t number_of_dex_registers, - const CodeInfo& code_info, - const CodeInfoEncoding& enc) const { - DexRegisterLocationCatalog dex_register_location_catalog = - code_info.GetDexRegisterLocationCatalog(enc); - size_t location_catalog_entry_index = GetLocationCatalogEntryIndex( - dex_register_number, - number_of_dex_registers, - code_info.GetNumberOfLocationCatalogEntries(enc)); - return dex_register_location_catalog.GetDexRegisterLocation(location_catalog_entry_index); +template +static void AddTableSizeStats(const char* table_name, + const BitTable& table, + /*out*/ Stats* parent) { + Stats* table_stats = parent->Child(table_name); + table_stats->AddBits(table.BitSize()); + table_stats->Child("Header")->AddBits(table.HeaderBitSize()); + const char* const* column_names = GetBitTableColumnNames(); + for (size_t c = 0; c < table.NumColumns(); c++) { + if (table.NumColumnBits(c) > 0) { + Stats* column_stats = table_stats->Child(column_names[c]); + column_stats->AddBits(table.NumRows() * table.NumColumnBits(c), table.NumRows()); + } + } } -static void DumpRegisterMapping(std::ostream& os, - size_t dex_register_num, - DexRegisterLocation location, - const std::string& prefix = "v", - const std::string& suffix = "") { - os << prefix << dex_register_num << ": " - << location.GetInternalKind() - << " (" << location.GetValue() << ")" << suffix << '\n'; +void CodeInfo::AddSizeStats(/*out*/ Stats* parent) const { + Stats* stats = parent->Child("CodeInfo"); + stats->AddBytes(Size()); + AddTableSizeStats("StackMaps", stack_maps_, stats); + AddTableSizeStats("RegisterMasks", register_masks_, stats); + AddTableSizeStats("StackMasks", stack_masks_, stats); + AddTableSizeStats("InlineInfos", inline_infos_, stats); + AddTableSizeStats("DexRegisterMasks", dex_register_masks_, stats); + AddTableSizeStats("DexRegisterMaps", dex_register_maps_, stats); + AddTableSizeStats("DexRegisterCatalog", dex_register_catalog_, stats); } -void StackMapEncoding::Dump(VariableIndentationOutputStream* vios) const { - vios->Stream() - << "StackMapEncoding" - << " (native_pc_bit_offset=" << static_cast(kNativePcBitOffset) - << ", dex_pc_bit_offset=" << static_cast(dex_pc_bit_offset_) - << ", dex_register_map_bit_offset=" << static_cast(dex_register_map_bit_offset_) - << ", inline_info_bit_offset=" << static_cast(inline_info_bit_offset_) - << ", register_mask_bit_offset=" << static_cast(register_mask_index_bit_offset_) - << ", stack_mask_index_bit_offset=" << static_cast(stack_mask_index_bit_offset_) - << ", total_bit_size=" << static_cast(total_bit_size_) - << ")\n"; +void DexRegisterMap::Dump(VariableIndentationOutputStream* vios) const { + if (HasAnyLiveDexRegisters()) { + ScopedIndentation indent1(vios); + for (size_t i = 0; i < size(); ++i) { + DexRegisterLocation reg = (*this)[i]; + if (reg.IsLive()) { + vios->Stream() << "v" << i << ":" << reg << " "; + } + } + vios->Stream() << "\n"; + } } -void InlineInfoEncoding::Dump(VariableIndentationOutputStream* vios) const { - vios->Stream() - << "InlineInfoEncoding" - << " (method_index_bit_offset=" << static_cast(kMethodIndexBitOffset) - << ", dex_pc_bit_offset=" << static_cast(dex_pc_bit_offset_) - << ", extra_data_bit_offset=" << static_cast(extra_data_bit_offset_) - << ", dex_register_map_bit_offset=" << static_cast(dex_register_map_bit_offset_) - << ", total_bit_size=" << static_cast(total_bit_size_) - << ")\n"; +template +static void DumpTable(VariableIndentationOutputStream* vios, + const char* table_name, + const BitTable& table, + bool verbose, + bool is_mask = false) { + if (table.NumRows() != 0) { + vios->Stream() << table_name << " BitSize=" << table.BitSize(); + vios->Stream() << " Rows=" << table.NumRows() << " Bits={"; + const char* const* column_names = GetBitTableColumnNames(); + for (size_t c = 0; c < table.NumColumns(); c++) { + vios->Stream() << (c != 0 ? " " : ""); + vios->Stream() << column_names[c] << "=" << table.NumColumnBits(c); + } + vios->Stream() << "}\n"; + if (verbose) { + ScopedIndentation indent1(vios); + for (size_t r = 0; r < table.NumRows(); r++) { + vios->Stream() << "[" << std::right << std::setw(3) << r << "]={"; + for (size_t c = 0; c < table.NumColumns(); c++) { + vios->Stream() << (c != 0 ? " " : ""); + if (is_mask) { + BitMemoryRegion bits = table.GetBitMemoryRegion(r, c); + for (size_t b = 0, e = bits.size_in_bits(); b < e; b++) { + vios->Stream() << bits.LoadBit(e - b - 1); + } + } else { + vios->Stream() << std::right << std::setw(8) << static_cast(table.Get(r, c)); + } + } + vios->Stream() << "}\n"; + } + } + } } void CodeInfo::Dump(VariableIndentationOutputStream* vios, uint32_t code_offset, - uint16_t number_of_dex_registers, - bool dump_stack_maps, + bool verbose, InstructionSet instruction_set, const MethodInfo& method_info) const { - CodeInfoEncoding encoding = ExtractEncoding(); - size_t number_of_stack_maps = GetNumberOfStackMaps(encoding); vios->Stream() - << "Optimized CodeInfo (number_of_dex_registers=" << number_of_dex_registers - << ", number_of_stack_maps=" << number_of_stack_maps - << ")\n"; + << "CodeInfo" + << " BitSize=" << size_in_bits_ + << "\n"; ScopedIndentation indent1(vios); - encoding.stack_map.encoding.Dump(vios); - if (HasInlineInfo(encoding)) { - encoding.inline_info.encoding.Dump(vios); - } - // Display the Dex register location catalog. - GetDexRegisterLocationCatalog(encoding).Dump(vios, *this); - // Display stack maps along with (live) Dex register maps. - if (dump_stack_maps) { - for (size_t i = 0; i < number_of_stack_maps; ++i) { - StackMap stack_map = GetStackMapAt(i, encoding); - stack_map.Dump(vios, - *this, - encoding, - method_info, - code_offset, - number_of_dex_registers, - instruction_set, - " " + std::to_string(i)); - } - } - // TODO: Dump the stack map's inline information? We need to know more from the caller: - // we need to know the number of dex registers for each inlined method. -} + DumpTable(vios, "StackMaps", stack_maps_, verbose); + DumpTable(vios, "RegisterMasks", register_masks_, verbose); + DumpTable(vios, "StackMasks", stack_masks_, verbose, true /* is_mask */); + DumpTable(vios, "InlineInfos", inline_infos_, verbose); + DumpTable(vios, "DexRegisterMasks", dex_register_masks_, verbose, true /* is_mask */); + DumpTable(vios, "DexRegisterMaps", dex_register_maps_, verbose); + DumpTable(vios, "DexRegisterCatalog", dex_register_catalog_, verbose); -void DexRegisterLocationCatalog::Dump(VariableIndentationOutputStream* vios, - const CodeInfo& code_info) { - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - size_t number_of_location_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding); - size_t location_catalog_size_in_bytes = code_info.GetDexRegisterLocationCatalogSize(encoding); - vios->Stream() - << "DexRegisterLocationCatalog (number_of_entries=" << number_of_location_catalog_entries - << ", size_in_bytes=" << location_catalog_size_in_bytes << ")\n"; - for (size_t i = 0; i < number_of_location_catalog_entries; ++i) { - DexRegisterLocation location = GetDexRegisterLocation(i); - ScopedIndentation indent1(vios); - DumpRegisterMapping(vios->Stream(), i, location, "entry "); - } -} - -void DexRegisterMap::Dump(VariableIndentationOutputStream* vios, - const CodeInfo& code_info, - uint16_t number_of_dex_registers) const { - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - size_t number_of_location_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding); - // TODO: Display the bit mask of live Dex registers. - for (size_t j = 0; j < number_of_dex_registers; ++j) { - if (IsDexRegisterLive(j)) { - size_t location_catalog_entry_index = GetLocationCatalogEntryIndex( - j, number_of_dex_registers, number_of_location_catalog_entries); - DexRegisterLocation location = GetDexRegisterLocation(j, - number_of_dex_registers, - code_info, - encoding); - ScopedIndentation indent1(vios); - DumpRegisterMapping( - vios->Stream(), j, location, "v", - "\t[entry " + std::to_string(static_cast(location_catalog_entry_index)) + "]"); + // Display stack maps along with (live) Dex register maps. + if (verbose) { + for (StackMap stack_map : stack_maps_) { + stack_map.Dump(vios, *this, method_info, code_offset, instruction_set); } } } void StackMap::Dump(VariableIndentationOutputStream* vios, const CodeInfo& code_info, - const CodeInfoEncoding& encoding, const MethodInfo& method_info, uint32_t code_offset, - uint16_t number_of_dex_registers, - InstructionSet instruction_set, - const std::string& header_suffix) const { - StackMapEncoding stack_map_encoding = encoding.stack_map.encoding; - const uint32_t pc_offset = GetNativePcOffset(stack_map_encoding, instruction_set); + InstructionSet instruction_set) const { + const uint32_t pc_offset = GetNativePcOffset(instruction_set); vios->Stream() - << "StackMap" << header_suffix + << "StackMap[" << Row() << "]" << std::hex - << " [native_pc=0x" << code_offset + pc_offset << "]" - << " [entry_size=0x" << encoding.stack_map.encoding.BitSize() << " bits]" - << " (dex_pc=0x" << GetDexPc(stack_map_encoding) - << ", native_pc_offset=0x" << pc_offset - << ", dex_register_map_offset=0x" << GetDexRegisterMapOffset(stack_map_encoding) - << ", inline_info_offset=0x" << GetInlineInfoIndex(stack_map_encoding) - << ", register_mask=0x" << code_info.GetRegisterMaskOf(encoding, *this) + << " (native_pc=0x" << code_offset + pc_offset + << ", dex_pc=0x" << GetDexPc() + << ", register_mask=0x" << code_info.GetRegisterMaskOf(*this) << std::dec << ", stack_mask=0b"; - BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, *this); - for (size_t i = 0, e = encoding.stack_mask.encoding.BitSize(); i < e; ++i) { + BitMemoryRegion stack_mask = code_info.GetStackMaskOf(*this); + for (size_t i = 0, e = stack_mask.size_in_bits(); i < e; ++i) { vios->Stream() << stack_mask.LoadBit(e - i - 1); } vios->Stream() << ")\n"; - if (HasDexRegisterMap(stack_map_encoding)) { - DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf( - *this, encoding, number_of_dex_registers); - dex_register_map.Dump(vios, code_info, number_of_dex_registers); - } - if (HasInlineInfo(stack_map_encoding)) { - InlineInfo inline_info = code_info.GetInlineInfoOf(*this, encoding); - // We do not know the length of the dex register maps of inlined frames - // at this level, so we just pass null to `InlineInfo::Dump` to tell - // it not to look at these maps. - inline_info.Dump(vios, code_info, method_info, nullptr); + code_info.GetDexRegisterMapOf(*this).Dump(vios); + for (InlineInfo inline_info : code_info.GetInlineInfosOf(*this)) { + inline_info.Dump(vios, code_info, *this, method_info); } } void InlineInfo::Dump(VariableIndentationOutputStream* vios, const CodeInfo& code_info, - const MethodInfo& method_info, - uint16_t number_of_dex_registers[]) const { - InlineInfoEncoding inline_info_encoding = code_info.ExtractEncoding().inline_info.encoding; - vios->Stream() << "InlineInfo with depth " - << static_cast(GetDepth(inline_info_encoding)) - << "\n"; - - for (size_t i = 0; i < GetDepth(inline_info_encoding); ++i) { + const StackMap& stack_map, + const MethodInfo& method_info) const { + uint32_t depth = Row() - stack_map.GetInlineInfoIndex(); + vios->Stream() + << "InlineInfo[" << Row() << "]" + << " (depth=" << depth + << std::hex + << ", dex_pc=0x" << GetDexPc(); + if (EncodesArtMethod()) { + ScopedObjectAccess soa(Thread::Current()); + vios->Stream() << ", method=" << GetArtMethod()->PrettyMethod(); + } else { vios->Stream() - << " At depth " << i - << std::hex - << " (dex_pc=0x" << GetDexPcAtDepth(inline_info_encoding, i); - if (EncodesArtMethodAtDepth(inline_info_encoding, i)) { - ScopedObjectAccess soa(Thread::Current()); - vios->Stream() << ", method=" << GetArtMethodAtDepth(inline_info_encoding, i)->PrettyMethod(); - } else { - vios->Stream() - << std::dec - << ", method_index=" << GetMethodIndexAtDepth(inline_info_encoding, method_info, i); - } - vios->Stream() << ")\n"; - if (HasDexRegisterMapAtDepth(inline_info_encoding, i) && (number_of_dex_registers != nullptr)) { - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapAtDepth(i, *this, encoding, number_of_dex_registers[i]); - ScopedIndentation indent1(vios); - dex_register_map.Dump(vios, code_info, number_of_dex_registers[i]); - } + << std::dec + << ", method_index=" << GetMethodIndex(method_info); } + vios->Stream() << ")\n"; + code_info.GetInlineDexRegisterMapOf(stack_map, *this).Dump(vios); } } // namespace art diff --git a/runtime/stack_map.h b/runtime/stack_map.h index bde3462437421e6a5bafeb1a2774d80b39681e11..26b95b0c2b8e52816c7a40892cf94292216c8ee4 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -19,17 +19,21 @@ #include -#include "arch/code_offset.h" +#include "arch/instruction_set.h" +#include "base/bit_memory_region.h" +#include "base/bit_table.h" #include "base/bit_utils.h" #include "base/bit_vector.h" #include "base/leb128.h" -#include "bit_memory_region.h" +#include "base/memory_region.h" #include "dex/dex_file_types.h" -#include "memory_region.h" +#include "dex_register_location.h" #include "method_info.h" +#include "quick/quick_method_frame_info.h" namespace art { +class OatQuickMethodHeader; class VariableIndentationOutputStream; // Size of a frame slot, in bytes. This constant is a signed value, @@ -37,754 +41,76 @@ class VariableIndentationOutputStream; // (signed) values. static constexpr ssize_t kFrameSlotSize = 4; -// Size of Dex virtual registers. -static constexpr size_t kVRegSize = 4; +// The delta compression of dex register maps means we need to scan the stackmaps backwards. +// We compress the data in such a way so that there is an upper bound on the search distance. +// Max distance 0 means each stack map must be fully defined and no scanning back is allowed. +// If this value is changed, the oat file version should be incremented (for DCHECK to pass). +static constexpr size_t kMaxDexRegisterMapSearchDistance = 32; class ArtMethod; class CodeInfo; -class StackMapEncoding; -struct CodeInfoEncoding; +class Stats; -/** - * Classes in the following file are wrapper on stack map information backed - * by a MemoryRegion. As such they read and write to the region, they don't have - * their own fields. - */ - -// Dex register location container used by DexRegisterMap and StackMapStream. -class DexRegisterLocation { - public: - /* - * The location kind used to populate the Dex register information in a - * StackMapStream can either be: - * - kStack: vreg stored on the stack, value holds the stack offset; - * - kInRegister: vreg stored in low 32 bits of a core physical register, - * value holds the register number; - * - kInRegisterHigh: vreg stored in high 32 bits of a core physical register, - * value holds the register number; - * - kInFpuRegister: vreg stored in low 32 bits of an FPU register, - * value holds the register number; - * - kInFpuRegisterHigh: vreg stored in high 32 bits of an FPU register, - * value holds the register number; - * - kConstant: value holds the constant; - * - * In addition, DexRegisterMap also uses these values: - * - kInStackLargeOffset: value holds a "large" stack offset (greater than - * or equal to 128 bytes); - * - kConstantLargeValue: value holds a "large" constant (lower than 0, or - * or greater than or equal to 32); - * - kNone: the register has no location, meaning it has not been set. - */ - enum class Kind : uint8_t { - // Short location kinds, for entries fitting on one byte (3 bits - // for the kind, 5 bits for the value) in a DexRegisterMap. - kInStack = 0, // 0b000 - kInRegister = 1, // 0b001 - kInRegisterHigh = 2, // 0b010 - kInFpuRegister = 3, // 0b011 - kInFpuRegisterHigh = 4, // 0b100 - kConstant = 5, // 0b101 - - // Large location kinds, requiring a 5-byte encoding (1 byte for the - // kind, 4 bytes for the value). - - // Stack location at a large offset, meaning that the offset value - // divided by the stack frame slot size (4 bytes) cannot fit on a - // 5-bit unsigned integer (i.e., this offset value is greater than - // or equal to 2^5 * 4 = 128 bytes). - kInStackLargeOffset = 6, // 0b110 - - // Large constant, that cannot fit on a 5-bit signed integer (i.e., - // lower than 0, or greater than or equal to 2^5 = 32). - kConstantLargeValue = 7, // 0b111 - - // Entries with no location are not stored and do not need own marker. - kNone = static_cast(-1), - - kLastLocationKind = kConstantLargeValue - }; - - static_assert( - sizeof(Kind) == 1u, - "art::DexRegisterLocation::Kind has a size different from one byte."); - - static bool IsShortLocationKind(Kind kind) { - switch (kind) { - case Kind::kInStack: - case Kind::kInRegister: - case Kind::kInRegisterHigh: - case Kind::kInFpuRegister: - case Kind::kInFpuRegisterHigh: - case Kind::kConstant: - return true; - - case Kind::kInStackLargeOffset: - case Kind::kConstantLargeValue: - return false; - - case Kind::kNone: - LOG(FATAL) << "Unexpected location kind"; - } - UNREACHABLE(); - } - - // Convert `kind` to a "surface" kind, i.e. one that doesn't include - // any value with a "large" qualifier. - // TODO: Introduce another enum type for the surface kind? - static Kind ConvertToSurfaceKind(Kind kind) { - switch (kind) { - case Kind::kInStack: - case Kind::kInRegister: - case Kind::kInRegisterHigh: - case Kind::kInFpuRegister: - case Kind::kInFpuRegisterHigh: - case Kind::kConstant: - return kind; - - case Kind::kInStackLargeOffset: - return Kind::kInStack; - - case Kind::kConstantLargeValue: - return Kind::kConstant; - - case Kind::kNone: - return kind; - } - UNREACHABLE(); - } - - // Required by art::StackMapStream::LocationCatalogEntriesIndices. - DexRegisterLocation() : kind_(Kind::kNone), value_(0) {} - - DexRegisterLocation(Kind kind, int32_t value) : kind_(kind), value_(value) {} - - static DexRegisterLocation None() { - return DexRegisterLocation(Kind::kNone, 0); - } - - // Get the "surface" kind of the location, i.e., the one that doesn't - // include any value with a "large" qualifier. - Kind GetKind() const { - return ConvertToSurfaceKind(kind_); - } - - // Get the value of the location. - int32_t GetValue() const { return value_; } - - // Get the actual kind of the location. - Kind GetInternalKind() const { return kind_; } - - bool operator==(DexRegisterLocation other) const { - return kind_ == other.kind_ && value_ == other.value_; - } - - bool operator!=(DexRegisterLocation other) const { - return !(*this == other); - } - - private: - Kind kind_; - int32_t value_; - - friend class DexRegisterLocationHashFn; -}; - -std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation::Kind& kind); - -/** - * Store information on unique Dex register locations used in a method. - * The information is of the form: - * - * [DexRegisterLocation+]. - * - * DexRegisterLocations are either 1- or 5-byte wide (see art::DexRegisterLocation::Kind). - */ -class DexRegisterLocationCatalog { - public: - explicit DexRegisterLocationCatalog(MemoryRegion region) : region_(region) {} - - // Short (compressed) location, fitting on one byte. - typedef uint8_t ShortLocation; - - void SetRegisterInfo(size_t offset, const DexRegisterLocation& dex_register_location) { - DexRegisterLocation::Kind kind = ComputeCompressedKind(dex_register_location); - int32_t value = dex_register_location.GetValue(); - if (DexRegisterLocation::IsShortLocationKind(kind)) { - // Short location. Compress the kind and the value as a single byte. - if (kind == DexRegisterLocation::Kind::kInStack) { - // Instead of storing stack offsets expressed in bytes for - // short stack locations, store slot offsets. A stack offset - // is a multiple of 4 (kFrameSlotSize). This means that by - // dividing it by 4, we can fit values from the [0, 128) - // interval in a short stack location, and not just values - // from the [0, 32) interval. - DCHECK_EQ(value % kFrameSlotSize, 0); - value /= kFrameSlotSize; - } - DCHECK(IsShortValue(value)) << value; - region_.StoreUnaligned(offset, MakeShortLocation(kind, value)); - } else { - // Large location. Write the location on one byte and the value - // on 4 bytes. - DCHECK(!IsShortValue(value)) << value; - if (kind == DexRegisterLocation::Kind::kInStackLargeOffset) { - // Also divide large stack offsets by 4 for the sake of consistency. - DCHECK_EQ(value % kFrameSlotSize, 0); - value /= kFrameSlotSize; - } - // Data can be unaligned as the written Dex register locations can - // either be 1-byte or 5-byte wide. Use - // art::MemoryRegion::StoreUnaligned instead of - // art::MemoryRegion::Store to prevent unligned word accesses on ARM. - region_.StoreUnaligned(offset, kind); - region_.StoreUnaligned(offset + sizeof(DexRegisterLocation::Kind), value); - } - } - - // Find the offset of the location catalog entry number `location_catalog_entry_index`. - size_t FindLocationOffset(size_t location_catalog_entry_index) const { - size_t offset = kFixedSize; - // Skip the first `location_catalog_entry_index - 1` entries. - for (uint16_t i = 0; i < location_catalog_entry_index; ++i) { - // Read the first next byte and inspect its first 3 bits to decide - // whether it is a short or a large location. - DexRegisterLocation::Kind kind = ExtractKindAtOffset(offset); - if (DexRegisterLocation::IsShortLocationKind(kind)) { - // Short location. Skip the current byte. - offset += SingleShortEntrySize(); - } else { - // Large location. Skip the 5 next bytes. - offset += SingleLargeEntrySize(); - } - } - return offset; - } - - // Get the internal kind of entry at `location_catalog_entry_index`. - DexRegisterLocation::Kind GetLocationInternalKind(size_t location_catalog_entry_index) const { - if (location_catalog_entry_index == kNoLocationEntryIndex) { - return DexRegisterLocation::Kind::kNone; - } - return ExtractKindAtOffset(FindLocationOffset(location_catalog_entry_index)); - } - - // Get the (surface) kind and value of entry at `location_catalog_entry_index`. - DexRegisterLocation GetDexRegisterLocation(size_t location_catalog_entry_index) const { - if (location_catalog_entry_index == kNoLocationEntryIndex) { - return DexRegisterLocation::None(); - } - size_t offset = FindLocationOffset(location_catalog_entry_index); - // Read the first byte and inspect its first 3 bits to get the location. - ShortLocation first_byte = region_.LoadUnaligned(offset); - DexRegisterLocation::Kind kind = ExtractKindFromShortLocation(first_byte); - if (DexRegisterLocation::IsShortLocationKind(kind)) { - // Short location. Extract the value from the remaining 5 bits. - int32_t value = ExtractValueFromShortLocation(first_byte); - if (kind == DexRegisterLocation::Kind::kInStack) { - // Convert the stack slot (short) offset to a byte offset value. - value *= kFrameSlotSize; - } - return DexRegisterLocation(kind, value); - } else { - // Large location. Read the four next bytes to get the value. - int32_t value = region_.LoadUnaligned(offset + sizeof(DexRegisterLocation::Kind)); - if (kind == DexRegisterLocation::Kind::kInStackLargeOffset) { - // Convert the stack slot (large) offset to a byte offset value. - value *= kFrameSlotSize; - } - return DexRegisterLocation(kind, value); - } - } - - // Compute the compressed kind of `location`. - static DexRegisterLocation::Kind ComputeCompressedKind(const DexRegisterLocation& location) { - DexRegisterLocation::Kind kind = location.GetInternalKind(); - switch (kind) { - case DexRegisterLocation::Kind::kInStack: - return IsShortStackOffsetValue(location.GetValue()) - ? DexRegisterLocation::Kind::kInStack - : DexRegisterLocation::Kind::kInStackLargeOffset; - - case DexRegisterLocation::Kind::kInRegister: - case DexRegisterLocation::Kind::kInRegisterHigh: - DCHECK_GE(location.GetValue(), 0); - DCHECK_LT(location.GetValue(), 1 << kValueBits); - return kind; - - case DexRegisterLocation::Kind::kInFpuRegister: - case DexRegisterLocation::Kind::kInFpuRegisterHigh: - DCHECK_GE(location.GetValue(), 0); - DCHECK_LT(location.GetValue(), 1 << kValueBits); - return kind; - - case DexRegisterLocation::Kind::kConstant: - return IsShortConstantValue(location.GetValue()) - ? DexRegisterLocation::Kind::kConstant - : DexRegisterLocation::Kind::kConstantLargeValue; - - case DexRegisterLocation::Kind::kConstantLargeValue: - case DexRegisterLocation::Kind::kInStackLargeOffset: - case DexRegisterLocation::Kind::kNone: - LOG(FATAL) << "Unexpected location kind " << kind; - } - UNREACHABLE(); - } - - // Can `location` be turned into a short location? - static bool CanBeEncodedAsShortLocation(const DexRegisterLocation& location) { - DexRegisterLocation::Kind kind = location.GetInternalKind(); - switch (kind) { - case DexRegisterLocation::Kind::kInStack: - return IsShortStackOffsetValue(location.GetValue()); - - case DexRegisterLocation::Kind::kInRegister: - case DexRegisterLocation::Kind::kInRegisterHigh: - case DexRegisterLocation::Kind::kInFpuRegister: - case DexRegisterLocation::Kind::kInFpuRegisterHigh: - return true; - - case DexRegisterLocation::Kind::kConstant: - return IsShortConstantValue(location.GetValue()); - - case DexRegisterLocation::Kind::kConstantLargeValue: - case DexRegisterLocation::Kind::kInStackLargeOffset: - case DexRegisterLocation::Kind::kNone: - LOG(FATAL) << "Unexpected location kind " << kind; - } - UNREACHABLE(); - } - - static size_t EntrySize(const DexRegisterLocation& location) { - return CanBeEncodedAsShortLocation(location) ? SingleShortEntrySize() : SingleLargeEntrySize(); - } - - static size_t SingleShortEntrySize() { - return sizeof(ShortLocation); - } - - static size_t SingleLargeEntrySize() { - return sizeof(DexRegisterLocation::Kind) + sizeof(int32_t); - } - - size_t Size() const { - return region_.size(); - } - - void Dump(VariableIndentationOutputStream* vios, - const CodeInfo& code_info); - - // Special (invalid) Dex register location catalog entry index meaning - // that there is no location for a given Dex register (i.e., it is - // mapped to a DexRegisterLocation::Kind::kNone location). - static constexpr size_t kNoLocationEntryIndex = -1; - - private: - static constexpr int kFixedSize = 0; - - // Width of the kind "field" in a short location, in bits. - static constexpr size_t kKindBits = 3; - // Width of the value "field" in a short location, in bits. - static constexpr size_t kValueBits = 5; - - static constexpr uint8_t kKindMask = (1 << kKindBits) - 1; - static constexpr int32_t kValueMask = (1 << kValueBits) - 1; - static constexpr size_t kKindOffset = 0; - static constexpr size_t kValueOffset = kKindBits; - - static bool IsShortStackOffsetValue(int32_t value) { - DCHECK_EQ(value % kFrameSlotSize, 0); - return IsShortValue(value / kFrameSlotSize); - } - - static bool IsShortConstantValue(int32_t value) { - return IsShortValue(value); - } - - static bool IsShortValue(int32_t value) { - return IsUint(value); - } - - static ShortLocation MakeShortLocation(DexRegisterLocation::Kind kind, int32_t value) { - uint8_t kind_integer_value = static_cast(kind); - DCHECK(IsUint(kind_integer_value)) << kind_integer_value; - DCHECK(IsShortValue(value)) << value; - return (kind_integer_value & kKindMask) << kKindOffset - | (value & kValueMask) << kValueOffset; - } - - static DexRegisterLocation::Kind ExtractKindFromShortLocation(ShortLocation location) { - uint8_t kind = (location >> kKindOffset) & kKindMask; - DCHECK_LE(kind, static_cast(DexRegisterLocation::Kind::kLastLocationKind)); - // We do not encode kNone locations in the stack map. - DCHECK_NE(kind, static_cast(DexRegisterLocation::Kind::kNone)); - return static_cast(kind); - } - - static int32_t ExtractValueFromShortLocation(ShortLocation location) { - return (location >> kValueOffset) & kValueMask; - } - - // Extract a location kind from the byte at position `offset`. - DexRegisterLocation::Kind ExtractKindAtOffset(size_t offset) const { - ShortLocation first_byte = region_.LoadUnaligned(offset); - return ExtractKindFromShortLocation(first_byte); - } - - MemoryRegion region_; - - friend class CodeInfo; - friend class StackMapStream; -}; +std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation& reg); -/* Information on Dex register locations for a specific PC, mapping a - * stack map's Dex register to a location entry in a DexRegisterLocationCatalog. - * The information is of the form: - * - * [live_bit_mask, entries*] - * - * where entries are concatenated unsigned integer values encoded on a number - * of bits (fixed per DexRegisterMap instances of a CodeInfo object) depending - * on the number of entries in the Dex register location catalog - * (see DexRegisterMap::SingleEntrySizeInBits). The map is 1-byte aligned. - */ +// Information on Dex register locations for a specific PC. +// Effectively just a convenience wrapper for DexRegisterLocation vector. +// If the size is small enough, it keeps the data on the stack. +// TODO: Replace this with generic purpose "small-vector" implementation. class DexRegisterMap { public: - explicit DexRegisterMap(MemoryRegion region) : region_(region) {} - DexRegisterMap() {} - - bool IsValid() const { return region_.pointer() != nullptr; } - - // Get the surface kind of Dex register `dex_register_number`. - DexRegisterLocation::Kind GetLocationKind(uint16_t dex_register_number, - uint16_t number_of_dex_registers, - const CodeInfo& code_info, - const CodeInfoEncoding& enc) const { - return DexRegisterLocation::ConvertToSurfaceKind( - GetLocationInternalKind(dex_register_number, number_of_dex_registers, code_info, enc)); - } - - // Get the internal kind of Dex register `dex_register_number`. - DexRegisterLocation::Kind GetLocationInternalKind(uint16_t dex_register_number, - uint16_t number_of_dex_registers, - const CodeInfo& code_info, - const CodeInfoEncoding& enc) const; - - // Get the Dex register location `dex_register_number`. - DexRegisterLocation GetDexRegisterLocation(uint16_t dex_register_number, - uint16_t number_of_dex_registers, - const CodeInfo& code_info, - const CodeInfoEncoding& enc) const; - - int32_t GetStackOffsetInBytes(uint16_t dex_register_number, - uint16_t number_of_dex_registers, - const CodeInfo& code_info, - const CodeInfoEncoding& enc) const { - DexRegisterLocation location = - GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info, enc); - DCHECK(location.GetKind() == DexRegisterLocation::Kind::kInStack); - // GetDexRegisterLocation returns the offset in bytes. - return location.GetValue(); - } - - int32_t GetConstant(uint16_t dex_register_number, - uint16_t number_of_dex_registers, - const CodeInfo& code_info, - const CodeInfoEncoding& enc) const { - DexRegisterLocation location = - GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info, enc); - DCHECK_EQ(location.GetKind(), DexRegisterLocation::Kind::kConstant); - return location.GetValue(); - } - - int32_t GetMachineRegister(uint16_t dex_register_number, - uint16_t number_of_dex_registers, - const CodeInfo& code_info, - const CodeInfoEncoding& enc) const { - DexRegisterLocation location = - GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info, enc); - DCHECK(location.GetInternalKind() == DexRegisterLocation::Kind::kInRegister || - location.GetInternalKind() == DexRegisterLocation::Kind::kInRegisterHigh || - location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegister || - location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegisterHigh) - << location.GetInternalKind(); - return location.GetValue(); - } - - // Get the index of the entry in the Dex register location catalog - // corresponding to `dex_register_number`. - size_t GetLocationCatalogEntryIndex(uint16_t dex_register_number, - uint16_t number_of_dex_registers, - size_t number_of_location_catalog_entries) const { - if (!IsDexRegisterLive(dex_register_number)) { - return DexRegisterLocationCatalog::kNoLocationEntryIndex; - } - - if (number_of_location_catalog_entries == 1) { - // We do not allocate space for location maps in the case of a - // single-entry location catalog, as it is useless. The only valid - // entry index is 0; - return 0; - } - - // The bit offset of the beginning of the map locations. - size_t map_locations_offset_in_bits = - GetLocationMappingDataOffset(number_of_dex_registers) * kBitsPerByte; - size_t index_in_dex_register_map = GetIndexInDexRegisterMap(dex_register_number); - DCHECK_LT(index_in_dex_register_map, GetNumberOfLiveDexRegisters(number_of_dex_registers)); - // The bit size of an entry. - size_t map_entry_size_in_bits = SingleEntrySizeInBits(number_of_location_catalog_entries); - // The bit offset where `index_in_dex_register_map` is located. - size_t entry_offset_in_bits = - map_locations_offset_in_bits + index_in_dex_register_map * map_entry_size_in_bits; - size_t location_catalog_entry_index = - region_.LoadBits(entry_offset_in_bits, map_entry_size_in_bits); - DCHECK_LT(location_catalog_entry_index, number_of_location_catalog_entries); - return location_catalog_entry_index; - } - - // Map entry at `index_in_dex_register_map` to `location_catalog_entry_index`. - void SetLocationCatalogEntryIndex(size_t index_in_dex_register_map, - size_t location_catalog_entry_index, - uint16_t number_of_dex_registers, - size_t number_of_location_catalog_entries) { - DCHECK_LT(index_in_dex_register_map, GetNumberOfLiveDexRegisters(number_of_dex_registers)); - DCHECK_LT(location_catalog_entry_index, number_of_location_catalog_entries); - - if (number_of_location_catalog_entries == 1) { - // We do not allocate space for location maps in the case of a - // single-entry location catalog, as it is useless. - return; - } - - // The bit offset of the beginning of the map locations. - size_t map_locations_offset_in_bits = - GetLocationMappingDataOffset(number_of_dex_registers) * kBitsPerByte; - // The bit size of an entry. - size_t map_entry_size_in_bits = SingleEntrySizeInBits(number_of_location_catalog_entries); - // The bit offset where `index_in_dex_register_map` is located. - size_t entry_offset_in_bits = - map_locations_offset_in_bits + index_in_dex_register_map * map_entry_size_in_bits; - region_.StoreBits(entry_offset_in_bits, location_catalog_entry_index, map_entry_size_in_bits); - } - - void SetLiveBitMask(uint16_t number_of_dex_registers, - const BitVector& live_dex_registers_mask) { - size_t live_bit_mask_offset_in_bits = GetLiveBitMaskOffset() * kBitsPerByte; - for (uint16_t i = 0; i < number_of_dex_registers; ++i) { - region_.StoreBit(live_bit_mask_offset_in_bits + i, live_dex_registers_mask.IsBitSet(i)); - } - } - - ALWAYS_INLINE bool IsDexRegisterLive(uint16_t dex_register_number) const { - size_t live_bit_mask_offset_in_bits = GetLiveBitMaskOffset() * kBitsPerByte; - return region_.LoadBit(live_bit_mask_offset_in_bits + dex_register_number); - } - - size_t GetNumberOfLiveDexRegisters(uint16_t number_of_dex_registers) const { - size_t number_of_live_dex_registers = 0; - for (size_t i = 0; i < number_of_dex_registers; ++i) { - if (IsDexRegisterLive(i)) { - ++number_of_live_dex_registers; - } - } - return number_of_live_dex_registers; - } - - static size_t GetLiveBitMaskOffset() { - return kFixedSize; - } - - // Compute the size of the live register bit mask (in bytes), for a - // method having `number_of_dex_registers` Dex registers. - static size_t GetLiveBitMaskSize(uint16_t number_of_dex_registers) { - return RoundUp(number_of_dex_registers, kBitsPerByte) / kBitsPerByte; - } - - static size_t GetLocationMappingDataOffset(uint16_t number_of_dex_registers) { - return GetLiveBitMaskOffset() + GetLiveBitMaskSize(number_of_dex_registers); - } - - size_t GetLocationMappingDataSize(uint16_t number_of_dex_registers, - size_t number_of_location_catalog_entries) const { - size_t location_mapping_data_size_in_bits = - GetNumberOfLiveDexRegisters(number_of_dex_registers) - * SingleEntrySizeInBits(number_of_location_catalog_entries); - return RoundUp(location_mapping_data_size_in_bits, kBitsPerByte) / kBitsPerByte; - } + using iterator = DexRegisterLocation*; + using const_iterator = const DexRegisterLocation*; - // Return the size of a map entry in bits. Note that if - // `number_of_location_catalog_entries` equals 1, this function returns 0, - // which is fine, as there is no need to allocate a map for a - // single-entry location catalog; the only valid location catalog entry index - // for a live register in this case is 0 and there is no need to - // store it. - static size_t SingleEntrySizeInBits(size_t number_of_location_catalog_entries) { - // Handle the case of 0, as we cannot pass 0 to art::WhichPowerOf2. - return number_of_location_catalog_entries == 0 - ? 0u - : WhichPowerOf2(RoundUpToPowerOfTwo(number_of_location_catalog_entries)); - } - - // Return the size of the DexRegisterMap object, in bytes. - size_t Size() const { - return region_.size(); - } - - void Dump(VariableIndentationOutputStream* vios, - const CodeInfo& code_info, uint16_t number_of_dex_registers) const; - - private: - // Return the index in the Dex register map corresponding to the Dex - // register number `dex_register_number`. - size_t GetIndexInDexRegisterMap(uint16_t dex_register_number) const { - if (!IsDexRegisterLive(dex_register_number)) { - return kInvalidIndexInDexRegisterMap; + // Create map for given number of registers and initialize them to the given value. + DexRegisterMap(size_t count, DexRegisterLocation value) : count_(count), regs_small_{} { + if (count_ <= kSmallCount) { + std::fill_n(regs_small_.begin(), count, value); + } else { + regs_large_.resize(count, value); } - return GetNumberOfLiveDexRegisters(dex_register_number); - } - - // Special (invalid) Dex register map entry index meaning that there - // is no index in the map for a given Dex register (i.e., it must - // have been mapped to a DexRegisterLocation::Kind::kNone location). - static constexpr size_t kInvalidIndexInDexRegisterMap = -1; - - static constexpr int kFixedSize = 0; - - MemoryRegion region_; - - friend class CodeInfo; - friend class StackMapStream; -}; - -// Represents bit range of bit-packed integer field. -// We reuse the idea from ULEB128p1 to support encoding of -1 (aka 0xFFFFFFFF). -// If min_value is set to -1, we implicitly subtract one from any loaded value, -// and add one to any stored value. This is generalized to any negative values. -// In other words, min_value acts as a base and the stored value is added to it. -struct FieldEncoding { - FieldEncoding(size_t start_offset, size_t end_offset, int32_t min_value = 0) - : start_offset_(start_offset), end_offset_(end_offset), min_value_(min_value) { - DCHECK_LE(start_offset_, end_offset_); - DCHECK_LE(BitSize(), 32u); } - ALWAYS_INLINE size_t BitSize() const { return end_offset_ - start_offset_; } - - template - ALWAYS_INLINE int32_t Load(const Region& region) const { - DCHECK_LE(end_offset_, region.size_in_bits()); - return static_cast(region.LoadBits(start_offset_, BitSize())) + min_value_; + DexRegisterLocation* data() { + return count_ <= kSmallCount ? regs_small_.data() : regs_large_.data(); } - - template - ALWAYS_INLINE void Store(Region region, int32_t value) const { - region.StoreBits(start_offset_, value - min_value_, BitSize()); - DCHECK_EQ(Load(region), value); + const DexRegisterLocation* data() const { + return count_ <= kSmallCount ? regs_small_.data() : regs_large_.data(); } - private: - size_t start_offset_; - size_t end_offset_; - int32_t min_value_; -}; + iterator begin() { return data(); } + iterator end() { return data() + count_; } + const_iterator begin() const { return data(); } + const_iterator end() const { return data() + count_; } + size_t size() const { return count_; } + bool empty() const { return count_ == 0; } -class StackMapEncoding { - public: - StackMapEncoding() - : dex_pc_bit_offset_(0), - dex_register_map_bit_offset_(0), - inline_info_bit_offset_(0), - register_mask_index_bit_offset_(0), - stack_mask_index_bit_offset_(0), - total_bit_size_(0) {} - - // Set stack map bit layout based on given sizes. - // Returns the size of stack map in bits. - size_t SetFromSizes(size_t native_pc_max, - size_t dex_pc_max, - size_t dex_register_map_size, - size_t number_of_inline_info, - size_t number_of_register_masks, - size_t number_of_stack_masks) { - total_bit_size_ = 0; - DCHECK_EQ(kNativePcBitOffset, total_bit_size_); - total_bit_size_ += MinimumBitsToStore(native_pc_max); - - dex_pc_bit_offset_ = total_bit_size_; - // Note: We're not encoding the dex pc if there is none. That's the case - // for an intrinsified native method, such as String.charAt(). - if (dex_pc_max != dex::kDexNoIndex) { - total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max); - } - - // We also need +1 for kNoDexRegisterMap, but since the size is strictly - // greater than any offset we might try to encode, we already implicitly have it. - dex_register_map_bit_offset_ = total_bit_size_; - total_bit_size_ += MinimumBitsToStore(dex_register_map_size); - - // We also need +1 for kNoInlineInfo, but since the inline_info_size is strictly - // greater than the offset we might try to encode, we already implicitly have it. - // If inline_info_size is zero, we can encode only kNoInlineInfo (in zero bits). - inline_info_bit_offset_ = total_bit_size_; - total_bit_size_ += MinimumBitsToStore(number_of_inline_info); - - register_mask_index_bit_offset_ = total_bit_size_; - total_bit_size_ += MinimumBitsToStore(number_of_register_masks); - - stack_mask_index_bit_offset_ = total_bit_size_; - total_bit_size_ += MinimumBitsToStore(number_of_stack_masks); - - return total_bit_size_; - } - - ALWAYS_INLINE FieldEncoding GetNativePcEncoding() const { - return FieldEncoding(kNativePcBitOffset, dex_pc_bit_offset_); - } - ALWAYS_INLINE FieldEncoding GetDexPcEncoding() const { - return FieldEncoding(dex_pc_bit_offset_, dex_register_map_bit_offset_, -1 /* min_value */); - } - ALWAYS_INLINE FieldEncoding GetDexRegisterMapEncoding() const { - return FieldEncoding(dex_register_map_bit_offset_, inline_info_bit_offset_, -1 /* min_value */); - } - ALWAYS_INLINE FieldEncoding GetInlineInfoEncoding() const { - return FieldEncoding(inline_info_bit_offset_, - register_mask_index_bit_offset_, - -1 /* min_value */); - } - ALWAYS_INLINE FieldEncoding GetRegisterMaskIndexEncoding() const { - return FieldEncoding(register_mask_index_bit_offset_, stack_mask_index_bit_offset_); + DexRegisterLocation& operator[](size_t index) { + DCHECK_LT(index, count_); + return data()[index]; } - ALWAYS_INLINE FieldEncoding GetStackMaskIndexEncoding() const { - return FieldEncoding(stack_mask_index_bit_offset_, total_bit_size_); - } - ALWAYS_INLINE size_t BitSize() const { - return total_bit_size_; + const DexRegisterLocation& operator[](size_t index) const { + DCHECK_LT(index, count_); + return data()[index]; } - // Encode the encoding into the vector. - template - void Encode(Vector* dest) const { - static_assert(alignof(StackMapEncoding) == 1, "Should not require alignment"); - const uint8_t* ptr = reinterpret_cast(this); - dest->insert(dest->end(), ptr, ptr + sizeof(*this)); + size_t GetNumberOfLiveDexRegisters() const { + return std::count_if(begin(), end(), [](auto& loc) { return loc.IsLive(); }); } - // Decode the encoding from a pointer, updates the pointer. - void Decode(const uint8_t** ptr) { - *this = *reinterpret_cast(*ptr); - *ptr += sizeof(*this); + bool HasAnyLiveDexRegisters() const { + return std::any_of(begin(), end(), [](auto& loc) { return loc.IsLive(); }); } void Dump(VariableIndentationOutputStream* vios) const; private: - static constexpr size_t kNativePcBitOffset = 0; - uint8_t dex_pc_bit_offset_; - uint8_t dex_register_map_bit_offset_; - uint8_t inline_info_bit_offset_; - uint8_t register_mask_index_bit_offset_; - uint8_t stack_mask_index_bit_offset_; - uint8_t total_bit_size_; + // Store the data inline if the number of registers is small to avoid memory allocations. + // If count_ <= kSmallCount, we use the regs_small_ array, and regs_large_ otherwise. + static constexpr size_t kSmallCount = 16; + size_t count_; + std::array regs_small_; + dchecked_vector regs_large_; }; /** @@ -794,859 +120,321 @@ class StackMapEncoding { * - Knowing which registers hold objects, * - Knowing the inlining information, * - Knowing the values of dex registers. - * - * The information is of the form: - * - * [native_pc_offset, dex_pc, dex_register_map_offset, inlining_info_index, register_mask_index, - * stack_mask_index]. */ -class StackMap { +class StackMap : public BitTableAccessor<8> { public: - StackMap() {} - explicit StackMap(BitMemoryRegion region) : region_(region) {} - - ALWAYS_INLINE bool IsValid() const { return region_.pointer() != nullptr; } - - ALWAYS_INLINE uint32_t GetDexPc(const StackMapEncoding& encoding) const { - return encoding.GetDexPcEncoding().Load(region_); - } - - ALWAYS_INLINE void SetDexPc(const StackMapEncoding& encoding, uint32_t dex_pc) { - encoding.GetDexPcEncoding().Store(region_, dex_pc); - } - - ALWAYS_INLINE uint32_t GetNativePcOffset(const StackMapEncoding& encoding, - InstructionSet instruction_set) const { - CodeOffset offset( - CodeOffset::FromCompressedOffset(encoding.GetNativePcEncoding().Load(region_))); - return offset.Uint32Value(instruction_set); - } - - ALWAYS_INLINE void SetNativePcCodeOffset(const StackMapEncoding& encoding, - CodeOffset native_pc_offset) { - encoding.GetNativePcEncoding().Store(region_, native_pc_offset.CompressedValue()); - } - - ALWAYS_INLINE uint32_t GetDexRegisterMapOffset(const StackMapEncoding& encoding) const { - return encoding.GetDexRegisterMapEncoding().Load(region_); - } - - ALWAYS_INLINE void SetDexRegisterMapOffset(const StackMapEncoding& encoding, uint32_t offset) { - encoding.GetDexRegisterMapEncoding().Store(region_, offset); - } - - ALWAYS_INLINE uint32_t GetInlineInfoIndex(const StackMapEncoding& encoding) const { - return encoding.GetInlineInfoEncoding().Load(region_); - } - - ALWAYS_INLINE void SetInlineInfoIndex(const StackMapEncoding& encoding, uint32_t index) { - encoding.GetInlineInfoEncoding().Store(region_, index); - } - - ALWAYS_INLINE uint32_t GetRegisterMaskIndex(const StackMapEncoding& encoding) const { - return encoding.GetRegisterMaskIndexEncoding().Load(region_); - } - - ALWAYS_INLINE void SetRegisterMaskIndex(const StackMapEncoding& encoding, uint32_t mask) { - encoding.GetRegisterMaskIndexEncoding().Store(region_, mask); - } + enum Kind { + Default = -1, + Catch = 0, + OSR = 1, + Debug = 2, + }; + BIT_TABLE_HEADER() + BIT_TABLE_COLUMN(0, Kind) + BIT_TABLE_COLUMN(1, PackedNativePc) + BIT_TABLE_COLUMN(2, DexPc) + BIT_TABLE_COLUMN(3, RegisterMaskIndex) + BIT_TABLE_COLUMN(4, StackMaskIndex) + BIT_TABLE_COLUMN(5, InlineInfoIndex) + BIT_TABLE_COLUMN(6, DexRegisterMaskIndex) + BIT_TABLE_COLUMN(7, DexRegisterMapIndex) - ALWAYS_INLINE uint32_t GetStackMaskIndex(const StackMapEncoding& encoding) const { - return encoding.GetStackMaskIndexEncoding().Load(region_); + ALWAYS_INLINE uint32_t GetNativePcOffset(InstructionSet instruction_set) const { + return UnpackNativePc(GetPackedNativePc(), instruction_set); } - ALWAYS_INLINE void SetStackMaskIndex(const StackMapEncoding& encoding, uint32_t mask) { - encoding.GetStackMaskIndexEncoding().Store(region_, mask); + ALWAYS_INLINE bool HasInlineInfo() const { + return HasInlineInfoIndex(); } - ALWAYS_INLINE bool HasDexRegisterMap(const StackMapEncoding& encoding) const { - return GetDexRegisterMapOffset(encoding) != kNoDexRegisterMap; + ALWAYS_INLINE bool HasDexRegisterMap() const { + return HasDexRegisterMapIndex(); } - ALWAYS_INLINE bool HasInlineInfo(const StackMapEncoding& encoding) const { - return GetInlineInfoIndex(encoding) != kNoInlineInfo; + static uint32_t PackNativePc(uint32_t native_pc, InstructionSet isa) { + DCHECK_ALIGNED_PARAM(native_pc, GetInstructionSetInstructionAlignment(isa)); + return native_pc / GetInstructionSetInstructionAlignment(isa); } - ALWAYS_INLINE bool Equals(const StackMap& other) const { - return region_.pointer() == other.region_.pointer() && - region_.size() == other.region_.size() && - region_.BitOffset() == other.region_.BitOffset(); + static uint32_t UnpackNativePc(uint32_t packed_native_pc, InstructionSet isa) { + uint32_t native_pc = packed_native_pc * GetInstructionSetInstructionAlignment(isa); + DCHECK_EQ(native_pc / GetInstructionSetInstructionAlignment(isa), packed_native_pc); + return native_pc; } void Dump(VariableIndentationOutputStream* vios, const CodeInfo& code_info, - const CodeInfoEncoding& encoding, const MethodInfo& method_info, uint32_t code_offset, - uint16_t number_of_dex_registers, - InstructionSet instruction_set, - const std::string& header_suffix = "") const; - - // Special (invalid) offset for the DexRegisterMapOffset field meaning - // that there is no Dex register map for this stack map. - static constexpr uint32_t kNoDexRegisterMap = -1; - - // Special (invalid) offset for the InlineDescriptorOffset field meaning - // that there is no inline info for this stack map. - static constexpr uint32_t kNoInlineInfo = -1; - - private: - static constexpr int kFixedSize = 0; - - BitMemoryRegion region_; - - friend class StackMapStream; -}; - -class InlineInfoEncoding { - public: - void SetFromSizes(size_t method_index_idx_max, - size_t dex_pc_max, - size_t extra_data_max, - size_t dex_register_map_size) { - total_bit_size_ = kMethodIndexBitOffset; - total_bit_size_ += MinimumBitsToStore(method_index_idx_max); - - dex_pc_bit_offset_ = dchecked_integral_cast(total_bit_size_); - // Note: We're not encoding the dex pc if there is none. That's the case - // for an intrinsified native method, such as String.charAt(). - if (dex_pc_max != dex::kDexNoIndex) { - total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max); - } - - extra_data_bit_offset_ = dchecked_integral_cast(total_bit_size_); - total_bit_size_ += MinimumBitsToStore(extra_data_max); - - // We also need +1 for kNoDexRegisterMap, but since the size is strictly - // greater than any offset we might try to encode, we already implicitly have it. - dex_register_map_bit_offset_ = dchecked_integral_cast(total_bit_size_); - total_bit_size_ += MinimumBitsToStore(dex_register_map_size); - } - - ALWAYS_INLINE FieldEncoding GetMethodIndexIdxEncoding() const { - return FieldEncoding(kMethodIndexBitOffset, dex_pc_bit_offset_); - } - ALWAYS_INLINE FieldEncoding GetDexPcEncoding() const { - return FieldEncoding(dex_pc_bit_offset_, extra_data_bit_offset_, -1 /* min_value */); - } - ALWAYS_INLINE FieldEncoding GetExtraDataEncoding() const { - return FieldEncoding(extra_data_bit_offset_, dex_register_map_bit_offset_); - } - ALWAYS_INLINE FieldEncoding GetDexRegisterMapEncoding() const { - return FieldEncoding(dex_register_map_bit_offset_, total_bit_size_, -1 /* min_value */); - } - ALWAYS_INLINE size_t BitSize() const { - return total_bit_size_; - } - - void Dump(VariableIndentationOutputStream* vios) const; - - // Encode the encoding into the vector. - template - void Encode(Vector* dest) const { - static_assert(alignof(InlineInfoEncoding) == 1, "Should not require alignment"); - const uint8_t* ptr = reinterpret_cast(this); - dest->insert(dest->end(), ptr, ptr + sizeof(*this)); - } - - // Decode the encoding from a pointer, updates the pointer. - void Decode(const uint8_t** ptr) { - *this = *reinterpret_cast(*ptr); - *ptr += sizeof(*this); - } - - private: - static constexpr uint8_t kIsLastBitOffset = 0; - static constexpr uint8_t kMethodIndexBitOffset = 1; - uint8_t dex_pc_bit_offset_; - uint8_t extra_data_bit_offset_; - uint8_t dex_register_map_bit_offset_; - uint8_t total_bit_size_; + InstructionSet instruction_set) const; }; /** - * Inline information for a specific PC. The information is of the form: - * - * [is_last, - * method_index (or ArtMethod high bits), - * dex_pc, - * extra_data (ArtMethod low bits or 1), - * dex_register_map_offset]+. + * Inline information for a specific PC. + * The row referenced from the StackMap holds information at depth 0. + * Following rows hold information for further depths. */ -class InlineInfo { +class InlineInfo : public BitTableAccessor<6> { public: - explicit InlineInfo(BitMemoryRegion region) : region_(region) {} - - ALWAYS_INLINE uint32_t GetDepth(const InlineInfoEncoding& encoding) const { - size_t depth = 0; - while (!GetRegionAtDepth(encoding, depth++).LoadBit(0)) { } // Check is_last bit. - return depth; - } - - ALWAYS_INLINE void SetDepth(const InlineInfoEncoding& encoding, uint32_t depth) { - DCHECK_GT(depth, 0u); - for (size_t d = 0; d < depth; ++d) { - GetRegionAtDepth(encoding, d).StoreBit(0, d == depth - 1); // Set is_last bit. - } - } - - ALWAYS_INLINE uint32_t GetMethodIndexIdxAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth) const { - DCHECK(!EncodesArtMethodAtDepth(encoding, depth)); - return encoding.GetMethodIndexIdxEncoding().Load(GetRegionAtDepth(encoding, depth)); - } - - ALWAYS_INLINE void SetMethodIndexIdxAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth, - uint32_t index) { - encoding.GetMethodIndexIdxEncoding().Store(GetRegionAtDepth(encoding, depth), index); - } - + BIT_TABLE_HEADER() + BIT_TABLE_COLUMN(0, IsLast) // Determines if there are further rows for further depths. + BIT_TABLE_COLUMN(1, DexPc) + BIT_TABLE_COLUMN(2, MethodInfoIndex) + BIT_TABLE_COLUMN(3, ArtMethodHi) // High bits of ArtMethod*. + BIT_TABLE_COLUMN(4, ArtMethodLo) // Low bits of ArtMethod*. + BIT_TABLE_COLUMN(5, NumberOfDexRegisters) // Includes outer levels and the main method. + BIT_TABLE_COLUMN(6, DexRegisterMapIndex) - ALWAYS_INLINE uint32_t GetMethodIndexAtDepth(const InlineInfoEncoding& encoding, - const MethodInfo& method_info, - uint32_t depth) const { - return method_info.GetMethodIndex(GetMethodIndexIdxAtDepth(encoding, depth)); - } - - ALWAYS_INLINE uint32_t GetDexPcAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth) const { - return encoding.GetDexPcEncoding().Load(GetRegionAtDepth(encoding, depth)); - } + static constexpr uint32_t kLast = -1; + static constexpr uint32_t kMore = 0; - ALWAYS_INLINE void SetDexPcAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth, - uint32_t dex_pc) { - encoding.GetDexPcEncoding().Store(GetRegionAtDepth(encoding, depth), dex_pc); - } - - ALWAYS_INLINE bool EncodesArtMethodAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth) const { - return (encoding.GetExtraDataEncoding().Load(GetRegionAtDepth(encoding, depth)) & 1) == 0; - } - - ALWAYS_INLINE void SetExtraDataAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth, - uint32_t extra_data) { - encoding.GetExtraDataEncoding().Store(GetRegionAtDepth(encoding, depth), extra_data); - } - - ALWAYS_INLINE ArtMethod* GetArtMethodAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth) const { - uint32_t low_bits = encoding.GetExtraDataEncoding().Load(GetRegionAtDepth(encoding, depth)); - uint32_t high_bits = encoding.GetMethodIndexIdxEncoding().Load( - GetRegionAtDepth(encoding, depth)); - if (high_bits == 0) { - return reinterpret_cast(low_bits); - } else { - uint64_t address = high_bits; - address = address << 32; - return reinterpret_cast(address | low_bits); - } + uint32_t GetMethodIndex(const MethodInfo& method_info) const { + return method_info.GetMethodIndex(GetMethodInfoIndex()); } - ALWAYS_INLINE uint32_t GetDexRegisterMapOffsetAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth) const { - return encoding.GetDexRegisterMapEncoding().Load(GetRegionAtDepth(encoding, depth)); + bool EncodesArtMethod() const { + return HasArtMethodLo(); } - ALWAYS_INLINE void SetDexRegisterMapOffsetAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth, - uint32_t offset) { - encoding.GetDexRegisterMapEncoding().Store(GetRegionAtDepth(encoding, depth), offset); - } - - ALWAYS_INLINE bool HasDexRegisterMapAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth) const { - return GetDexRegisterMapOffsetAtDepth(encoding, depth) != StackMap::kNoDexRegisterMap; + ArtMethod* GetArtMethod() const { + uint64_t lo = GetArtMethodLo(); + uint64_t hi = GetArtMethodHi(); + return reinterpret_cast((hi << 32) | lo); } void Dump(VariableIndentationOutputStream* vios, const CodeInfo& info, - const MethodInfo& method_info, - uint16_t* number_of_dex_registers) const; - - private: - ALWAYS_INLINE BitMemoryRegion GetRegionAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth) const { - size_t entry_size = encoding.BitSize(); - DCHECK_GT(entry_size, 0u); - return region_.Subregion(depth * entry_size, entry_size); - } - - BitMemoryRegion region_; + const StackMap& stack_map, + const MethodInfo& method_info) const; }; -// Bit sized region encoding, may be more than 255 bits. -class BitRegionEncoding { +class MaskInfo : public BitTableAccessor<1> { public: - uint32_t num_bits = 0; - - ALWAYS_INLINE size_t BitSize() const { - return num_bits; - } - - template - void Encode(Vector* dest) const { - EncodeUnsignedLeb128(dest, num_bits); // Use leb in case num_bits is greater than 255. - } - - void Decode(const uint8_t** ptr) { - num_bits = DecodeUnsignedLeb128(ptr); - } -}; - -// A table of bit sized encodings. -template -struct BitEncodingTable { - static constexpr size_t kInvalidOffset = static_cast(-1); - // How the encoding is laid out (serialized). - Encoding encoding; - - // Number of entries in the table (serialized). - size_t num_entries; - - // Bit offset for the base of the table (computed). - size_t bit_offset = kInvalidOffset; - - template - void Encode(Vector* dest) const { - EncodeUnsignedLeb128(dest, num_entries); - encoding.Encode(dest); - } - - ALWAYS_INLINE void Decode(const uint8_t** ptr) { - num_entries = DecodeUnsignedLeb128(ptr); - encoding.Decode(ptr); - } - - // Set the bit offset in the table and adds the space used by the table to offset. - void UpdateBitOffset(size_t* offset) { - DCHECK(offset != nullptr); - bit_offset = *offset; - *offset += encoding.BitSize() * num_entries; - } - - // Return the bit region for the map at index i. - ALWAYS_INLINE BitMemoryRegion BitRegion(MemoryRegion region, size_t index) const { - DCHECK_NE(bit_offset, kInvalidOffset) << "Invalid table offset"; - DCHECK_LT(index, num_entries); - const size_t map_size = encoding.BitSize(); - return BitMemoryRegion(region, bit_offset + index * map_size, map_size); - } -}; - -// A byte sized table of possible variable sized encodings. -struct ByteSizedTable { - static constexpr size_t kInvalidOffset = static_cast(-1); - - // Number of entries in the table (serialized). - size_t num_entries = 0; - - // Number of bytes of the table (serialized). - size_t num_bytes; - - // Bit offset for the base of the table (computed). - size_t byte_offset = kInvalidOffset; - - template - void Encode(Vector* dest) const { - EncodeUnsignedLeb128(dest, num_entries); - EncodeUnsignedLeb128(dest, num_bytes); - } - - ALWAYS_INLINE void Decode(const uint8_t** ptr) { - num_entries = DecodeUnsignedLeb128(ptr); - num_bytes = DecodeUnsignedLeb128(ptr); - } - - // Set the bit offset of the table. Adds the total bit size of the table to offset. - void UpdateBitOffset(size_t* offset) { - DCHECK(offset != nullptr); - DCHECK_ALIGNED(*offset, kBitsPerByte); - byte_offset = *offset / kBitsPerByte; - *offset += num_bytes * kBitsPerByte; - } + BIT_TABLE_HEADER() + BIT_TABLE_COLUMN(0, Mask) }; -// Format is [native pc, invoke type, method index]. -class InvokeInfoEncoding { +class DexRegisterMapInfo : public BitTableAccessor<1> { public: - void SetFromSizes(size_t native_pc_max, - size_t invoke_type_max, - size_t method_index_max) { - total_bit_size_ = 0; - DCHECK_EQ(kNativePcBitOffset, total_bit_size_); - total_bit_size_ += MinimumBitsToStore(native_pc_max); - invoke_type_bit_offset_ = total_bit_size_; - total_bit_size_ += MinimumBitsToStore(invoke_type_max); - method_index_bit_offset_ = total_bit_size_; - total_bit_size_ += MinimumBitsToStore(method_index_max); - } - - ALWAYS_INLINE FieldEncoding GetNativePcEncoding() const { - return FieldEncoding(kNativePcBitOffset, invoke_type_bit_offset_); - } - - ALWAYS_INLINE FieldEncoding GetInvokeTypeEncoding() const { - return FieldEncoding(invoke_type_bit_offset_, method_index_bit_offset_); - } - - ALWAYS_INLINE FieldEncoding GetMethodIndexEncoding() const { - return FieldEncoding(method_index_bit_offset_, total_bit_size_); - } - - ALWAYS_INLINE size_t BitSize() const { - return total_bit_size_; - } - - template - void Encode(Vector* dest) const { - static_assert(alignof(InvokeInfoEncoding) == 1, "Should not require alignment"); - const uint8_t* ptr = reinterpret_cast(this); - dest->insert(dest->end(), ptr, ptr + sizeof(*this)); - } - - void Decode(const uint8_t** ptr) { - *this = *reinterpret_cast(*ptr); - *ptr += sizeof(*this); - } - - private: - static constexpr uint8_t kNativePcBitOffset = 0; - uint8_t invoke_type_bit_offset_; - uint8_t method_index_bit_offset_; - uint8_t total_bit_size_; + BIT_TABLE_HEADER() + BIT_TABLE_COLUMN(0, CatalogueIndex) }; -class InvokeInfo { +class DexRegisterInfo : public BitTableAccessor<2> { public: - explicit InvokeInfo(BitMemoryRegion region) : region_(region) {} - - ALWAYS_INLINE uint32_t GetNativePcOffset(const InvokeInfoEncoding& encoding, - InstructionSet instruction_set) const { - CodeOffset offset( - CodeOffset::FromCompressedOffset(encoding.GetNativePcEncoding().Load(region_))); - return offset.Uint32Value(instruction_set); - } - - ALWAYS_INLINE void SetNativePcCodeOffset(const InvokeInfoEncoding& encoding, - CodeOffset native_pc_offset) { - encoding.GetNativePcEncoding().Store(region_, native_pc_offset.CompressedValue()); - } - - ALWAYS_INLINE uint32_t GetInvokeType(const InvokeInfoEncoding& encoding) const { - return encoding.GetInvokeTypeEncoding().Load(region_); - } - - ALWAYS_INLINE void SetInvokeType(const InvokeInfoEncoding& encoding, uint32_t invoke_type) { - encoding.GetInvokeTypeEncoding().Store(region_, invoke_type); - } - - ALWAYS_INLINE uint32_t GetMethodIndexIdx(const InvokeInfoEncoding& encoding) const { - return encoding.GetMethodIndexEncoding().Load(region_); - } - - ALWAYS_INLINE void SetMethodIndexIdx(const InvokeInfoEncoding& encoding, - uint32_t method_index_idx) { - encoding.GetMethodIndexEncoding().Store(region_, method_index_idx); - } + BIT_TABLE_HEADER() + BIT_TABLE_COLUMN(0, Kind) + BIT_TABLE_COLUMN(1, PackedValue) - ALWAYS_INLINE uint32_t GetMethodIndex(const InvokeInfoEncoding& encoding, - MethodInfo method_info) const { - return method_info.GetMethodIndex(GetMethodIndexIdx(encoding)); + ALWAYS_INLINE DexRegisterLocation GetLocation() const { + DexRegisterLocation::Kind kind = static_cast(GetKind()); + return DexRegisterLocation(kind, UnpackValue(kind, GetPackedValue())); } - bool IsValid() const { return region_.pointer() != nullptr; } - - private: - BitMemoryRegion region_; -}; - -// Most of the fields are encoded as ULEB128 to save space. -struct CodeInfoEncoding { - using SizeType = uint32_t; - - static constexpr SizeType kInvalidSize = std::numeric_limits::max(); - - // Byte sized tables go first to avoid unnecessary alignment bits. - ByteSizedTable dex_register_map; - ByteSizedTable location_catalog; - BitEncodingTable stack_map; - BitEncodingTable register_mask; - BitEncodingTable stack_mask; - BitEncodingTable invoke_info; - BitEncodingTable inline_info; - - CodeInfoEncoding() {} - - explicit CodeInfoEncoding(const void* data) { - const uint8_t* ptr = reinterpret_cast(data); - dex_register_map.Decode(&ptr); - location_catalog.Decode(&ptr); - stack_map.Decode(&ptr); - register_mask.Decode(&ptr); - stack_mask.Decode(&ptr); - invoke_info.Decode(&ptr); - if (stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0) { - inline_info.Decode(&ptr); - } else { - inline_info = BitEncodingTable(); + static uint32_t PackValue(DexRegisterLocation::Kind kind, uint32_t value) { + uint32_t packed_value = value; + if (kind == DexRegisterLocation::Kind::kInStack) { + DCHECK(IsAligned(packed_value)); + packed_value /= kFrameSlotSize; } - cache_header_size = - dchecked_integral_cast(ptr - reinterpret_cast(data)); - ComputeTableOffsets(); + return packed_value; } - // Compress is not const since it calculates cache_header_size. This is used by PrepareForFillIn. - template - void Compress(Vector* dest) { - dex_register_map.Encode(dest); - location_catalog.Encode(dest); - stack_map.Encode(dest); - register_mask.Encode(dest); - stack_mask.Encode(dest); - invoke_info.Encode(dest); - if (stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0) { - inline_info.Encode(dest); + static uint32_t UnpackValue(DexRegisterLocation::Kind kind, uint32_t packed_value) { + uint32_t value = packed_value; + if (kind == DexRegisterLocation::Kind::kInStack) { + value *= kFrameSlotSize; } - cache_header_size = dest->size(); - } - - ALWAYS_INLINE void ComputeTableOffsets() { - // Skip the header. - size_t bit_offset = HeaderSize() * kBitsPerByte; - // The byte tables must be aligned so they must go first. - dex_register_map.UpdateBitOffset(&bit_offset); - location_catalog.UpdateBitOffset(&bit_offset); - // Other tables don't require alignment. - stack_map.UpdateBitOffset(&bit_offset); - register_mask.UpdateBitOffset(&bit_offset); - stack_mask.UpdateBitOffset(&bit_offset); - invoke_info.UpdateBitOffset(&bit_offset); - inline_info.UpdateBitOffset(&bit_offset); - cache_non_header_size = RoundUp(bit_offset, kBitsPerByte) / kBitsPerByte - HeaderSize(); + return value; } +}; - ALWAYS_INLINE size_t HeaderSize() const { - DCHECK_NE(cache_header_size, kInvalidSize) << "Uninitialized"; - return cache_header_size; - } +// Register masks tend to have many trailing zero bits (caller-saves are usually not encoded), +// therefore it is worth encoding the mask as value+shift. +class RegisterMask : public BitTableAccessor<2> { + public: + BIT_TABLE_HEADER() + BIT_TABLE_COLUMN(0, Value) + BIT_TABLE_COLUMN(1, Shift) - ALWAYS_INLINE size_t NonHeaderSize() const { - DCHECK_NE(cache_non_header_size, kInvalidSize) << "Uninitialized"; - return cache_non_header_size; + ALWAYS_INLINE uint32_t GetMask() const { + return GetValue() << GetShift(); } - - private: - // Computed fields (not serialized). - // Header size in bytes, cached to avoid needing to re-decoding the encoding in HeaderSize. - SizeType cache_header_size = kInvalidSize; - // Non header size in bytes, cached to avoid needing to re-decoding the encoding in NonHeaderSize. - SizeType cache_non_header_size = kInvalidSize; }; /** * Wrapper around all compiler information collected for a method. - * The information is of the form: - * - * [CodeInfoEncoding, DexRegisterMap+, DexLocationCatalog+, StackMap+, RegisterMask+, StackMask+, - * InlineInfo*] - * - * where CodeInfoEncoding is of the form: - * - * [ByteSizedTable(dex_register_map), ByteSizedTable(location_catalog), - * BitEncodingTable, BitEncodingTable, - * BitEncodingTable, BitEncodingTable] + * See the Decode method at the end for the precise binary format. */ class CodeInfo { public: - explicit CodeInfo(MemoryRegion region) : region_(region) { - } - explicit CodeInfo(const void* data) { - CodeInfoEncoding encoding = CodeInfoEncoding(data); - region_ = MemoryRegion(const_cast(data), - encoding.HeaderSize() + encoding.NonHeaderSize()); + Decode(reinterpret_cast(data)); } - CodeInfoEncoding ExtractEncoding() const { - CodeInfoEncoding encoding(region_.begin()); - AssertValidStackMap(encoding); - return encoding; + explicit CodeInfo(MemoryRegion region) : CodeInfo(region.begin()) { + DCHECK_EQ(Size(), region.size()); } - bool HasInlineInfo(const CodeInfoEncoding& encoding) const { - return encoding.stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0; - } - - DexRegisterLocationCatalog GetDexRegisterLocationCatalog(const CodeInfoEncoding& encoding) const { - return DexRegisterLocationCatalog(region_.Subregion(encoding.location_catalog.byte_offset, - encoding.location_catalog.num_bytes)); - } - - ALWAYS_INLINE size_t GetNumberOfStackMaskBits(const CodeInfoEncoding& encoding) const { - return encoding.stack_mask.encoding.BitSize(); - } + explicit CodeInfo(const OatQuickMethodHeader* header); - ALWAYS_INLINE StackMap GetStackMapAt(size_t index, const CodeInfoEncoding& encoding) const { - return StackMap(encoding.stack_map.BitRegion(region_, index)); + size_t Size() const { + return BitsToBytesRoundUp(size_in_bits_); } - BitMemoryRegion GetStackMask(size_t index, const CodeInfoEncoding& encoding) const { - return encoding.stack_mask.BitRegion(region_, index); + ALWAYS_INLINE const BitTable& GetStackMaps() const { + return stack_maps_; } - BitMemoryRegion GetStackMaskOf(const CodeInfoEncoding& encoding, - const StackMap& stack_map) const { - return GetStackMask(stack_map.GetStackMaskIndex(encoding.stack_map.encoding), encoding); + ALWAYS_INLINE StackMap GetStackMapAt(size_t index) const { + return stack_maps_.GetRow(index); } - BitMemoryRegion GetRegisterMask(size_t index, const CodeInfoEncoding& encoding) const { - return encoding.register_mask.BitRegion(region_, index); + BitMemoryRegion GetStackMask(size_t index) const { + return stack_masks_.GetBitMemoryRegion(index); } - uint32_t GetRegisterMaskOf(const CodeInfoEncoding& encoding, const StackMap& stack_map) const { - size_t index = stack_map.GetRegisterMaskIndex(encoding.stack_map.encoding); - return GetRegisterMask(index, encoding).LoadBits(0u, encoding.register_mask.encoding.BitSize()); + BitMemoryRegion GetStackMaskOf(const StackMap& stack_map) const { + uint32_t index = stack_map.GetStackMaskIndex(); + return (index == StackMap::kNoValue) ? BitMemoryRegion() : GetStackMask(index); } - uint32_t GetNumberOfLocationCatalogEntries(const CodeInfoEncoding& encoding) const { - return encoding.location_catalog.num_entries; + uint32_t GetRegisterMaskOf(const StackMap& stack_map) const { + uint32_t index = stack_map.GetRegisterMaskIndex(); + return (index == StackMap::kNoValue) ? 0 : register_masks_.GetRow(index).GetMask(); } - uint32_t GetDexRegisterLocationCatalogSize(const CodeInfoEncoding& encoding) const { - return encoding.location_catalog.num_bytes; + uint32_t GetNumberOfLocationCatalogEntries() const { + return dex_register_catalog_.NumRows(); } - uint32_t GetNumberOfStackMaps(const CodeInfoEncoding& encoding) const { - return encoding.stack_map.num_entries; + ALWAYS_INLINE DexRegisterLocation GetDexRegisterCatalogEntry(size_t index) const { + return (index == StackMap::kNoValue) + ? DexRegisterLocation::None() + : dex_register_catalog_.GetRow(index).GetLocation(); } - // Get the size of all the stack maps of this CodeInfo object, in bits. Not byte aligned. - ALWAYS_INLINE size_t GetStackMapsSizeInBits(const CodeInfoEncoding& encoding) const { - return encoding.stack_map.encoding.BitSize() * GetNumberOfStackMaps(encoding); + bool HasInlineInfo() const { + return inline_infos_.NumRows() > 0; } - InvokeInfo GetInvokeInfo(const CodeInfoEncoding& encoding, size_t index) const { - return InvokeInfo(encoding.invoke_info.BitRegion(region_, index)); + uint32_t GetNumberOfStackMaps() const { + return stack_maps_.NumRows(); } - DexRegisterMap GetDexRegisterMapOf(StackMap stack_map, - const CodeInfoEncoding& encoding, - size_t number_of_dex_registers) const { - if (!stack_map.HasDexRegisterMap(encoding.stack_map.encoding)) { - return DexRegisterMap(); + ALWAYS_INLINE DexRegisterMap GetDexRegisterMapOf(StackMap stack_map) const { + if (stack_map.HasDexRegisterMap()) { + DexRegisterMap map(number_of_dex_registers_, DexRegisterLocation::Invalid()); + DecodeDexRegisterMap(stack_map.Row(), /* first_dex_register */ 0, &map); + return map; } - const uint32_t offset = encoding.dex_register_map.byte_offset + - stack_map.GetDexRegisterMapOffset(encoding.stack_map.encoding); - size_t size = ComputeDexRegisterMapSizeOf(encoding, offset, number_of_dex_registers); - return DexRegisterMap(region_.Subregion(offset, size)); + return DexRegisterMap(0, DexRegisterLocation::None()); } - size_t GetDexRegisterMapsSize(const CodeInfoEncoding& encoding, - uint32_t number_of_dex_registers) const { - size_t total = 0; - for (size_t i = 0, e = GetNumberOfStackMaps(encoding); i < e; ++i) { - StackMap stack_map = GetStackMapAt(i, encoding); - DexRegisterMap map(GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers)); - total += map.Size(); + ALWAYS_INLINE DexRegisterMap GetInlineDexRegisterMapOf(StackMap stack_map, + InlineInfo inline_info) const { + if (stack_map.HasDexRegisterMap()) { + DCHECK(stack_map.HasInlineInfoIndex()); + uint32_t depth = inline_info.Row() - stack_map.GetInlineInfoIndex(); + // The register counts are commutative and include all outer levels. + // This allows us to determine the range [first, last) in just two lookups. + // If we are at depth 0 (the first inlinee), the count from the main method is used. + uint32_t first = (depth == 0) + ? number_of_dex_registers_ + : inline_infos_.GetRow(inline_info.Row() - 1).GetNumberOfDexRegisters(); + uint32_t last = inline_info.GetNumberOfDexRegisters(); + DexRegisterMap map(last - first, DexRegisterLocation::Invalid()); + DecodeDexRegisterMap(stack_map.Row(), first, &map); + return map; } - return total; + return DexRegisterMap(0, DexRegisterLocation::None()); } - // Return the `DexRegisterMap` pointed by `inline_info` at depth `depth`. - DexRegisterMap GetDexRegisterMapAtDepth(uint8_t depth, - InlineInfo inline_info, - const CodeInfoEncoding& encoding, - uint32_t number_of_dex_registers) const { - if (!inline_info.HasDexRegisterMapAtDepth(encoding.inline_info.encoding, depth)) { - return DexRegisterMap(); + BitTableRange GetInlineInfosOf(StackMap stack_map) const { + uint32_t index = stack_map.GetInlineInfoIndex(); + if (index != StackMap::kNoValue) { + auto begin = inline_infos_.begin() + index; + auto end = begin; + while ((*end++).GetIsLast() == InlineInfo::kMore) { } + return BitTableRange(begin, end); } else { - uint32_t offset = encoding.dex_register_map.byte_offset + - inline_info.GetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding, depth); - size_t size = ComputeDexRegisterMapSizeOf(encoding, offset, number_of_dex_registers); - return DexRegisterMap(region_.Subregion(offset, size)); + return BitTableRange(); } } - InlineInfo GetInlineInfo(size_t index, const CodeInfoEncoding& encoding) const { - // Since we do not know the depth, we just return the whole remaining map. The caller may - // access the inline info for arbitrary depths. To return the precise inline info we would need - // to count the depth before returning. - // TODO: Clean this up. - const size_t bit_offset = encoding.inline_info.bit_offset + - index * encoding.inline_info.encoding.BitSize(); - return InlineInfo(BitMemoryRegion(region_, bit_offset, region_.size_in_bits() - bit_offset)); - } - - InlineInfo GetInlineInfoOf(StackMap stack_map, const CodeInfoEncoding& encoding) const { - DCHECK(stack_map.HasInlineInfo(encoding.stack_map.encoding)); - uint32_t index = stack_map.GetInlineInfoIndex(encoding.stack_map.encoding); - return GetInlineInfo(index, encoding); - } - - StackMap GetStackMapForDexPc(uint32_t dex_pc, const CodeInfoEncoding& encoding) const { - for (size_t i = 0, e = GetNumberOfStackMaps(encoding); i < e; ++i) { - StackMap stack_map = GetStackMapAt(i, encoding); - if (stack_map.GetDexPc(encoding.stack_map.encoding) == dex_pc) { + StackMap GetStackMapForDexPc(uint32_t dex_pc) const { + for (StackMap stack_map : stack_maps_) { + if (stack_map.GetDexPc() == dex_pc && stack_map.GetKind() != StackMap::Kind::Debug) { return stack_map; } } - return StackMap(); + return stack_maps_.GetInvalidRow(); } - // Searches the stack map list backwards because catch stack maps are stored - // at the end. - StackMap GetCatchStackMapForDexPc(uint32_t dex_pc, const CodeInfoEncoding& encoding) const { - for (size_t i = GetNumberOfStackMaps(encoding); i > 0; --i) { - StackMap stack_map = GetStackMapAt(i - 1, encoding); - if (stack_map.GetDexPc(encoding.stack_map.encoding) == dex_pc) { + // Searches the stack map list backwards because catch stack maps are stored at the end. + StackMap GetCatchStackMapForDexPc(uint32_t dex_pc) const { + for (size_t i = GetNumberOfStackMaps(); i > 0; --i) { + StackMap stack_map = GetStackMapAt(i - 1); + if (stack_map.GetDexPc() == dex_pc && stack_map.GetKind() == StackMap::Kind::Catch) { return stack_map; } } - return StackMap(); + return stack_maps_.GetInvalidRow(); } - StackMap GetOsrStackMapForDexPc(uint32_t dex_pc, const CodeInfoEncoding& encoding) const { - size_t e = GetNumberOfStackMaps(encoding); - if (e == 0) { - // There cannot be OSR stack map if there is no stack map. - return StackMap(); - } - // Walk over all stack maps. If two consecutive stack maps are identical, then we - // have found a stack map suitable for OSR. - const StackMapEncoding& stack_map_encoding = encoding.stack_map.encoding; - for (size_t i = 0; i < e - 1; ++i) { - StackMap stack_map = GetStackMapAt(i, encoding); - if (stack_map.GetDexPc(stack_map_encoding) == dex_pc) { - StackMap other = GetStackMapAt(i + 1, encoding); - if (other.GetDexPc(stack_map_encoding) == dex_pc && - other.GetNativePcOffset(stack_map_encoding, kRuntimeISA) == - stack_map.GetNativePcOffset(stack_map_encoding, kRuntimeISA)) { - DCHECK_EQ(other.GetDexRegisterMapOffset(stack_map_encoding), - stack_map.GetDexRegisterMapOffset(stack_map_encoding)); - DCHECK(!stack_map.HasInlineInfo(stack_map_encoding)); - if (i < e - 2) { - // Make sure there are not three identical stack maps following each other. - DCHECK_NE( - stack_map.GetNativePcOffset(stack_map_encoding, kRuntimeISA), - GetStackMapAt(i + 2, encoding).GetNativePcOffset(stack_map_encoding, kRuntimeISA)); - } - return stack_map; - } - } - } - return StackMap(); - } - - StackMap GetStackMapForNativePcOffset(uint32_t native_pc_offset, - const CodeInfoEncoding& encoding) const { - // TODO: Safepoint stack maps are sorted by native_pc_offset but catch stack - // maps are not. If we knew that the method does not have try/catch, - // we could do binary search. - for (size_t i = 0, e = GetNumberOfStackMaps(encoding); i < e; ++i) { - StackMap stack_map = GetStackMapAt(i, encoding); - if (stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA) == - native_pc_offset) { + StackMap GetOsrStackMapForDexPc(uint32_t dex_pc) const { + for (StackMap stack_map : stack_maps_) { + if (stack_map.GetDexPc() == dex_pc && stack_map.GetKind() == StackMap::Kind::OSR) { return stack_map; } } - return StackMap(); + return stack_maps_.GetInvalidRow(); } - InvokeInfo GetInvokeInfoForNativePcOffset(uint32_t native_pc_offset, - const CodeInfoEncoding& encoding) { - for (size_t index = 0; index < encoding.invoke_info.num_entries; index++) { - InvokeInfo item = GetInvokeInfo(encoding, index); - if (item.GetNativePcOffset(encoding.invoke_info.encoding, kRuntimeISA) == native_pc_offset) { - return item; - } - } - return InvokeInfo(BitMemoryRegion()); - } + StackMap GetStackMapForNativePcOffset(uint32_t pc, InstructionSet isa = kRuntimeISA) const; - // Dump this CodeInfo object on `os`. `code_offset` is the (absolute) - // native PC of the compiled method and `number_of_dex_registers` the - // number of Dex virtual registers used in this method. If - // `dump_stack_maps` is true, also dump the stack maps and the - // associated Dex register maps. + // Dump this CodeInfo object on `vios`. + // `code_offset` is the (absolute) native PC of the compiled method. void Dump(VariableIndentationOutputStream* vios, uint32_t code_offset, - uint16_t number_of_dex_registers, - bool dump_stack_maps, + bool verbose, InstructionSet instruction_set, const MethodInfo& method_info) const; - // Check that the code info has valid stack map and abort if it does not. - void AssertValidStackMap(const CodeInfoEncoding& encoding) const { - if (region_.size() != 0 && region_.size_in_bits() < GetStackMapsSizeInBits(encoding)) { - LOG(FATAL) << region_.size() << "\n" - << encoding.HeaderSize() << "\n" - << encoding.NonHeaderSize() << "\n" - << encoding.location_catalog.num_entries << "\n" - << encoding.stack_map.num_entries << "\n" - << encoding.stack_map.encoding.BitSize(); - } - } - - private: - // Compute the size of the Dex register map associated to the stack map at - // `dex_register_map_offset_in_code_info`. - size_t ComputeDexRegisterMapSizeOf(const CodeInfoEncoding& encoding, - uint32_t dex_register_map_offset_in_code_info, - uint16_t number_of_dex_registers) const { - // Offset where the actual mapping data starts within art::DexRegisterMap. - size_t location_mapping_data_offset_in_dex_register_map = - DexRegisterMap::GetLocationMappingDataOffset(number_of_dex_registers); - // Create a temporary art::DexRegisterMap to be able to call - // art::DexRegisterMap::GetNumberOfLiveDexRegisters and - DexRegisterMap dex_register_map_without_locations( - MemoryRegion(region_.Subregion(dex_register_map_offset_in_code_info, - location_mapping_data_offset_in_dex_register_map))); - size_t number_of_live_dex_registers = - dex_register_map_without_locations.GetNumberOfLiveDexRegisters(number_of_dex_registers); - size_t location_mapping_data_size_in_bits = - DexRegisterMap::SingleEntrySizeInBits(GetNumberOfLocationCatalogEntries(encoding)) - * number_of_live_dex_registers; - size_t location_mapping_data_size_in_bytes = - RoundUp(location_mapping_data_size_in_bits, kBitsPerByte) / kBitsPerByte; - size_t dex_register_map_size = - location_mapping_data_offset_in_dex_register_map + location_mapping_data_size_in_bytes; - return dex_register_map_size; - } + // Accumulate code info size statistics into the given Stats tree. + void AddSizeStats(/*out*/ Stats* parent) const; - // Compute the size of a Dex register location catalog starting at offset `origin` - // in `region_` and containing `number_of_dex_locations` entries. - size_t ComputeDexRegisterLocationCatalogSize(uint32_t origin, - uint32_t number_of_dex_locations) const { - // TODO: Ideally, we would like to use art::DexRegisterLocationCatalog::Size or - // art::DexRegisterLocationCatalog::FindLocationOffset, but the - // DexRegisterLocationCatalog is not yet built. Try to factor common code. - size_t offset = origin + DexRegisterLocationCatalog::kFixedSize; - - // Skip the first `number_of_dex_locations - 1` entries. - for (uint16_t i = 0; i < number_of_dex_locations; ++i) { - // Read the first next byte and inspect its first 3 bits to decide - // whether it is a short or a large location. - DexRegisterLocationCatalog::ShortLocation first_byte = - region_.LoadUnaligned(offset); - DexRegisterLocation::Kind kind = - DexRegisterLocationCatalog::ExtractKindFromShortLocation(first_byte); - if (DexRegisterLocation::IsShortLocationKind(kind)) { - // Short location. Skip the current byte. - offset += DexRegisterLocationCatalog::SingleShortEntrySize(); - } else { - // Large location. Skip the 5 next bytes. - offset += DexRegisterLocationCatalog::SingleLargeEntrySize(); - } - } - size_t size = offset - origin; - return size; + ALWAYS_INLINE static QuickMethodFrameInfo DecodeFrameInfo(const uint8_t* data) { + return QuickMethodFrameInfo( + DecodeUnsignedLeb128(&data), + DecodeUnsignedLeb128(&data), + DecodeUnsignedLeb128(&data)); } - MemoryRegion region_; - friend class StackMapStream; + private: + // Returns lower bound (fist stack map which has pc greater or equal than the desired one). + // It ignores catch stack maps at the end (it is the same as if they had maximum pc value). + BitTable::const_iterator BinarySearchNativePc(uint32_t packed_pc) const; + + // Scan backward to determine dex register locations at given stack map. + void DecodeDexRegisterMap(uint32_t stack_map_index, + uint32_t first_dex_register, + /*out*/ DexRegisterMap* map) const; + + void Decode(const uint8_t* data); + + uint32_t frame_size_in_bytes_; + uint32_t core_spill_mask_; + uint32_t fp_spill_mask_; + uint32_t number_of_dex_registers_; + BitTable stack_maps_; + BitTable register_masks_; + BitTable stack_masks_; + BitTable inline_infos_; + BitTable dex_register_masks_; + BitTable dex_register_maps_; + BitTable dex_register_catalog_; + uint32_t size_in_bits_; }; #undef ELEMENT_BYTE_OFFSET_AFTER diff --git a/runtime/subtype_check.h b/runtime/subtype_check.h index 3b1d5f8c4a7c529aabb89433bacec3c26c4cb400..aac547eb7851b3e320274a99b3442a1662e5c2a1 100644 --- a/runtime/subtype_check.h +++ b/runtime/subtype_check.h @@ -286,6 +286,17 @@ struct SubtypeCheck { return SubtypeCheckInfo::kUninitialized; } + // Retrieve the state of this class's SubtypeCheckInfo. + // + // Cost: O(Depth(Class)). + // + // Returns: The precise SubtypeCheckInfo::State. + static SubtypeCheckInfo::State GetState(ClassPtr klass) + REQUIRES(Locks::subtype_check_lock_) + REQUIRES_SHARED(Locks::mutator_lock_) { + return GetSubtypeCheckInfo(klass).GetState(); + } + // Retrieve the path to root bitstring as a plain uintN_t value that is amenable to // be used by a fast check "encoded_src & mask_target == encoded_target". // @@ -308,8 +319,9 @@ struct SubtypeCheck { static BitString::StorageType GetEncodedPathToRootForTarget(ClassPtr klass) REQUIRES(Locks::subtype_check_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK_EQ(SubtypeCheckInfo::kAssigned, GetSubtypeCheckInfo(klass).GetState()); - return GetSubtypeCheckInfo(klass).GetEncodedPathToRoot(); + SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass); + DCHECK_EQ(SubtypeCheckInfo::kAssigned, sci.GetState()); + return sci.GetEncodedPathToRoot(); } // Retrieve the path to root bitstring mask as a plain uintN_t value that is amenable to @@ -321,8 +333,9 @@ struct SubtypeCheck { static BitString::StorageType GetEncodedPathToRootMask(ClassPtr klass) REQUIRES(Locks::subtype_check_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK_EQ(SubtypeCheckInfo::kAssigned, GetSubtypeCheckInfo(klass).GetState()); - return GetSubtypeCheckInfo(klass).GetEncodedPathToRootMask(); + SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass); + DCHECK_EQ(SubtypeCheckInfo::kAssigned, sci.GetState()); + return sci.GetEncodedPathToRootMask(); } // Is the source class a subclass of the target? @@ -529,15 +542,17 @@ struct SubtypeCheck { int32_t new_value) REQUIRES_SHARED(Locks::mutator_lock_) { if (Runtime::Current() != nullptr && Runtime::Current()->IsActiveTransaction()) { - return klass->template - CasFieldWeakSequentiallyConsistent32(offset, - old_value, - new_value); + return klass->template CasField32(offset, + old_value, + new_value, + CASMode::kWeak, + std::memory_order_seq_cst); } else { - return klass->template - CasFieldWeakSequentiallyConsistent32(offset, - old_value, - new_value); + return klass->template CasField32(offset, + old_value, + new_value, + CASMode::kWeak, + std::memory_order_seq_cst); } } diff --git a/runtime/subtype_check_info_test.cc b/runtime/subtype_check_info_test.cc index 91fcc07d65b30f8cdaca71946c1a42fac1c6e8f6..e40bca57fed2cebd8e010f3a02a2eed0f3e636d2 100644 --- a/runtime/subtype_check_info_test.cc +++ b/runtime/subtype_check_info_test.cc @@ -121,11 +121,11 @@ struct SubtypeCheckInfoTest : public ::testing::Test { return SubtypeCheckInfo::MakeUnchecked(bs, overflow, depth); } - static bool HasNext(SubtypeCheckInfo io) { + static bool HasNext(const SubtypeCheckInfo& io) { return io.HasNext(); } - static BitString GetPathToRoot(SubtypeCheckInfo io) { + static BitString GetPathToRoot(const SubtypeCheckInfo& io) { return io.GetPathToRoot(); } diff --git a/runtime/subtype_check_test.cc b/runtime/subtype_check_test.cc index e297d0beb42304c3c43f82aa3b0fce7447989b90..666bf812f5e31192489b69000e934a458f72f3d1 100644 --- a/runtime/subtype_check_test.cc +++ b/runtime/subtype_check_test.cc @@ -86,9 +86,11 @@ struct MockClass { } template - bool CasFieldWeakSequentiallyConsistent32(art::MemberOffset offset, - int32_t old_value, - int32_t new_value) + bool CasField32(art::MemberOffset offset, + int32_t old_value, + int32_t new_value, + CASMode mode ATTRIBUTE_UNUSED, + std::memory_order memory_order ATTRIBUTE_UNUSED) REQUIRES_SHARED(Locks::mutator_lock_) { UNUSED(offset); if (old_value == GetField32Volatile(offset)) { @@ -652,13 +654,15 @@ void EnsureStateChangedTestRecursive( MockClass* klass, size_t cur_depth, size_t total_depth, - std::vector> transitions) { + const std::vector>& transitions) { MockScopedLockSubtypeCheck lock_a; MockScopedLockMutator lock_b; using SCTree = MockSubtypeCheck; ASSERT_EQ(cur_depth, klass->Depth()); - ApplyTransition(SCTree::Lookup(klass), transitions[cur_depth].first, transitions[cur_depth].second); + ApplyTransition(SCTree::Lookup(klass), + transitions[cur_depth].first, + transitions[cur_depth].second); if (total_depth == cur_depth + 1) { return; @@ -674,7 +678,7 @@ void EnsureStateChangedTestRecursive( void EnsureStateChangedTest( MockClass* root, size_t depth, - std::vector> transitions) { + const std::vector>& transitions) { ASSERT_EQ(depth, transitions.size()); EnsureStateChangedTestRecursive(root, /*cur_depth*/0u, depth, transitions); diff --git a/runtime/suspend_reason.h b/runtime/suspend_reason.h index 289a1a4fb3eac7937c5f87693a8f7f566fe3518c..4e75a4feec9dc019a8726b19a57a108f8cc529a9 100644 --- a/runtime/suspend_reason.h +++ b/runtime/suspend_reason.h @@ -22,6 +22,8 @@ namespace art { // The various reasons that we might be suspending a thread. +// TODO Once kForDebugger is removed by removing the old debugger we should make the kForUserCode +// just a basic count for bookkeeping instead of linking it as directly with internal suspends. enum class SuspendReason { // Suspending for internal reasons (e.g. GC, stack trace, etc.). // TODO Split this into more descriptive sections. diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index 2f6f50e31eecbdcae37a81acffba1a6710b8b113..91c27af4073cb8c61bbc17a755364e190305e55a 100644 --- a/runtime/thread-inl.h +++ b/runtime/thread-inl.h @@ -23,7 +23,7 @@ #include "base/casts.h" #include "base/mutex-inl.h" #include "base/time_utils.h" -#include "jni_env_ext.h" +#include "jni/jni_env_ext.h" #include "managed_stack-inl.h" #include "obj_ptr.h" #include "thread-current-inl.h" @@ -251,6 +251,7 @@ inline ThreadState Thread::TransitionFromSuspendedToRunnable() { union StateAndFlags new_state_and_flags; new_state_and_flags.as_int = old_state_and_flags.as_int; new_state_and_flags.as_struct.state = kRunnable; + // CAS the value with a memory barrier. if (LIKELY(tls32_.state_and_flags.as_atomic_int.CompareAndSetWeakAcquire( old_state_and_flags.as_int, diff --git a/runtime/thread.cc b/runtime/thread.cc index ea6c071fa7a931023deeaf02ca1f7f3cc8d84eb1..18dc0e8c45e964be4e35bf88f8f8ebccc2c577c0 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -42,11 +42,13 @@ #include "base/file_utils.h" #include "base/memory_tool.h" #include "base/mutex.h" +#include "base/stl_util.h" #include "base/systrace.h" #include "base/timing_logger.h" #include "base/to_str.h" #include "base/utils.h" #include "class_linker-inl.h" +#include "class_root.h" #include "debugger.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" @@ -63,10 +65,10 @@ #include "handle_scope-inl.h" #include "indirect_reference_table-inl.h" #include "interpreter/interpreter.h" -#include "interpreter/shadow_frame.h" +#include "interpreter/shadow_frame-inl.h" #include "java_frame_root_info.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/object_array-inl.h" @@ -392,6 +394,22 @@ ShadowFrame* Thread::FindOrCreateDebuggerShadowFrame(size_t frame_id, return shadow_frame; } +TLSData* Thread::GetCustomTLS(const char* key) { + MutexLock mu(Thread::Current(), *Locks::custom_tls_lock_); + auto it = custom_tls_.find(key); + return (it != custom_tls_.end()) ? it->second.get() : nullptr; +} + +void Thread::SetCustomTLS(const char* key, TLSData* data) { + // We will swap the old data (which might be nullptr) with this and then delete it outside of the + // custom_tls_lock_. + std::unique_ptr old_data(data); + { + MutexLock mu(Thread::Current(), *Locks::custom_tls_lock_); + custom_tls_.GetOrCreate(key, []() { return std::unique_ptr(); }).swap(old_data); + } +} + void Thread::RemoveDebuggerShadowFrameMapping(size_t frame_id) { FrameIdToShadowFrame* head = tlsPtr_.frame_id_to_shadow_frame; if (head->GetFrameId() == frame_id) { @@ -490,7 +508,7 @@ Thread* Thread::FromManagedThread(const ScopedObjectAccessAlreadyRunnable& soa, Thread* Thread::FromManagedThread(const ScopedObjectAccessAlreadyRunnable& soa, jobject java_thread) { - return FromManagedThread(soa, soa.Decode(java_thread).Ptr()); + return FromManagedThread(soa, soa.Decode(java_thread)); } static size_t FixStackSize(size_t stack_size) { @@ -503,6 +521,13 @@ static size_t FixStackSize(size_t stack_size) { // so include that here to support apps that expect large native stacks. stack_size += 1 * MB; + // Under sanitization, frames of the interpreter may become bigger, both for C code as + // well as the ShadowFrame. Ensure a larger minimum size. Otherwise initialization + // of all core classes cannot be done in all test circumstances. + if (kMemoryToolIsAvailable) { + stack_size = std::max(2 * MB, stack_size); + } + // It's not possible to request a stack smaller than the system-defined PTHREAD_STACK_MIN. if (stack_size < PTHREAD_STACK_MIN) { stack_size = PTHREAD_STACK_MIN; @@ -597,7 +622,7 @@ void Thread::InstallImplicitProtection() { 1u; #endif volatile char space[kPageSize - (kAsanMultiplier * 256)]; - char sink ATTRIBUTE_UNUSED = space[zero]; + char sink ATTRIBUTE_UNUSED = space[zero]; // NOLINT if (reinterpret_cast(space) >= target + kPageSize) { Touch(target); } @@ -1115,21 +1140,10 @@ bool Thread::InitStackHwm() { 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. @@ -1214,6 +1228,34 @@ static void UnsafeLogFatalForSuspendCount(Thread* self, Thread* thread) NO_THREA LOG(FATAL) << ss.str(); } +void Thread::SetCanBeSuspendedByUserCode(bool can_be_suspended_by_user_code) { + CHECK_EQ(this, Thread::Current()) << "This function may only be called on the current thread. " + << *Thread::Current() << " tried to modify the suspendability " + << "of " << *this; + // NB This checks the new value! This ensures that we can only set can_be_suspended_by_user_code + // to false if !CanCallIntoJava(). + DCHECK(!CanCallIntoJava() || can_be_suspended_by_user_code) + << "Threads able to call into java may not be marked as unsuspendable!"; + if (can_be_suspended_by_user_code == CanBeSuspendedByUserCode()) { + // Don't need to do anything if nothing is changing. + return; + } + art::MutexLock mu(this, *Locks::user_code_suspension_lock_); + art::MutexLock thread_list_mu(this, *Locks::thread_suspend_count_lock_); + + // We want to add the user-code suspend count if we are newly allowing user-code suspends and + // remove them if we are disabling them. + int adj = can_be_suspended_by_user_code ? GetUserCodeSuspendCount() : -GetUserCodeSuspendCount(); + // Adjust the global suspend count appropriately. Use kInternal to not change the ForUserCode + // count. + if (adj != 0) { + bool suspend = ModifySuspendCountInternal(this, adj, nullptr, SuspendReason::kInternal); + CHECK(suspend) << this << " was unable to modify it's own suspend count!"; + } + // Mark thread as accepting user-code suspensions. + can_be_suspended_by_user_code_ = can_be_suspended_by_user_code; +} + bool Thread::ModifySuspendCountInternal(Thread* self, int delta, AtomicInteger* suspend_barrier, @@ -1235,6 +1277,17 @@ bool Thread::ModifySuspendCountInternal(Thread* self, LOG(ERROR) << "attempting to modify suspend count in an illegal way."; return false; } + DCHECK(this == self || this->IsSuspended()) + << "Only self kForUserCode suspension on an unsuspended thread is allowed: " << this; + if (UNLIKELY(!CanBeSuspendedByUserCode())) { + VLOG(threads) << this << " is being requested to suspend for user code but that is disabled " + << "the thread will not actually go to sleep."; + // Having the user_code_suspend_count still be around is useful but we don't need to actually + // do anything since we aren't going to 'really' suspend. Just adjust the + // user_code_suspend_count and return. + tls32_.user_code_suspend_count += delta; + return true; + } } if (UNLIKELY(delta < 0 && tls32_.suspend_count <= 0)) { UnsafeLogFatalForSuspendCount(self, this); @@ -1280,7 +1333,7 @@ bool Thread::ModifySuspendCountInternal(Thread* self, AtomicClearFlag(kSuspendRequest); } else { // Two bits might be set simultaneously. - tls32_.state_and_flags.as_atomic_int.FetchAndBitwiseOrSequentiallyConsistent(flags); + tls32_.state_and_flags.as_atomic_int.fetch_or(flags, std::memory_order_seq_cst); TriggerSuspend(); } return true; @@ -1318,7 +1371,7 @@ bool Thread::PassActiveSuspendBarriers(Thread* self) { if (pending_threads != nullptr) { bool done = false; do { - int32_t cur_val = pending_threads->LoadRelaxed(); + int32_t cur_val = pending_threads->load(std::memory_order_relaxed); CHECK_GT(cur_val, 0) << "Unexpected value for PassActiveSuspendBarriers(): " << cur_val; // Reduce value by 1. done = pending_threads->CompareAndSetWeakRelaxed(cur_val, cur_val - 1); @@ -1562,7 +1615,7 @@ Closure* Thread::GetFlipFunction() { Atomic* atomic_func = reinterpret_cast*>(&tlsPtr_.flip_function); Closure* func; do { - func = atomic_func->LoadRelaxed(); + func = atomic_func->load(std::memory_order_relaxed); if (func == nullptr) { return nullptr; } @@ -1574,7 +1627,7 @@ Closure* Thread::GetFlipFunction() { void Thread::SetFlipFunction(Closure* function) { CHECK(function != nullptr); Atomic* atomic_func = reinterpret_cast*>(&tlsPtr_.flip_function); - atomic_func->StoreSequentiallyConsistent(function); + atomic_func->store(function, std::memory_order_seq_cst); } void Thread::FullSuspendCheck() { @@ -1582,7 +1635,7 @@ void Thread::FullSuspendCheck() { VLOG(threads) << this << " self-suspending"; // Make thread appear suspended to other threads, release mutator_lock_. // Transition to suspended and back to runnable, re-acquire share on mutator_lock_. - ScopedThreadSuspension(this, kSuspended); + ScopedThreadSuspension(this, kSuspended); // NOLINT VLOG(threads) << this << " self-reviving"; } @@ -2048,15 +2101,15 @@ void Thread::FinishStartup() { // Finish attaching the main thread. ScopedObjectAccess soa(Thread::Current()); - Thread::Current()->CreatePeer("main", false, runtime->GetMainThreadGroup()); - Thread::Current()->AssertNoPendingException(); + soa.Self()->CreatePeer("main", false, runtime->GetMainThreadGroup()); + soa.Self()->AssertNoPendingException(); - Runtime::Current()->GetClassLinker()->RunRootClinits(); + runtime->RunRootClinits(soa.Self()); // The thread counts as started from now on. We need to add it to the ThreadGroup. For regular // threads, this is done in Thread.start() on the Java side. - Thread::Current()->NotifyThreadGroup(soa, runtime->GetMainThreadGroup()); - Thread::Current()->AssertNoPendingException(); + soa.Self()->NotifyThreadGroup(soa, runtime->GetMainThreadGroup()); + soa.Self()->AssertNoPendingException(); } void Thread::Shutdown() { @@ -2095,8 +2148,8 @@ void Thread::NotifyThreadGroup(ScopedObjectAccessAlreadyRunnable& soa, jobject t Thread::Thread(bool daemon) : tls32_(daemon), wait_monitor_(nullptr), - custom_tls_(nullptr), - can_call_into_java_(true) { + can_call_into_java_(true), + can_be_suspended_by_user_code_(true) { wait_mutex_ = new Mutex("a thread wait mutex"); wait_cond_ = new ConditionVariable("a thread wait condition variable", *wait_mutex_); tlsPtr_.instrumentation_stack = new std::deque; @@ -2106,7 +2159,7 @@ Thread::Thread(bool daemon) "art::Thread has a size which is not a multiple of 4."); tls32_.state_and_flags.as_struct.flags = 0; tls32_.state_and_flags.as_struct.state = kNative; - tls32_.interrupted.StoreRelaxed(false); + tls32_.interrupted.store(false, std::memory_order_relaxed); memset(&tlsPtr_.held_mutexes[0], 0, sizeof(tlsPtr_.held_mutexes)); std::fill(tlsPtr_.rosalloc_runs, tlsPtr_.rosalloc_runs + kNumRosAllocThreadLocalSizeBracketsInThread, @@ -2401,24 +2454,24 @@ bool Thread::IsJWeakCleared(jweak obj) const { bool Thread::Interrupted() { DCHECK_EQ(Thread::Current(), this); // No other thread can concurrently reset the interrupted flag. - bool interrupted = tls32_.interrupted.LoadSequentiallyConsistent(); + bool interrupted = tls32_.interrupted.load(std::memory_order_seq_cst); if (interrupted) { - tls32_.interrupted.StoreSequentiallyConsistent(false); + tls32_.interrupted.store(false, std::memory_order_seq_cst); } return interrupted; } // Implements java.lang.Thread.isInterrupted. bool Thread::IsInterrupted() { - return tls32_.interrupted.LoadSequentiallyConsistent(); + return tls32_.interrupted.load(std::memory_order_seq_cst); } void Thread::Interrupt(Thread* self) { MutexLock mu(self, *wait_mutex_); - if (tls32_.interrupted.LoadSequentiallyConsistent()) { + if (tls32_.interrupted.load(std::memory_order_seq_cst)) { return; } - tls32_.interrupted.StoreSequentiallyConsistent(true); + tls32_.interrupted.store(true, std::memory_order_seq_cst); NotifyLocked(self); } @@ -2460,7 +2513,7 @@ class FetchStackTraceVisitor : public StackVisitor { // save frame) ArtMethod* m = GetMethod(); if (skipping_ && !m->IsRuntimeMethod() && - !mirror::Throwable::GetJavaLangThrowable()->IsAssignableFrom(m->GetDeclaringClass())) { + !GetClassRoot()->IsAssignableFrom(m->GetDeclaringClass())) { skipping_ = false; } if (!skipping_) { @@ -2510,7 +2563,8 @@ class BuildInternalStackTraceVisitor : public StackVisitor { // class of the ArtMethod pointers. ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); StackHandleScope<1> hs(self_); - ObjPtr array_class = class_linker->GetClassRoot(ClassLinker::kObjectArrayClass); + ObjPtr array_class = + GetClassRoot>(class_linker); // The first element is the methods and dex pc array, the other elements are declaring classes // for the methods to ensure classes in the stack trace don't get unloaded. Handle> trace( @@ -2726,7 +2780,7 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray( depth = std::min(depth, traces_length); } else { // Create java_trace array and place in local reference table - mirror::ObjectArray* java_traces = + ObjPtr> java_traces = class_linker->AllocStackTraceElementArray(soa.Self(), depth); if (java_traces == nullptr) { return nullptr; @@ -2860,27 +2914,18 @@ jobjectArray Thread::CreateAnnotatedStackTrace(const ScopedObjectAccessAlreadyRu ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); StackHandleScope<6> hs(soa.Self()); - mirror::Class* aste_array_class = class_linker->FindClass( + Handle h_aste_array_class = hs.NewHandle(class_linker->FindSystemClass( soa.Self(), - "[Ldalvik/system/AnnotatedStackTraceElement;", - ScopedNullHandle()); - if (aste_array_class == nullptr) { + "[Ldalvik/system/AnnotatedStackTraceElement;")); + if (h_aste_array_class == nullptr) { return nullptr; } - Handle h_aste_array_class(hs.NewHandle(aste_array_class)); + Handle h_aste_class = hs.NewHandle(h_aste_array_class->GetComponentType()); - mirror::Class* o_array_class = class_linker->FindClass(soa.Self(), - "[Ljava/lang/Object;", - ScopedNullHandle()); - if (o_array_class == nullptr) { - // This should not fail in a healthy runtime. - soa.Self()->AssertPendingException(); - return nullptr; - } - Handle h_o_array_class(hs.NewHandle(o_array_class)); + Handle h_o_array_class = + hs.NewHandle(GetClassRoot>(class_linker)); + DCHECK(h_o_array_class != nullptr); // Class roots must be already initialized. - Handle h_aste_class(hs.NewHandle( - h_aste_array_class->GetComponentType())); // Make sure the AnnotatedStackTraceElement.class is initialized, b/76208924 . class_linker->EnsureInitialized(soa.Self(), @@ -2904,7 +2949,7 @@ jobjectArray Thread::CreateAnnotatedStackTrace(const ScopedObjectAccessAlreadyRu size_t length = dumper.stack_trace_elements_.size(); ObjPtr> array = - mirror::ObjectArray::Alloc(soa.Self(), aste_array_class, length); + mirror::ObjectArray::Alloc(soa.Self(), h_aste_array_class.Get(), length); if (array == nullptr) { soa.Self()->AssertPendingOOMException(); return nullptr; @@ -3021,7 +3066,8 @@ void Thread::ThrowNewWrappedException(const char* exception_class_descriptor, // If we couldn't allocate the exception, throw the pre-allocated out of memory exception. if (exception == nullptr) { - SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryError()); + Dump(LOG_STREAM(WARNING)); // The pre-allocated OOME has no stack, so help out and log one. + SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryErrorWhenThrowingException()); return; } @@ -3101,7 +3147,7 @@ void Thread::ThrowOutOfMemoryError(const char* msg) { tls32_.throwing_OutOfMemoryError = false; } else { Dump(LOG_STREAM(WARNING)); // The pre-allocated OOME has no stack, so help out and log one. - SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryError()); + SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryErrorWhenThrowingOOME()); } } @@ -3170,6 +3216,7 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset) { QUICK_ENTRY_POINT_INFO(pAllocObjectResolved) QUICK_ENTRY_POINT_INFO(pAllocObjectInitialized) QUICK_ENTRY_POINT_INFO(pAllocObjectWithChecks) + QUICK_ENTRY_POINT_INFO(pAllocStringObject) QUICK_ENTRY_POINT_INFO(pAllocStringFromBytes) QUICK_ENTRY_POINT_INFO(pAllocStringFromChars) QUICK_ENTRY_POINT_INFO(pAllocStringFromString) @@ -3378,7 +3425,6 @@ void Thread::QuickDeliverException() { ClearException(); QuickExceptionHandler exception_handler(this, false); exception_handler.FindCatch(exception); - exception_handler.UpdateInstrumentationStack(); if (exception_handler.GetClearException()) { // Exception was cleared as part of delivery. DCHECK(!IsExceptionPending()); @@ -3558,17 +3604,15 @@ class ReferenceMapVisitor : public StackVisitor { StackReference* vreg_base = reinterpret_cast*>( reinterpret_cast(cur_quick_frame)); uintptr_t native_pc_offset = method_header->NativeQuickPcOffset(GetCurrentQuickFramePc()); - CodeInfo code_info = method_header->GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - StackMap map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); + CodeInfo code_info(method_header); + StackMap map = code_info.GetStackMapForNativePcOffset(native_pc_offset); DCHECK(map.IsValid()); - T vreg_info(m, code_info, encoding, map, visitor_); + T vreg_info(m, code_info, map, visitor_); // Visit stack entries that hold pointers. - const size_t number_of_bits = code_info.GetNumberOfStackMaskBits(encoding); - BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, map); - for (size_t i = 0; i < number_of_bits; ++i) { + BitMemoryRegion stack_mask = code_info.GetStackMaskOf(map); + for (size_t i = 0; i < stack_mask.size_in_bits(); ++i) { if (stack_mask.LoadBit(i)) { StackReference* ref_addr = vreg_base + i; mirror::Object* ref = ref_addr->AsMirrorPtr(); @@ -3582,7 +3626,7 @@ class ReferenceMapVisitor : public StackVisitor { } } // Visit callee-save registers that hold pointers. - uint32_t register_mask = code_info.GetRegisterMaskOf(encoding, map); + uint32_t register_mask = code_info.GetRegisterMaskOf(map); for (size_t i = 0; i < BitSizeOf(); ++i) { if (register_mask & (1 << i)) { mirror::Object** ref_addr = reinterpret_cast(GetGPRAddress(i)); @@ -3630,7 +3674,6 @@ class ReferenceMapVisitor : public StackVisitor { struct UndefinedVRegInfo { UndefinedVRegInfo(ArtMethod* method ATTRIBUTE_UNUSED, const CodeInfo& code_info ATTRIBUTE_UNUSED, - const CodeInfoEncoding& encoding ATTRIBUTE_UNUSED, const StackMap& map ATTRIBUTE_UNUSED, RootVisitor& _visitor) : visitor(_visitor) { @@ -3661,15 +3704,11 @@ class ReferenceMapVisitor : public StackVisitor { struct StackMapVRegInfo { StackMapVRegInfo(ArtMethod* method, const CodeInfo& _code_info, - const CodeInfoEncoding& _encoding, const StackMap& map, RootVisitor& _visitor) : number_of_dex_registers(method->DexInstructionData().RegistersSize()), code_info(_code_info), - encoding(_encoding), - dex_register_map(code_info.GetDexRegisterMapOf(map, - encoding, - number_of_dex_registers)), + dex_register_map(code_info.GetDexRegisterMapOf(map)), visitor(_visitor) { } @@ -3682,8 +3721,7 @@ class ReferenceMapVisitor : public StackVisitor { REQUIRES_SHARED(Locks::mutator_lock_) { bool found = false; for (size_t dex_reg = 0; dex_reg != number_of_dex_registers; ++dex_reg) { - DexRegisterLocation location = dex_register_map.GetDexRegisterLocation( - dex_reg, number_of_dex_registers, code_info, encoding); + DexRegisterLocation location = dex_register_map[dex_reg]; if (location.GetKind() == kind && static_cast(location.GetValue()) == index) { visitor(ref, dex_reg, stack_visitor); found = true; @@ -3717,7 +3755,6 @@ class ReferenceMapVisitor : public StackVisitor { size_t number_of_dex_registers; const CodeInfo& code_info; - const CodeInfoEncoding& encoding; DexRegisterMap dex_register_map; RootVisitor& visitor; }; diff --git a/runtime/thread.h b/runtime/thread.h index 9adae96a9ce93149a7c1c32707c41705370be2c7..d169a621982694a16802d66294fa8d747eb93cb3 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -30,11 +30,12 @@ #include "arch/instruction_set.h" #include "base/atomic.h" #include "base/enums.h" +#include "base/globals.h" #include "base/macros.h" #include "base/mutex.h" +#include "base/safe_map.h" #include "entrypoints/jni/jni_entrypoints.h" #include "entrypoints/quick/quick_entrypoints.h" -#include "globals.h" #include "handle_scope.h" #include "instrumentation.h" #include "jvalue.h" @@ -97,6 +98,14 @@ class Thread; class ThreadList; enum VisitRootFlags : uint8_t; +// A piece of data that can be held in the CustomTls. The destructor will be called during thread +// shutdown. The thread the destructor is called on is not necessarily the same thread it was stored +// on. +class TLSData { + public: + virtual ~TLSData() {} +}; + // Thread priorities. These must match the Thread.MIN_PRIORITY, // Thread.NORM_PRIORITY, and Thread.MAX_PRIORITY constants. enum ThreadPriority { @@ -556,7 +565,7 @@ class Thread { bool IsInterrupted(); void Interrupt(Thread* self) REQUIRES(!*wait_mutex_); void SetInterrupted(bool i) { - tls32_.interrupted.StoreSequentiallyConsistent(i); + tls32_.interrupted.store(i, std::memory_order_seq_cst); } void Notify() REQUIRES(!*wait_mutex_); @@ -817,13 +826,17 @@ class Thread { } uint8_t* GetStackEndForInterpreter(bool implicit_overflow_check) const { - if (implicit_overflow_check) { - // The interpreter needs the extra overflow bytes that stack_end does - // not include. - return tlsPtr_.stack_end + GetStackOverflowReservedBytes(kRuntimeISA); - } else { - return tlsPtr_.stack_end; + uint8_t* end = tlsPtr_.stack_end + (implicit_overflow_check + ? GetStackOverflowReservedBytes(kRuntimeISA) + : 0); + if (kIsDebugBuild) { + // In a debuggable build, but especially under ASAN, the access-checks interpreter has a + // potentially humongous stack size. We don't want to take too much of the stack regularly, + // so do not increase the regular reserved size (for compiled code etc) and only report the + // virtually smaller stack to the interpreter here. + end += GetStackOverflowReservedBytes(kRuntimeISA); } + return end; } uint8_t* GetStackEnd() const { @@ -976,6 +989,17 @@ class Thread { --tls32_.disable_thread_flip_count; } + // Returns true if the thread is subject to user_code_suspensions. + bool CanBeSuspendedByUserCode() const { + return can_be_suspended_by_user_code_; + } + + // Sets CanBeSuspenededByUserCode and adjusts the suspend-count as needed. This may only be called + // when running on the current thread. It is **absolutely required** that this be called only on + // the Thread::Current() thread. + void SetCanBeSuspendedByUserCode(bool can_be_suspended_by_user_code) + REQUIRES(!Locks::thread_suspend_count_lock_, !Locks::user_code_suspension_lock_); + // Returns true if the thread is allowed to call into java. bool CanCallIntoJava() const { return can_call_into_java_; @@ -1110,11 +1134,11 @@ class Thread { } void AtomicSetFlag(ThreadFlag flag) { - tls32_.state_and_flags.as_atomic_int.FetchAndBitwiseOrSequentiallyConsistent(flag); + tls32_.state_and_flags.as_atomic_int.fetch_or(flag, std::memory_order_seq_cst); } void AtomicClearFlag(ThreadFlag flag) { - tls32_.state_and_flags.as_atomic_int.FetchAndBitwiseAndSequentiallyConsistent(-1 ^ flag); + tls32_.state_and_flags.as_atomic_int.fetch_and(-1 ^ flag, std::memory_order_seq_cst); } void ResetQuickAllocEntryPointsForThread(bool is_marking); @@ -1244,13 +1268,14 @@ class Thread { return debug_disallow_read_barrier_; } - void* GetCustomTLS() const REQUIRES(Locks::thread_list_lock_) { - return custom_tls_; - } + // Gets the current TLSData associated with the key or nullptr if there isn't any. Note that users + // do not gain ownership of TLSData and must synchronize with SetCustomTls themselves to prevent + // it from being deleted. + TLSData* GetCustomTLS(const char* key) REQUIRES(!Locks::custom_tls_lock_); - void SetCustomTLS(void* data) REQUIRES(Locks::thread_list_lock_) { - custom_tls_ = data; - } + // Sets the tls entry at 'key' to data. The thread takes ownership of the TLSData. The destructor + // will be run when the thread exits or when SetCustomTLS is called again with the same key. + void SetCustomTLS(const char* key, TLSData* data) REQUIRES(!Locks::custom_tls_lock_); // Returns true if the current thread is the jit sensitive thread. bool IsJitSensitiveThread() const { @@ -1538,8 +1563,9 @@ class Thread { // critical section enter. uint32_t disable_thread_flip_count; - // How much of 'suspend_count_' is by request of user code, used to distinguish threads - // suspended by the runtime from those suspended by user code. + // If CanBeSuspendedByUserCode, how much of 'suspend_count_' is by request of user code, used to + // distinguish threads suspended by the runtime from those suspended by user code. Otherwise + // this is just a count of how many user-code suspends have been attempted (but were ignored). // This should have GUARDED_BY(Locks::user_code_suspension_lock_) but auto analysis cannot be // told that AssertHeld should be good enough. int user_code_suspend_count GUARDED_BY(Locks::thread_suspend_count_lock_); @@ -1750,14 +1776,18 @@ class Thread { // Pending extra checkpoints if checkpoint_function_ is already used. std::list checkpoint_overflow_ GUARDED_BY(Locks::thread_suspend_count_lock_); - // Custom TLS field that can be used by plugins. - // TODO: Generalize once we have more plugins. - void* custom_tls_; + // Custom TLS field that can be used by plugins or the runtime. Should not be accessed directly by + // compiled code or entrypoints. + SafeMap> custom_tls_ GUARDED_BY(Locks::custom_tls_lock_); // True if the thread is allowed to call back into java (for e.g. during class resolution). // By default this is true. bool can_call_into_java_; + // True if the thread is subject to user-code suspension. By default this is true. This can only + // be false for threads where '!can_call_into_java_'. + bool can_be_suspended_by_user_code_; + friend class Dbg; // For SetStateUnsafe. friend class gc::collector::SemiSpace; // For getting stack traces. friend class Runtime; // For CreatePeer. diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 8095ef57c701112a638ed3f8110e762781ba85a4..ba333f6dd94af9fc7b75a021b1e230dcf4711987 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -40,7 +40,7 @@ #include "gc/heap.h" #include "gc/reference_processor.h" #include "gc_root.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "lock_word.h" #include "monitor.h" #include "native_stack_dump.h" @@ -732,7 +732,7 @@ void ThreadList::SuspendAllInternal(Thread* self, if (reason == SuspendReason::kForDebugger) { ++debug_suspend_all_count_; } - pending_threads.StoreRelaxed(list_.size() - num_ignored); + pending_threads.store(list_.size() - num_ignored, std::memory_order_relaxed); // Increment everybody's suspend count (except those that should be ignored). for (const auto& thread : list_) { if (thread == ignore1 || thread == ignore2) { @@ -748,7 +748,7 @@ void ThreadList::SuspendAllInternal(Thread* self, if (thread->IsSuspended()) { // Only clear the counter for the current thread. thread->ClearSuspendBarrier(&pending_threads); - pending_threads.FetchAndSubSequentiallyConsistent(1); + pending_threads.fetch_sub(1, std::memory_order_seq_cst); } } } @@ -761,7 +761,7 @@ void ThreadList::SuspendAllInternal(Thread* self, #endif const uint64_t start_time = NanoTime(); while (true) { - int32_t cur_val = pending_threads.LoadRelaxed(); + int32_t cur_val = pending_threads.load(std::memory_order_relaxed); if (LIKELY(cur_val > 0)) { #if ART_USE_FUTEXES if (futex(pending_threads.Address(), FUTEX_WAIT, cur_val, &wait_timeout, nullptr, 0) != 0) { @@ -902,6 +902,8 @@ Thread* ThreadList::SuspendThreadByPeer(jobject peer, bool request_suspension, SuspendReason reason, bool* timed_out) { + CHECK_NE(reason, SuspendReason::kForUserCode) << "Cannot suspend for user-code by peer. Must be " + << "done directly on the thread."; const uint64_t start_time = NanoTime(); useconds_t sleep_us = kThreadSuspendInitialSleepUs; *timed_out = false; diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc index bec11508074f9b4ded3298da07d050ef6393bbf3..26ca19054d8b17007f544a16e12be8cc7624033d 100644 --- a/runtime/thread_pool.cc +++ b/runtime/thread_pool.cc @@ -100,8 +100,13 @@ void* ThreadPoolWorker::Callback(void* arg) { worker->thread_ = Thread::Current(); // Thread pool workers cannot call into java. worker->thread_->SetCanCallIntoJava(false); + // Thread pool workers should not be getting paused by user-code. + worker->thread_->SetCanBeSuspendedByUserCode(false); // Do work until its time to shut down. worker->Run(); + // Thread pool worker is finished. We want to allow suspension during shutdown. + worker->thread_->SetCanBeSuspendedByUserCode(true); + // Thread shuts down. runtime->DetachCurrentThread(); return nullptr; } diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h index a465e110557a00c64b0984800dee73d6b0d5bb47..2784953d69daff334304fb5703278707593d8b7c 100644 --- a/runtime/thread_pool.h +++ b/runtime/thread_pool.h @@ -21,8 +21,8 @@ #include #include "barrier.h" +#include "base/mem_map.h" #include "base/mutex.h" -#include "mem_map.h" namespace art { diff --git a/runtime/thread_pool_test.cc b/runtime/thread_pool_test.cc index 895a108af0f065ede958e35071dcdb02c4a68725..d7842002ee23b5b61a37867f8f4def9ff195f3c0 100644 --- a/runtime/thread_pool_test.cc +++ b/runtime/thread_pool_test.cc @@ -71,7 +71,7 @@ TEST_F(ThreadPoolTest, CheckRun) { // Wait for tasks to complete. thread_pool.Wait(self, true, false); // Make sure that we finished all the work. - EXPECT_EQ(num_tasks, count.LoadSequentiallyConsistent()); + EXPECT_EQ(num_tasks, count.load(std::memory_order_seq_cst)); } TEST_F(ThreadPoolTest, StopStart) { @@ -84,7 +84,7 @@ TEST_F(ThreadPoolTest, StopStart) { } usleep(200); // Check that no threads started prematurely. - EXPECT_EQ(0, count.LoadSequentiallyConsistent()); + EXPECT_EQ(0, count.load(std::memory_order_seq_cst)); // Signal the threads to start processing tasks. thread_pool.StartWorkers(self); usleep(200); @@ -93,7 +93,7 @@ TEST_F(ThreadPoolTest, StopStart) { thread_pool.AddTask(self, new CountTask(&bad_count)); usleep(200); // Ensure that the task added after the workers were stopped doesn't get run. - EXPECT_EQ(0, bad_count.LoadSequentiallyConsistent()); + EXPECT_EQ(0, bad_count.load(std::memory_order_seq_cst)); // Allow tasks to finish up and delete themselves. thread_pool.StartWorkers(self); thread_pool.Wait(self, false, false); @@ -157,7 +157,7 @@ TEST_F(ThreadPoolTest, RecursiveTest) { thread_pool.AddTask(self, new TreeTask(&thread_pool, &count, depth)); thread_pool.StartWorkers(self); thread_pool.Wait(self, true, false); - EXPECT_EQ((1 << depth) - 1, count.LoadSequentiallyConsistent()); + EXPECT_EQ((1 << depth) - 1, count.load(std::memory_order_seq_cst)); } class PeerTask : public Task { diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc index 15c514e5937166b0144fd5325073ea081835d53d..608f0ee13cda7cc00858676bd3c0d91031317163 100644 --- a/runtime/ti/agent.cc +++ b/runtime/ti/agent.cc @@ -21,7 +21,7 @@ #include "nativeloader/native_loader.h" #include "base/strlcpy.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "runtime.h" #include "thread-current-inl.h" #include "scoped_thread_state_change-inl.h" diff --git a/runtime/trace.cc b/runtime/trace.cc index 0f321b6591b2b8a0679f970a2760928588951ab6..1986eec35368e07fb98a1110f00e4269be06ee7a 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -319,8 +319,74 @@ void* Trace::RunSamplingThread(void* arg) { return nullptr; } -void Trace::Start(const char* trace_filename, int trace_fd, size_t buffer_size, int flags, - TraceOutputMode output_mode, TraceMode trace_mode, int interval_us) { +void Trace::Start(const char* trace_filename, + size_t buffer_size, + int flags, + TraceOutputMode output_mode, + TraceMode trace_mode, + int interval_us) { + std::unique_ptr file(OS::CreateEmptyFileWriteOnly(trace_filename)); + if (file == nullptr) { + std::string msg = android::base::StringPrintf("Unable to open trace file '%s'", trace_filename); + PLOG(ERROR) << msg; + ScopedObjectAccess soa(Thread::Current()); + Thread::Current()->ThrowNewException("Ljava/lang/RuntimeException;", msg.c_str()); + return; + } + Start(std::move(file), buffer_size, flags, output_mode, trace_mode, interval_us); +} + +void Trace::Start(int trace_fd, + size_t buffer_size, + int flags, + TraceOutputMode output_mode, + TraceMode trace_mode, + int interval_us) { + if (trace_fd < 0) { + std::string msg = android::base::StringPrintf("Unable to start tracing with invalid fd %d", + trace_fd); + LOG(ERROR) << msg; + ScopedObjectAccess soa(Thread::Current()); + Thread::Current()->ThrowNewException("Ljava/lang/RuntimeException;", msg.c_str()); + return; + } + std::unique_ptr file(new File(trace_fd, "tracefile")); + Start(std::move(file), buffer_size, flags, output_mode, trace_mode, interval_us); +} + +void Trace::StartDDMS(size_t buffer_size, + int flags, + TraceMode trace_mode, + int interval_us) { + Start(std::unique_ptr(), + buffer_size, + flags, + TraceOutputMode::kDDMS, + trace_mode, + interval_us); +} + +void Trace::Start(std::unique_ptr&& trace_file_in, + size_t buffer_size, + int flags, + TraceOutputMode output_mode, + TraceMode trace_mode, + int interval_us) { + // We own trace_file now and are responsible for closing it. To account for error situations, use + // a specialized unique_ptr to ensure we close it on the way out (if it hasn't been passed to a + // Trace instance). + auto deleter = [](File* file) { + if (file != nullptr) { + file->MarkUnchecked(); // Don't deal with flushing requirements. + int result ATTRIBUTE_UNUSED = file->Close(); + delete file; + } + }; + std::unique_ptr trace_file(trace_file_in.release(), deleter); + if (trace_file != nullptr) { + trace_file->DisableAutoClose(); + } + Thread* self = Thread::Current(); { MutexLock mu(self, *Locks::trace_lock_); @@ -338,23 +404,6 @@ void Trace::Start(const char* trace_filename, int trace_fd, size_t buffer_size, return; } - // Open trace file if not going directly to ddms. - std::unique_ptr trace_file; - if (output_mode != TraceOutputMode::kDDMS) { - if (trace_fd < 0) { - trace_file.reset(OS::CreateEmptyFileWriteOnly(trace_filename)); - } else { - trace_file.reset(new File(trace_fd, "tracefile")); - trace_file->DisableAutoClose(); - } - if (trace_file.get() == nullptr) { - PLOG(ERROR) << "Unable to open trace file '" << trace_filename << "'"; - ScopedObjectAccess soa(self); - ThrowRuntimeException("Unable to open trace file '%s'", trace_filename); - return; - } - } - Runtime* runtime = Runtime::Current(); // Enable count of allocs if specified in the flags. @@ -372,8 +421,7 @@ void Trace::Start(const char* trace_filename, int trace_fd, size_t buffer_size, LOG(ERROR) << "Trace already in progress, ignoring this request"; } else { enable_stats = (flags && kTraceCountAllocs) != 0; - the_trace_ = new Trace(trace_file.release(), trace_filename, buffer_size, flags, output_mode, - trace_mode); + the_trace_ = new Trace(trace_file.release(), buffer_size, flags, output_mode, trace_mode); if (trace_mode == TraceMode::kSampling) { CHECK_PTHREAD_CALL(pthread_create, (&sampling_pthread_, nullptr, &RunSamplingThread, reinterpret_cast(interval_us)), @@ -385,7 +433,12 @@ void Trace::Start(const char* trace_filename, int trace_fd, size_t buffer_size, instrumentation::Instrumentation::kMethodExited | instrumentation::Instrumentation::kMethodUnwind); // TODO: In full-PIC mode, we don't need to fully deopt. - runtime->GetInstrumentation()->EnableMethodTracing(kTracerInstrumentationKey); + // TODO: We can only use trampoline entrypoints if we are java-debuggable since in that case + // we know that inlining and other problematic optimizations are disabled. We might just + // want to use the trampolines anyway since it is faster. It makes the story with disabling + // jit-gc more complex though. + runtime->GetInstrumentation()->EnableMethodTracing( + kTracerInstrumentationKey, /*needs_interpreter*/!runtime->IsJavaDebuggable()); } } } @@ -422,24 +475,30 @@ void Trace::StopTracing(bool finish_tracing, bool flush_file) { if (the_trace != nullptr) { stop_alloc_counting = (the_trace->flags_ & Trace::kTraceCountAllocs) != 0; + // Stop the trace sources adding more entries to the trace buffer and synchronise stores. + { + gc::ScopedGCCriticalSection gcs(self, + gc::kGcCauseInstrumentation, + gc::kCollectorTypeInstrumentation); + ScopedSuspendAll ssa(__FUNCTION__); + + if (the_trace->trace_mode_ == TraceMode::kSampling) { + MutexLock mu(self, *Locks::thread_list_lock_); + runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr); + } else { + runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey); + runtime->GetInstrumentation()->RemoveListener( + the_trace, instrumentation::Instrumentation::kMethodEntered | + instrumentation::Instrumentation::kMethodExited | + instrumentation::Instrumentation::kMethodUnwind); + } + } + // At this point, code may read buf_ as it's writers are shutdown + // and the ScopedSuspendAll above has ensured all stores to buf_ + // are now visible. if (finish_tracing) { the_trace->FinishTracing(); } - gc::ScopedGCCriticalSection gcs(self, - gc::kGcCauseInstrumentation, - gc::kCollectorTypeInstrumentation); - ScopedSuspendAll ssa(__FUNCTION__); - - if (the_trace->trace_mode_ == TraceMode::kSampling) { - MutexLock mu(self, *Locks::thread_list_lock_); - runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr); - } else { - runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey); - runtime->GetInstrumentation()->RemoveListener( - the_trace, instrumentation::Instrumentation::kMethodEntered | - instrumentation::Instrumentation::kMethodExited | - instrumentation::Instrumentation::kMethodUnwind); - } if (the_trace->trace_file_.get() != nullptr) { // Do not try to erase, so flush and close explicitly. if (flush_file) { @@ -595,16 +654,21 @@ TracingMode Trace::GetMethodTracingMode() { static constexpr size_t kMinBufSize = 18U; // Trace header is up to 18B. -Trace::Trace(File* trace_file, const char* trace_name, size_t buffer_size, int flags, - TraceOutputMode output_mode, TraceMode trace_mode) +Trace::Trace(File* trace_file, + size_t buffer_size, + int flags, + TraceOutputMode output_mode, + TraceMode trace_mode) : trace_file_(trace_file), buf_(new uint8_t[std::max(kMinBufSize, buffer_size)]()), flags_(flags), trace_output_mode_(output_mode), trace_mode_(trace_mode), clock_source_(default_clock_source_), buffer_size_(std::max(kMinBufSize, buffer_size)), - start_time_(MicroTime()), clock_overhead_ns_(GetClockOverheadNanoSeconds()), cur_offset_(0), + start_time_(MicroTime()), clock_overhead_ns_(GetClockOverheadNanoSeconds()), overflow_(false), interval_us_(0), streaming_lock_(nullptr), unique_methods_lock_(new Mutex("unique methods lock", kTracingUniqueMethodsLock)) { + CHECK(trace_file != nullptr || output_mode == TraceOutputMode::kDDMS); + uint16_t trace_version = GetTraceVersion(clock_source_); if (output_mode == TraceOutputMode::kStreaming) { trace_version |= 0xF0U; @@ -621,11 +685,9 @@ Trace::Trace(File* trace_file, const char* trace_name, size_t buffer_size, int f } static_assert(18 <= kMinBufSize, "Minimum buffer size not large enough for trace header"); - // Update current offset. - cur_offset_.StoreRelaxed(kTraceHeaderLength); + cur_offset_.store(kTraceHeaderLength, std::memory_order_relaxed); if (output_mode == TraceOutputMode::kStreaming) { - streaming_file_name_ = trace_name; streaming_lock_ = new Mutex("tracing lock", LockLevel::kTracingStreamingLock); seen_threads_.reset(new ThreadIDBitSet()); } @@ -659,13 +721,13 @@ void Trace::DumpBuf(uint8_t* buf, size_t buf_size, TraceClockSource clock_source void Trace::FinishTracing() { size_t final_offset = 0; - std::set visited_methods; if (trace_output_mode_ == TraceOutputMode::kStreaming) { // Clean up. + MutexLock mu(Thread::Current(), *streaming_lock_); STLDeleteValues(&seen_methods_); } else { - final_offset = cur_offset_.LoadRelaxed(); + final_offset = cur_offset_.load(std::memory_order_relaxed); GetVisitedMethods(final_offset, &visited_methods); } @@ -707,7 +769,8 @@ void Trace::FinishTracing() { std::string header(os.str()); if (trace_output_mode_ == TraceOutputMode::kStreaming) { - MutexLock mu(Thread::Current(), *streaming_lock_); // To serialize writing. + // Protect access to buf_ and satisfy sanitizer for calls to WriteBuf / FlushBuf. + MutexLock mu(Thread::Current(), *streaming_lock_); // Write a special token to mark the end of trace records and the start of // trace summary. uint8_t buf[7]; @@ -892,7 +955,8 @@ std::string Trace::GetMethodLine(ArtMethod* method) { } void Trace::WriteToBuf(const uint8_t* src, size_t src_size) { - int32_t old_offset = cur_offset_.LoadRelaxed(); + // Updates to cur_offset_ are done under the streaming_lock_ here as in streaming mode. + int32_t old_offset = cur_offset_.load(std::memory_order_relaxed); int32_t new_offset = old_offset + static_cast(src_size); if (dchecked_integral_cast(new_offset) > buffer_size_) { // Flush buffer. @@ -905,46 +969,59 @@ void Trace::WriteToBuf(const uint8_t* src, size_t src_size) { if (!trace_file_->WriteFully(src, src_size)) { PLOG(WARNING) << "Failed streaming a tracing event."; } - cur_offset_.StoreRelease(0); // Buffer is empty now. + cur_offset_.store(0, std::memory_order_relaxed); // Buffer is empty now. return; } old_offset = 0; new_offset = static_cast(src_size); } - cur_offset_.StoreRelease(new_offset); + cur_offset_.store(new_offset, std::memory_order_relaxed); // Fill in data. memcpy(buf_.get() + old_offset, src, src_size); } void Trace::FlushBuf() { - int32_t offset = cur_offset_.LoadRelaxed(); + // Updates to cur_offset_ are done under the streaming_lock_ here as in streaming mode. + int32_t offset = cur_offset_.load(std::memory_order_relaxed); if (!trace_file_->WriteFully(buf_.get(), offset)) { PLOG(WARNING) << "Failed flush the remaining data in streaming."; } - cur_offset_.StoreRelease(0); + cur_offset_.store(0, std::memory_order_relaxed); } void Trace::LogMethodTraceEvent(Thread* thread, ArtMethod* method, instrumentation::Instrumentation::InstrumentationEvent event, uint32_t thread_clock_diff, uint32_t wall_clock_diff) { + // This method is called in both tracing modes (method and + // sampling). In sampling mode, this method is only called by the + // sampling thread. In method tracing mode, it can be called + // concurrently. + // Ensure we always use the non-obsolete version of the method so that entry/exit events have the // same pointer value. method = method->GetNonObsoleteMethod(); + // Advance cur_offset_ atomically. int32_t new_offset; int32_t old_offset = 0; - // We do a busy loop here trying to acquire the next offset. + // In the non-streaming case, we do a busy loop here trying to get + // an offset to write our record and advance cur_offset_ for the + // next use. if (trace_output_mode_ != TraceOutputMode::kStreaming) { + // Although multiple threads can call this method concurrently, + // the compare_exchange_weak here is still atomic (by definition). + // A succeeding update is visible to other cores when they pass + // through this point. + old_offset = cur_offset_.load(std::memory_order_relaxed); // Speculative read do { - old_offset = cur_offset_.LoadRelaxed(); new_offset = old_offset + GetRecordSize(clock_source_); if (static_cast(new_offset) > buffer_size_) { overflow_ = true; return; } - } while (!cur_offset_.CompareAndSetWeakSequentiallyConsistent(old_offset, new_offset)); + } while (!cur_offset_.compare_exchange_weak(old_offset, new_offset, std::memory_order_relaxed)); } TraceAction action = kTraceMethodEnter; @@ -964,7 +1041,14 @@ void Trace::LogMethodTraceEvent(Thread* thread, ArtMethod* method, uint32_t method_value = EncodeTraceMethodAndAction(method, action); - // Write data + // Write data into the tracing buffer (if not streaming) or into a + // small buffer on the stack (if streaming) which we'll put into the + // tracing buffer below. + // + // These writes to the tracing buffer are synchronised with the + // future reads that (only) occur under FinishTracing(). The callers + // of FinishTracing() acquire locks and (implicitly) synchronise + // the buffer memory. uint8_t* ptr; static constexpr size_t kPacketSize = 14U; // The maximum size of data in a packet. uint8_t stack_buf[kPacketSize]; // Space to store a packet when in streaming mode. diff --git a/runtime/trace.h b/runtime/trace.h index 86b8d00d515da1ef0e6b424455770327f83fa99b..1fae250d77033d3aeb083874041979e1d2234565 100644 --- a/runtime/trace.h +++ b/runtime/trace.h @@ -27,12 +27,16 @@ #include #include "base/atomic.h" +#include "base/globals.h" #include "base/macros.h" #include "base/os.h" #include "base/safe_map.h" -#include "globals.h" #include "instrumentation.h" +namespace unix_file { +class FdFile; +} // namespace unix_file + namespace art { class ArtField; @@ -48,9 +52,10 @@ using ThreadIDBitSet = std::bitset; enum TracingMode { kTracingInactive, - kMethodTracingActive, - kSampleProfilingActive, + kMethodTracingActive, // Trace activity synchronous with method progress. + kSampleProfilingActive, // Trace activity captured by sampling thread. }; +std::ostream& operator<<(std::ostream& os, const TracingMode& rhs); // File format: // header @@ -94,6 +99,9 @@ enum TraceAction { kTraceMethodActionMask = 0x03, // two bits }; +// Class for recording event traces. Trace data is either collected +// synchronously during execution (TracingMode::kMethodTracingActive), +// or by a separate sampling thread (TracingMode::kSampleProfilingActive). class Trace FINAL : public instrumentation::InstrumentationListener { public: enum TraceFlag { @@ -115,10 +123,37 @@ class Trace FINAL : public instrumentation::InstrumentationListener { static void SetDefaultClockSource(TraceClockSource clock_source); - static void Start(const char* trace_filename, int trace_fd, size_t buffer_size, int flags, - TraceOutputMode output_mode, TraceMode trace_mode, int interval_us) + static void Start(const char* trace_filename, + size_t buffer_size, + int flags, + TraceOutputMode output_mode, + TraceMode trace_mode, + int interval_us) + REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_, + !Locks::trace_lock_); + static void Start(int trace_fd, + size_t buffer_size, + int flags, + TraceOutputMode output_mode, + TraceMode trace_mode, + int interval_us) + REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_, + !Locks::trace_lock_); + static void Start(std::unique_ptr&& file, + size_t buffer_size, + int flags, + TraceOutputMode output_mode, + TraceMode trace_mode, + int interval_us) + REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_, + !Locks::trace_lock_); + static void StartDDMS(size_t buffer_size, + int flags, + TraceMode trace_mode, + int interval_us) REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_, !Locks::trace_lock_); + static void Pause() REQUIRES(!Locks::trace_lock_, !Locks::thread_list_lock_); static void Resume() REQUIRES(!Locks::trace_lock_); @@ -212,8 +247,11 @@ class Trace FINAL : public instrumentation::InstrumentationListener { static bool IsTracingEnabled() REQUIRES(!Locks::trace_lock_); private: - Trace(File* trace_file, const char* trace_name, size_t buffer_size, int flags, - TraceOutputMode output_mode, TraceMode trace_mode); + Trace(File* trace_file, + size_t buffer_size, + int flags, + TraceOutputMode output_mode, + TraceMode trace_mode); // The sampling interval in microseconds is passed as an argument. static void* RunSamplingThread(void* arg) REQUIRES(!Locks::trace_lock_); @@ -282,7 +320,10 @@ class Trace FINAL : public instrumentation::InstrumentationListener { // File to write trace data out to, null if direct to ddms. std::unique_ptr trace_file_; - // Buffer to store trace data. + // Buffer to store trace data. In streaming mode, this is protected + // by the streaming_lock_. In non-streaming mode, reserved regions + // are atomically allocated (using cur_offset_) for log entries to + // be written. std::unique_ptr buf_; // Flags enabling extra tracing of things such as alloc counts. @@ -305,7 +346,27 @@ class Trace FINAL : public instrumentation::InstrumentationListener { // Clock overhead. const uint32_t clock_overhead_ns_; - // Offset into buf_. + // Offset into buf_. The field is atomic to allow multiple writers + // to concurrently reserve space in the buffer. The newly written + // buffer contents are not read without some other form of thread + // synchronization, such as suspending all potential writers or + // acquiring *streaming_lock_. Reading cur_offset_ is thus never + // used to ensure visibility of any other objects, and all accesses + // are memory_order_relaxed. + // + // All accesses to buf_ in streaming mode occur whilst holding the + // streaming lock. In streaming mode, the buffer may be written out + // so cur_offset_ can move forwards and backwards. + // + // When not in streaming mode, the buf_ writes can come from + // multiple threads when the trace mode is kMethodTracing. When + // trace mode is kSampling, writes only come from the sampling + // thread. + // + // Reads to the buffer happen after the event sources writing to the + // buffer have been shutdown and all stores have completed. The + // stores are made visible in StopTracing() when execution leaves + // the ScopedSuspendAll block. AtomicInteger cur_offset_; // Did we overflow the buffer recording traces? @@ -318,10 +379,9 @@ class Trace FINAL : public instrumentation::InstrumentationListener { int interval_us_; // Streaming mode data. - std::string streaming_file_name_; Mutex* streaming_lock_; - std::map seen_methods_; - std::unique_ptr seen_threads_; + std::map seen_methods_ GUARDED_BY(streaming_lock_); + std::unique_ptr seen_threads_ GUARDED_BY(streaming_lock_); // Bijective map from ArtMethod* to index. // Map from ArtMethod* to index in unique_methods_; diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc index 02e61d76a12219e771b4316f253b8cad2d909940..370a61982015f0b9c201bb3de477747cb206940a 100644 --- a/runtime/transaction_test.cc +++ b/runtime/transaction_test.cc @@ -506,7 +506,7 @@ TEST_F(TransactionTest, ResolveString) { class_linker_->LookupString(string_idx, h_dex_cache.Get()); ASSERT_TRUE(s != nullptr); EXPECT_STREQ(s->ToModifiedUtf8().c_str(), kResolvedString); - EXPECT_EQ(s.Ptr(), h_dex_cache->GetResolvedString(string_idx)); + EXPECT_OBJ_PTR_EQ(s, h_dex_cache->GetResolvedString(string_idx)); } Runtime::Current()->RollbackAndExitTransactionMode(); // Check that the string did not stay resolved. diff --git a/runtime/type_lookup_table.cc b/runtime/type_lookup_table.cc deleted file mode 100644 index 7e204fc03a5ee02ad04fe3739ac622d5cea367c9..0000000000000000000000000000000000000000 --- a/runtime/type_lookup_table.cc +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "type_lookup_table.h" - -#include -#include - -#include "base/bit_utils.h" -#include "base/utils.h" -#include "dex/dex_file-inl.h" -#include "dex/utf-inl.h" - -namespace art { - -static uint16_t MakeData(uint16_t class_def_idx, uint32_t hash, uint32_t mask) { - uint16_t hash_mask = static_cast(~mask); - return (static_cast(hash) & hash_mask) | class_def_idx; -} - -TypeLookupTable::~TypeLookupTable() { - if (!owns_entries_) { - // We don't actually own the entries, don't let the unique_ptr release them. - entries_.release(); - } -} - -uint32_t TypeLookupTable::RawDataLength(uint32_t num_class_defs) { - return SupportedSize(num_class_defs) ? RoundUpToPowerOfTwo(num_class_defs) * sizeof(Entry) : 0u; -} - -uint32_t TypeLookupTable::CalculateMask(uint32_t num_class_defs) { - return SupportedSize(num_class_defs) ? RoundUpToPowerOfTwo(num_class_defs) - 1u : 0u; -} - -bool TypeLookupTable::SupportedSize(uint32_t num_class_defs) { - return num_class_defs != 0u && num_class_defs <= std::numeric_limits::max(); -} - -std::unique_ptr TypeLookupTable::Create(const DexFile& dex_file, - uint8_t* storage) { - const uint32_t num_class_defs = dex_file.NumClassDefs(); - return std::unique_ptr(SupportedSize(num_class_defs) - ? new TypeLookupTable(dex_file, storage) - : nullptr); -} - -std::unique_ptr TypeLookupTable::Open(const uint8_t* dex_file_pointer, - const uint8_t* raw_data, - uint32_t num_class_defs) { - return std::unique_ptr( - new TypeLookupTable(dex_file_pointer, raw_data, num_class_defs)); -} - -TypeLookupTable::TypeLookupTable(const DexFile& dex_file, uint8_t* storage) - : dex_data_begin_(dex_file.DataBegin()), - raw_data_length_(RawDataLength(dex_file.NumClassDefs())), - mask_(CalculateMask(dex_file.NumClassDefs())), - entries_(storage != nullptr ? reinterpret_cast(storage) : new Entry[mask_ + 1]), - owns_entries_(storage == nullptr) { - static_assert(alignof(Entry) == 4u, "Expecting Entry to be 4-byte aligned."); - DCHECK_ALIGNED(storage, alignof(Entry)); - std::vector conflict_class_defs; - // The first stage. Put elements on their initial positions. If an initial position is already - // occupied then delay the insertion of the element to the second stage to reduce probing - // distance. - for (size_t i = 0; i < dex_file.NumClassDefs(); ++i) { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(i); - const DexFile::TypeId& type_id = dex_file.GetTypeId(class_def.class_idx_); - const DexFile::StringId& str_id = dex_file.GetStringId(type_id.descriptor_idx_); - const uint32_t hash = ComputeModifiedUtf8Hash(dex_file.GetStringData(str_id)); - Entry entry; - entry.str_offset = str_id.string_data_off_; - entry.data = MakeData(i, hash, GetSizeMask()); - if (!SetOnInitialPos(entry, hash)) { - conflict_class_defs.push_back(i); - } - } - // The second stage. The initial position of these elements had a collision. Put these elements - // into the nearest free cells and link them together by updating next_pos_delta. - for (uint16_t class_def_idx : conflict_class_defs) { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx); - const DexFile::TypeId& type_id = dex_file.GetTypeId(class_def.class_idx_); - const DexFile::StringId& str_id = dex_file.GetStringId(type_id.descriptor_idx_); - const uint32_t hash = ComputeModifiedUtf8Hash(dex_file.GetStringData(str_id)); - Entry entry; - entry.str_offset = str_id.string_data_off_; - entry.data = MakeData(class_def_idx, hash, GetSizeMask()); - Insert(entry, hash); - } -} - -TypeLookupTable::TypeLookupTable(const uint8_t* dex_file_pointer, - const uint8_t* raw_data, - uint32_t num_class_defs) - : dex_data_begin_(dex_file_pointer), - raw_data_length_(RawDataLength(num_class_defs)), - mask_(CalculateMask(num_class_defs)), - entries_(reinterpret_cast(const_cast(raw_data))), - owns_entries_(false) {} - -bool TypeLookupTable::SetOnInitialPos(const Entry& entry, uint32_t hash) { - const uint32_t pos = hash & GetSizeMask(); - if (!entries_[pos].IsEmpty()) { - return false; - } - entries_[pos] = entry; - entries_[pos].next_pos_delta = 0; - return true; -} - -void TypeLookupTable::Insert(const Entry& entry, uint32_t hash) { - uint32_t pos = FindLastEntryInBucket(hash & GetSizeMask()); - uint32_t next_pos = (pos + 1) & GetSizeMask(); - while (!entries_[next_pos].IsEmpty()) { - next_pos = (next_pos + 1) & GetSizeMask(); - } - const uint32_t delta = (next_pos >= pos) ? (next_pos - pos) : (next_pos + Size() - pos); - entries_[pos].next_pos_delta = delta; - entries_[next_pos] = entry; - entries_[next_pos].next_pos_delta = 0; -} - -uint32_t TypeLookupTable::FindLastEntryInBucket(uint32_t pos) const { - const Entry* entry = &entries_[pos]; - while (!entry->IsLast()) { - pos = (pos + entry->next_pos_delta) & GetSizeMask(); - entry = &entries_[pos]; - } - return pos; -} - -} // namespace art diff --git a/runtime/type_lookup_table.h b/runtime/type_lookup_table.h deleted file mode 100644 index 3352d60ed16a32b197a81d2f32e8af7c04a6a588..0000000000000000000000000000000000000000 --- a/runtime/type_lookup_table.h +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_TYPE_LOOKUP_TABLE_H_ -#define ART_RUNTIME_TYPE_LOOKUP_TABLE_H_ - -#include "base/leb128.h" -#include "dex/dex_file_types.h" -#include "dex/utf.h" - -namespace art { - -class DexFile; - -/** - * TypeLookupTable used to find class_def_idx by class descriptor quickly. - * Implementation of TypeLookupTable is based on hash table. - * This class instantiated at compile time by calling Create() method and written into OAT file. - * At runtime, the raw data is read from memory-mapped file by calling Open() method. The table - * memory remains clean. - */ -class TypeLookupTable { - public: - ~TypeLookupTable(); - - // Return the number of buckets in the lookup table. - uint32_t Size() const { - return mask_ + 1; - } - - // Method search class_def_idx by class descriptor and it's hash. - // If no data found then the method returns dex::kDexNoIndex. - uint32_t Lookup(const char* str, uint32_t hash) const { - uint32_t pos = hash & GetSizeMask(); - // Thanks to special insertion algorithm, the element at position pos can be empty or start of - // bucket. - const Entry* entry = &entries_[pos]; - while (!entry->IsEmpty()) { - if (CmpHashBits(entry->data, hash) && IsStringsEquals(str, entry->str_offset)) { - return GetClassDefIdx(entry->data); - } - if (entry->IsLast()) { - return dex::kDexNoIndex; - } - pos = (pos + entry->next_pos_delta) & GetSizeMask(); - entry = &entries_[pos]; - } - return dex::kDexNoIndex; - } - - // Method creates lookup table for dex file - static std::unique_ptr Create(const DexFile& dex_file, - uint8_t* storage = nullptr); - - // Method opens lookup table from binary data. Lookups will traverse strings and other - // data contained in dex_file as well. Lookup table does not own raw_data or dex_file. - static std::unique_ptr Open(const uint8_t* dex_file_pointer, - const uint8_t* raw_data, - uint32_t num_class_defs); - - // Method returns pointer to binary data of lookup table. Used by the oat writer. - const uint8_t* RawData() const { - return reinterpret_cast(entries_.get()); - } - - // Method returns length of binary data. Used by the oat writer. - uint32_t RawDataLength() const { return raw_data_length_; } - - // Method returns length of binary data for the specified number of class definitions. - static uint32_t RawDataLength(uint32_t num_class_defs); - - private: - /** - * To find element we need to compare strings. - * It is faster to compare first hashes and then strings itself. - * But we have no full hash of element of table. But we can use 2 ideas. - * 1. All minor bits of hash inside one bucket are equals. - * 2. If dex file contains N classes and size of hash table is 2^n (where N <= 2^n) - * then 16-n bits are free. So we can encode part of element's hash into these bits. - * So hash of element can be divided on three parts: - * XXXX XXXX XXXX YYYY YZZZ ZZZZ ZZZZZ - * Z - a part of hash encoded in bucket (these bits of has are same for all elements in bucket) - - * n bits - * Y - a part of hash that we can write into free 16-n bits (because only n bits used to store - * class_def_idx) - * X - a part of has that we can't use without increasing increase - * So the data element of Entry used to store class_def_idx and part of hash of the entry. - */ - struct Entry { - uint32_t str_offset; - uint16_t data; - uint16_t next_pos_delta; - - Entry() : str_offset(0), data(0), next_pos_delta(0) {} - - bool IsEmpty() const { - return str_offset == 0; - } - - bool IsLast() const { - return next_pos_delta == 0; - } - }; - - static uint32_t CalculateMask(uint32_t num_class_defs); - static bool SupportedSize(uint32_t num_class_defs); - - // Construct from a dex file. - explicit TypeLookupTable(const DexFile& dex_file, uint8_t* storage); - - // Construct from a dex file with existing data. - TypeLookupTable(const uint8_t* dex_file_pointer, - const uint8_t* raw_data, - uint32_t num_class_defs); - - bool IsStringsEquals(const char* str, uint32_t str_offset) const { - const uint8_t* ptr = dex_data_begin_ + str_offset; - CHECK(dex_data_begin_ != nullptr); - // Skip string length. - DecodeUnsignedLeb128(&ptr); - return CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues( - str, reinterpret_cast(ptr)) == 0; - } - - // Method extracts hash bits from element's data and compare them with - // the corresponding bits of the specified hash - bool CmpHashBits(uint32_t data, uint32_t hash) const { - uint32_t mask = static_cast(~GetSizeMask()); - return (hash & mask) == (data & mask); - } - - uint32_t GetClassDefIdx(uint32_t data) const { - return data & mask_; - } - - uint32_t GetSizeMask() const { - return mask_; - } - - // Attempt to set an entry on its hash's slot. If there is already something there, return false. - // Otherwise return true. - bool SetOnInitialPos(const Entry& entry, uint32_t hash); - - // Insert an entry, probes until there is an empty slot. - void Insert(const Entry& entry, uint32_t hash); - - // Find the last entry in a chain. - uint32_t FindLastEntryInBucket(uint32_t cur_pos) const; - - const uint8_t* dex_data_begin_; - const uint32_t raw_data_length_; - const uint32_t mask_; - std::unique_ptr entries_; - // owns_entries_ specifies if the lookup table owns the entries_ array. - const bool owns_entries_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(TypeLookupTable); -}; - -} // namespace art - -#endif // ART_RUNTIME_TYPE_LOOKUP_TABLE_H_ diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h index 68a5760f7eb5f3bf3f646e14728ab0fbc570eebb..c0ea6be5a336e68c414b8044a1a37da9f73c759f 100644 --- a/runtime/utils/dex_cache_arrays_layout-inl.h +++ b/runtime/utils/dex_cache_arrays_layout-inl.h @@ -22,9 +22,9 @@ #include #include "base/bit_utils.h" +#include "base/globals.h" #include "dex/primitive.h" #include "gc_root.h" -#include "globals.h" #include "mirror/dex_cache.h" namespace art { diff --git a/runtime/var_handles.cc b/runtime/var_handles.cc new file mode 100644 index 0000000000000000000000000000000000000000..f08742fcd75578f5cc5639a16c80291c3d6615d9 --- /dev/null +++ b/runtime/var_handles.cc @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "var_handles.h" + +#include "common_throws.h" +#include "dex/dex_instruction.h" +#include "handle.h" +#include "method_handles-inl.h" +#include "mirror/method_type.h" +#include "mirror/var_handle.h" + +namespace art { + +namespace { + +bool VarHandleInvokeAccessorWithConversions(Thread* self, + ShadowFrame& shadow_frame, + Handle var_handle, + Handle callsite_type, + const mirror::VarHandle::AccessMode access_mode, + const InstructionOperands* const operands, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + StackHandleScope<1> hs(self); + Handle accessor_type(hs.NewHandle( + var_handle->GetMethodTypeForAccessMode(self, access_mode))); + const size_t num_vregs = accessor_type->NumberOfVRegs(); + const int num_params = accessor_type->GetPTypes()->GetLength(); + ShadowFrameAllocaUniquePtr accessor_frame = + CREATE_SHADOW_FRAME(num_vregs, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC()); + ShadowFrameGetter getter(shadow_frame, operands); + static const uint32_t kFirstDestinationReg = 0; + ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg); + if (!PerformConversions(self, callsite_type, accessor_type, &getter, &setter, num_params)) { + return false; + } + RangeInstructionOperands accessor_operands(kFirstDestinationReg, + kFirstDestinationReg + num_vregs); + if (!var_handle->Access(access_mode, accessor_frame.get(), &accessor_operands, result)) { + return false; + } + return ConvertReturnValue(callsite_type, accessor_type, result); +} + +} // namespace + +bool VarHandleInvokeAccessor(Thread* self, + ShadowFrame& shadow_frame, + Handle var_handle, + Handle callsite_type, + const mirror::VarHandle::AccessMode access_mode, + const InstructionOperands* const operands, + JValue* result) { + if (var_handle.IsNull()) { + ThrowNullPointerExceptionFromDexPC(); + return false; + } + + if (!var_handle->IsAccessModeSupported(access_mode)) { + ThrowUnsupportedOperationException(); + return false; + } + + mirror::VarHandle::MatchKind match_kind = + var_handle->GetMethodTypeMatchForAccessMode(access_mode, callsite_type.Get()); + if (LIKELY(match_kind == mirror::VarHandle::MatchKind::kExact)) { + return var_handle->Access(access_mode, &shadow_frame, operands, result); + } else if (match_kind == mirror::VarHandle::MatchKind::kWithConversions) { + return VarHandleInvokeAccessorWithConversions(self, + shadow_frame, + var_handle, + callsite_type, + access_mode, + operands, + result); + } else { + DCHECK_EQ(match_kind, mirror::VarHandle::MatchKind::kNone); + ThrowWrongMethodTypeException(var_handle->PrettyDescriptorForAccessMode(access_mode), + callsite_type->PrettyDescriptor()); + return false; + } +} + +} // namespace art diff --git a/runtime/var_handles.h b/runtime/var_handles.h new file mode 100644 index 0000000000000000000000000000000000000000..2ff8405f035330c0c94fadef72254b556d0142b9 --- /dev/null +++ b/runtime/var_handles.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_VAR_HANDLES_H_ +#define ART_RUNTIME_VAR_HANDLES_H_ + +#include "mirror/var_handle.h" + +namespace art { + +bool VarHandleInvokeAccessor(Thread* self, + ShadowFrame& shadow_frame, + Handle var_handle, + Handle callsite_type, + const mirror::VarHandle::AccessMode access_mode, + const InstructionOperands* const operands, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_); + +} // namespace art + +#endif // ART_RUNTIME_VAR_HANDLES_H_ diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index 838d7f14bcaf319824e1c060072c8ac7027188a6..32aa86dc93fdc16fcd4ae4789149c21032977e55 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -28,6 +28,7 @@ #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "dex/art_dex_file_loader.h" +#include "dex/class_accessor-inl.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "dex/hidden_api_access_flags.h" @@ -283,30 +284,25 @@ void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, std::unordered_set unquickened_code_item; CompactOffsetTable::Accessor accessor(GetQuickenInfoOffsetTable(source_dex_begin, quickening_info)); - for (uint32_t i = 0; i < target_dex_file.NumClassDefs(); ++i) { - const DexFile::ClassDef& class_def = target_dex_file.GetClassDef(i); - const uint8_t* class_data = target_dex_file.GetClassData(class_def); - if (class_data != nullptr) { - for (ClassDataItemIterator class_it(target_dex_file, class_data); - class_it.HasNext(); - class_it.Next()) { - if (class_it.IsAtMethod()) { - const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem(); - if (code_item != nullptr && unquickened_code_item.emplace(code_item).second) { - const uint32_t offset = accessor.GetOffset(class_it.GetMemberIndex()); - // Offset being 0 means not quickened. - if (offset != 0u) { - ArrayRef quicken_data = GetQuickeningInfoAt(quickening_info, offset); - optimizer::ArtDecompileDEX( - target_dex_file, - *code_item, - quicken_data, - decompile_return_instruction); - } - } + for (ClassAccessor class_accessor : target_dex_file.GetClasses()) { + for (const ClassAccessor::Method& method : class_accessor.GetMethods()) { + const DexFile::CodeItem* code_item = method.GetCodeItem(); + if (code_item != nullptr && unquickened_code_item.emplace(code_item).second) { + const uint32_t offset = accessor.GetOffset(method.GetIndex()); + // Offset being 0 means not quickened. + if (offset != 0u) { + ArrayRef quicken_data = GetQuickeningInfoAt(quickening_info, offset); + optimizer::ArtDecompileDEX( + target_dex_file, + *code_item, + quicken_data, + decompile_return_instruction); } - DexFile::UnHideAccessFlags(class_it); } + method.UnHideAccessFlags(); + } + for (const ClassAccessor::Field& field : class_accessor.GetFields()) { + field.UnHideAccessFlags(); } } } diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index 326fcbc1fe4210e51f0a9c56301b89722dde3f9a..b7f28f0d03dc77d09a074b152215f304175d8406 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -22,9 +22,9 @@ #include "base/array_ref.h" #include "base/macros.h" +#include "base/mem_map.h" #include "base/os.h" #include "dex/compact_offset_table.h" -#include "mem_map.h" #include "quicken_info.h" namespace art { diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index dfb98b60836b40cfcb900ef314852bceab885b28..47877bd195f881891032568ff7f3556ba0acb201 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -25,6 +25,7 @@ #include "base/aborting.h" #include "base/enums.h" #include "base/leb128.h" +#include "base/indenter.h" #include "base/logging.h" // For VLOG. #include "base/mutex-inl.h" #include "base/stl_util.h" @@ -32,7 +33,9 @@ #include "base/time_utils.h" #include "base/utils.h" #include "class_linker.h" +#include "class_root.h" #include "compiler_callbacks.h" +#include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_exception_helpers.h" @@ -41,7 +44,6 @@ #include "experimental_flags.h" #include "gc/accounting/card_table-inl.h" #include "handle_scope-inl.h" -#include "indenter.h" #include "intern_table.h" #include "mirror/class-inl.h" #include "mirror/class.h" @@ -143,7 +145,7 @@ static void SafelyMarkAllRegistersAsConflicts(MethodVerifier* verifier, Register } FailureKind MethodVerifier::VerifyClass(Thread* self, - mirror::Class* klass, + ObjPtr klass, CompilerCallbacks* callbacks, bool allow_soft_failures, HardFailLogMode log_level, @@ -189,11 +191,6 @@ FailureKind MethodVerifier::VerifyClass(Thread* self, error); } -template -static bool HasNextMethod(ClassDataItemIterator* it) { - return kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod(); -} - static FailureKind FailureKindMax(FailureKind fk1, FailureKind fk2) { static_assert(FailureKind::kNoFailure < FailureKind::kSoftFailure && FailureKind::kSoftFailure < FailureKind::kHardFailure, @@ -206,45 +203,50 @@ void MethodVerifier::FailureData::Merge(const MethodVerifier::FailureData& fd) { types |= fd.types; } -template -MethodVerifier::FailureData MethodVerifier::VerifyMethods(Thread* self, - ClassLinker* linker, - const DexFile* dex_file, - const DexFile::ClassDef& class_def, - ClassDataItemIterator* it, - Handle dex_cache, - Handle class_loader, - CompilerCallbacks* callbacks, - bool allow_soft_failures, - HardFailLogMode log_level, - bool need_precise_constants, - std::string* error_string) { - DCHECK(it != nullptr); +FailureKind MethodVerifier::VerifyClass(Thread* self, + const DexFile* dex_file, + Handle dex_cache, + Handle class_loader, + const DexFile::ClassDef& class_def, + CompilerCallbacks* callbacks, + bool allow_soft_failures, + HardFailLogMode log_level, + std::string* error) { + // A class must not be abstract and final. + if ((class_def.access_flags_ & (kAccAbstract | kAccFinal)) == (kAccAbstract | kAccFinal)) { + *error = "Verifier rejected class "; + *error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); + *error += ": class is abstract and final."; + return FailureKind::kHardFailure; + } + + ClassAccessor accessor(*dex_file, class_def); + SCOPED_TRACE << "VerifyClass " << PrettyDescriptor(accessor.GetDescriptor()); + int64_t previous_method_idx[2] = { -1, -1 }; MethodVerifier::FailureData failure_data; + ClassLinker* const linker = Runtime::Current()->GetClassLinker(); - int64_t previous_method_idx = -1; - while (HasNextMethod(it)) { + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + int64_t* previous_idx = &previous_method_idx[method.IsStaticOrDirect() ? 0u : 1u]; self->AllowThreadSuspension(); - uint32_t method_idx = it->GetMemberIndex(); - if (method_idx == previous_method_idx) { + const uint32_t method_idx = method.GetIndex(); + if (method_idx == *previous_idx) { // smali can create dex files with two encoded_methods sharing the same method_idx // http://code.google.com/p/smali/issues/detail?id=119 - it->Next(); continue; } - previous_method_idx = method_idx; - InvokeType type = it->GetMethodInvokeType(class_def); - ArtMethod* method = linker->ResolveMethod( + *previous_idx = method_idx; + const InvokeType type = method.GetInvokeType(class_def.access_flags_); + ArtMethod* resolved_method = linker->ResolveMethod( method_idx, dex_cache, class_loader, /* referrer */ nullptr, type); - if (method == nullptr) { + if (resolved_method == nullptr) { DCHECK(self->IsExceptionPending()); // We couldn't resolve the method, but continue regardless. self->ClearException(); } else { - DCHECK(method->GetDeclaringClassUnchecked() != nullptr) << type; + DCHECK(resolved_method->GetDeclaringClassUnchecked() != nullptr) << type; } - StackHandleScope<1> hs(self); std::string hard_failure_msg; MethodVerifier::FailureData result = VerifyMethod(self, method_idx, @@ -252,99 +254,39 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethods(Thread* self, dex_cache, class_loader, class_def, - it->GetMethodCodeItem(), - method, - it->GetMethodAccessFlags(), + method.GetCodeItem(), + resolved_method, + method.GetAccessFlags(), callbacks, allow_soft_failures, log_level, - need_precise_constants, + /*need_precise_constants*/ false, &hard_failure_msg); if (result.kind == FailureKind::kHardFailure) { if (failure_data.kind == FailureKind::kHardFailure) { // If we logged an error before, we need a newline. - *error_string += "\n"; + *error += "\n"; } else { // If we didn't log a hard failure before, print the header of the message. - *error_string += "Verifier rejected class "; - *error_string += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); - *error_string += ":"; + *error += "Verifier rejected class "; + *error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); + *error += ":"; } - *error_string += " "; - *error_string += hard_failure_msg; + *error += " "; + *error += hard_failure_msg; } failure_data.Merge(result); - it->Next(); - } - - return failure_data; -} - -FailureKind MethodVerifier::VerifyClass(Thread* self, - const DexFile* dex_file, - Handle dex_cache, - Handle class_loader, - const DexFile::ClassDef& class_def, - CompilerCallbacks* callbacks, - 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 "; - *error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); - *error += ": class is abstract and final."; - return FailureKind::kHardFailure; } - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - // empty class, probably a marker interface - return FailureKind::kNoFailure; - } - ClassDataItemIterator it(*dex_file, class_data); - it.SkipAllFields(); - ClassLinker* linker = Runtime::Current()->GetClassLinker(); - // Direct methods. - MethodVerifier::FailureData data1 = VerifyMethods(self, - linker, - dex_file, - class_def, - &it, - dex_cache, - class_loader, - callbacks, - allow_soft_failures, - log_level, - false /* need precise constants */, - error); - // Virtual methods. - MethodVerifier::FailureData data2 = VerifyMethods(self, - linker, - dex_file, - class_def, - &it, - dex_cache, - class_loader, - callbacks, - allow_soft_failures, - log_level, - false /* need precise constants */, - error); - - data1.Merge(data2); - - if (data1.kind == FailureKind::kNoFailure) { + if (failure_data.kind == FailureKind::kNoFailure) { return FailureKind::kNoFailure; } else { - if ((data1.types & VERIFY_ERROR_LOCKING) != 0) { + if ((failure_data.types & VERIFY_ERROR_LOCKING) != 0) { // Print a warning about expected slow-down. Use a string temporary to print one contiguous // warning. std::string tmp = StringPrintf("Class %s failed lock verification and will run slower.", - PrettyDescriptor(dex_file->GetClassDescriptor(class_def)).c_str()); + PrettyDescriptor(accessor.GetDescriptor()).c_str()); if (!gPrintedDxMonitorText) { tmp = tmp + "\nCommon causes for lock verification issues are non-optimized dex code\n" "and incorrect proguard optimizations."; @@ -352,7 +294,7 @@ FailureKind MethodVerifier::VerifyClass(Thread* self, } LOG(WARNING) << tmp; } - return data1.kind; + return failure_data.kind; } } @@ -1923,15 +1865,11 @@ bool MethodVerifier::CodeFlowVerifyMethod() { static uint32_t GetFirstFinalInstanceFieldIndex(const DexFile& dex_file, dex::TypeIndex type_idx) { const DexFile::ClassDef* class_def = dex_file.FindClassDef(type_idx); DCHECK(class_def != nullptr); - const uint8_t* class_data = dex_file.GetClassData(*class_def); - DCHECK(class_data != nullptr); - ClassDataItemIterator it(dex_file, class_data); - it.SkipStaticFields(); - while (it.HasNextInstanceField()) { - if ((it.GetFieldAccessFlags() & kAccFinal) != 0) { - return it.GetMemberIndex(); + ClassAccessor accessor(dex_file, *class_def); + for (const ClassAccessor::Field& field : accessor.GetInstanceFields()) { + if (field.IsFinal()) { + return field.GetIndex(); } - it.Next(); } return dex::kDexNoIndex; } @@ -2287,14 +2225,10 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::CONST_METHOD_HANDLE: work_line_->SetRegisterType( this, inst->VRegA_21c(), reg_types_.JavaLangInvokeMethodHandle()); - // TODO: add compiler support for const-method-{handle,type} (b/66890674) - Fail(VERIFY_ERROR_FORCE_INTERPRETER); break; case Instruction::CONST_METHOD_TYPE: work_line_->SetRegisterType( this, inst->VRegA_21c(), reg_types_.JavaLangInvokeMethodType()); - // TODO: add compiler support for const-method-{handle,type} (b/66890674) - Fail(VERIFY_ERROR_FORCE_INTERPRETER); break; case Instruction::MONITOR_ENTER: work_line_->PushMonitor(this, inst->VRegA_11x(), work_insn_idx_); @@ -2935,7 +2869,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { : called_method->LookupResolvedReturnType(); if (return_type_class != nullptr) { return_type = &FromClass(return_type_descriptor, - return_type_class.Ptr(), + return_type_class, return_type_class->CannotBeAssignedFromOtherTypes()); } else { DCHECK(!can_load_classes_ || self_->IsExceptionPending()); @@ -3090,7 +3024,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { DCHECK(HasFailures()); break; } - const uint32_t proto_idx = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc(); + const uint16_t vRegH = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc(); + const dex::ProtoIndex proto_idx(vRegH); const char* return_descriptor = dex_file_->GetReturnTypeDescriptor(dex_file_->GetProtoId(proto_idx)); const RegType& return_type = @@ -3118,10 +3053,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { // 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 uint32_t proto_idx = static_cast(it.GetJavaValue().i); + 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. @@ -3137,8 +3069,6 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { 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: @@ -3368,7 +3298,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } break; } - auto* klass = declaring_class.GetClass(); + ObjPtr klass = declaring_class.GetClass(); for (uint32_t i = 0, num_fields = klass->NumInstanceFields(); i < num_fields; ++i) { if (klass->GetInstanceField(i)->IsFinal()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-void-no-barrier not expected for " @@ -3531,7 +3461,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { ObjPtr klass = linker->ResolveType(handler_type_idx, dex_cache_, class_loader_); if (klass != nullptr) { - if (klass == mirror::Throwable::GetJavaLangThrowable()) { + if (klass == GetClassRoot()) { has_catch_all_handler = true; } } else { @@ -3669,10 +3599,10 @@ const RegType& MethodVerifier::ResolveClass(dex::TypeIndex class_idx) { UninstantiableError(descriptor); precise = false; } - result = reg_types_.FindClass(klass.Ptr(), precise); + result = reg_types_.FindClass(klass, precise); if (result == nullptr) { const char* descriptor = dex_file_->StringByTypeIdx(class_idx); - result = reg_types_.InsertClass(descriptor, klass.Ptr(), precise); + result = reg_types_.InsertClass(descriptor, klass, precise); } } else { const char* descriptor = dex_file_->StringByTypeIdx(class_idx); @@ -3687,7 +3617,7 @@ const RegType& MethodVerifier::ResolveClass(dex::TypeIndex class_idx) { } // Record result of class resolution attempt. - VerifierDeps::MaybeRecordClassResolution(*dex_file_, class_idx, klass.Ptr()); + VerifierDeps::MaybeRecordClassResolution(*dex_file_, class_idx, klass); // If requested, check if access is allowed. Unresolved types are included in this check, as the // interpreter only tests whether access is allowed when a class is not pre-verified and runs in @@ -3909,6 +3839,8 @@ ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess( template ArtMethod* MethodVerifier::VerifyInvocationArgsFromIterator( T* it, const Instruction* inst, MethodType method_type, bool is_range, ArtMethod* res_method) { + DCHECK_EQ(!is_range, inst->HasVarArgs()); + // We use vAA as our expected arg count, rather than res_method->insSize, because we need to // match the call to the signature. Also, we might be calling through an abstract method // definition (which doesn't have register count values). @@ -4072,112 +4004,47 @@ bool MethodVerifier::CheckCallSite(uint32_t call_site_idx) { 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(it.GetJavaValue().i); - it.Next(); + std::pair 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]; - const DexFile::MethodHandleItem& mh = dex_file_->GetMethodHandle(method_handle_idx); + // 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(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(); + } + + // Check method handle kind is valid. + const DexFile::MethodHandleItem& mh = dex_file_->GetMethodHandle(index[0]); if (mh.method_handle_type_ != static_cast(DexFile::MethodHandleType::kInvokeStatic)) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx << " argument 0 method handle type is not InvokeStatic: " << mh.method_handle_type_; return false; } - - // Skip the second argument, the name to resolve, as checked by the - // dex file verifier. - it.Next(); - - // Skip the third argument, the method type expected, as checked by - // the dex file verifier. - it.Next(); - - // Check the bootstrap method handle and remaining arguments. - const DexFile::MethodId& method_id = dex_file_->GetMethodId(mh.field_or_method_idx_); - uint32_t length; - const char* shorty = dex_file_->GetMethodShorty(method_id, &length); - - if (it.Size() < length - 1) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx - << " too few arguments for bootstrap method: " - << it.Size() << " < " << (length - 1); - return false; - } - - // Check the return type and first 3 arguments are references - // (CallSite, Lookup, String, MethodType). If they are not of the - // expected types (or subtypes), it will trigger a - // WrongMethodTypeException during execution. - if (shorty[0] != 'L') { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx - << " bootstrap return type is not a reference"; - return false; - } - - for (uint32_t i = 1; i < 4; ++i) { - if (shorty[i] != 'L') { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx - << " bootstrap method argument " << (i - 1) - << " is not a reference"; - return false; - } - } - - // Check the optional arguments. - for (uint32_t i = 4; i < length; ++i, it.Next()) { - bool match = false; - switch (it.GetValueType()) { - case EncodedArrayValueIterator::ValueType::kBoolean: - case EncodedArrayValueIterator::ValueType::kByte: - case EncodedArrayValueIterator::ValueType::kShort: - case EncodedArrayValueIterator::ValueType::kChar: - case EncodedArrayValueIterator::ValueType::kInt: - // These all fit within one register and encoders do not seem - // too exacting on the encoding type they use (ie using - // integer for all of these). - match = (strchr("ZBCSI", shorty[i]) != nullptr); - break; - case EncodedArrayValueIterator::ValueType::kLong: - match = ('J' == shorty[i]); - break; - case EncodedArrayValueIterator::ValueType::kFloat: - match = ('F' == shorty[i]); - break; - case EncodedArrayValueIterator::ValueType::kDouble: - match = ('D' == shorty[i]); - break; - case EncodedArrayValueIterator::ValueType::kMethodType: - case EncodedArrayValueIterator::ValueType::kMethodHandle: - case EncodedArrayValueIterator::ValueType::kString: - case EncodedArrayValueIterator::ValueType::kType: - case EncodedArrayValueIterator::ValueType::kNull: - match = ('L' == shorty[i]); - break; - case EncodedArrayValueIterator::ValueType::kField: - case EncodedArrayValueIterator::ValueType::kMethod: - case EncodedArrayValueIterator::ValueType::kEnum: - case EncodedArrayValueIterator::ValueType::kArray: - case EncodedArrayValueIterator::ValueType::kAnnotation: - // Unreachable based on current EncodedArrayValueIterator::Next(). - UNREACHABLE(); - } - - if (!match) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx - << " bootstrap method argument " << (i - 1) - << " expected " << shorty[i] - << " got value type: " << it.GetValueType(); - return false; - } - } return true; } @@ -4276,7 +4143,8 @@ ArtMethod* MethodVerifier::VerifyInvocationArgs( if (UNLIKELY(method_type == METHOD_POLYMORPHIC)) { // Process the signature of the calling site that is invoking the method handle. - DexFileParameterIterator it(*dex_file_, dex_file_->GetProtoId(inst->VRegH())); + dex::ProtoIndex proto_idx(inst->VRegH()); + DexFileParameterIterator it(*dex_file_, dex_file_->GetProtoId(proto_idx)); return VerifyInvocationArgsFromIterator(&it, inst, method_type, is_range, res_method); } else { // Process the target method's signature. @@ -4290,12 +4158,12 @@ bool MethodVerifier::CheckSignaturePolymorphicMethod(ArtMethod* method) { const char* method_name = method->GetName(); const char* expected_return_descriptor; - if (klass == mirror::MethodHandle::StaticClass()) { + ObjPtr> class_roots = + Runtime::Current()->GetClassLinker()->GetClassRoots(); + if (klass == GetClassRoot(class_roots)) { expected_return_descriptor = mirror::MethodHandle::GetReturnTypeDescriptor(method_name); - } else if (klass == mirror::VarHandle::StaticClass()) { + } else if (klass == GetClassRoot(class_roots)) { expected_return_descriptor = mirror::VarHandle::GetReturnTypeDescriptor(method_name); - // TODO: add compiler support for VarHandle accessor methods (b/71781600) - Fail(VERIFY_ERROR_FORCE_INTERPRETER); } else { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Signature polymorphic method in unsuppported class: " << klass->PrettyDescriptor(); @@ -4354,12 +4222,16 @@ bool MethodVerifier::CheckSignaturePolymorphicReceiver(const Instruction* inst) << "invoke-polymorphic receiver has no class: " << this_type; return false; - } else if (!this_type.GetClass()->IsSubClass(mirror::MethodHandle::StaticClass()) && - !this_type.GetClass()->IsSubClass(mirror::VarHandle::StaticClass())) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) - << "invoke-polymorphic receiver is not a subclass of MethodHandle or VarHandle: " - << this_type; - return false; + } else { + ObjPtr> class_roots = + Runtime::Current()->GetClassLinker()->GetClassRoots(); + if (!this_type.GetClass()->IsSubClass(GetClassRoot(class_roots)) && + !this_type.GetClass()->IsSubClass(GetClassRoot(class_roots))) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) + << "invoke-polymorphic receiver is not a subclass of MethodHandle or VarHandle: " + << this_type; + return false; + } } return true; } @@ -4707,9 +4579,7 @@ ArtField* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_id std::string temp; ObjPtr klass = field->GetDeclaringClass(); const RegType& field_klass = - FromClass(klass->GetDescriptor(&temp), - klass.Ptr(), - klass->CannotBeAssignedFromOtherTypes()); + FromClass(klass->GetDescriptor(&temp), klass, klass->CannotBeAssignedFromOtherTypes()); if (obj_type.IsUninitializedTypes()) { // Field accesses through uninitialized references are only allowable for constructors where // the field is declared in this class. @@ -4810,7 +4680,7 @@ void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, const RegType& can_load_classes_ ? field->ResolveType() : field->LookupResolvedType(); if (field_type_class != nullptr) { field_type = &FromClass(field->GetTypeDescriptor(), - field_type_class.Ptr(), + field_type_class, field_type_class->CannotBeAssignedFromOtherTypes()); } else { DCHECK(!can_load_classes_ || self_->IsExceptionPending()); @@ -4998,7 +4868,7 @@ const RegType& MethodVerifier::GetMethodReturnType() { : method_being_verified_->LookupResolvedReturnType(); if (return_type_class != nullptr) { return_type_ = &FromClass(method_being_verified_->GetReturnTypeDescriptor(), - return_type_class.Ptr(), + return_type_class, return_type_class->CannotBeAssignedFromOtherTypes()); } else { DCHECK(!can_load_classes_ || self_->IsExceptionPending()); @@ -5022,7 +4892,7 @@ const RegType& MethodVerifier::GetDeclaringClass() { const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_)); if (method_being_verified_ != nullptr) { - mirror::Class* klass = method_being_verified_->GetDeclaringClass(); + ObjPtr klass = method_being_verified_->GetDeclaringClass(); declaring_class_ = &FromClass(descriptor, klass, klass->CannotBeAssignedFromOtherTypes()); } else { declaring_class_ = ®_types_.FromDescriptor(GetClassLoader(), descriptor, false); @@ -5124,7 +4994,7 @@ void MethodVerifier::VisitRoots(RootVisitor* visitor, const RootInfo& root_info) } const RegType& MethodVerifier::FromClass(const char* descriptor, - mirror::Class* klass, + ObjPtr klass, bool precise) { DCHECK(klass != nullptr); if (precise && !klass->IsInstantiable() && !klass->IsPrimitive()) { diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 531d3dabfad9fa43a72fcdde516810b9d273d778..9890af9d95522c2ec2dea1127e05d75ec28a789c 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -96,7 +96,7 @@ class MethodVerifier { public: // Verify a class. Returns "kNoFailure" on success. static FailureKind VerifyClass(Thread* self, - mirror::Class* klass, + ObjPtr klass, CompilerCallbacks* callbacks, bool allow_soft_failures, HardFailLogMode log_level, @@ -275,23 +275,6 @@ class MethodVerifier { void Merge(const FailureData& src); }; - // Verify all direct or virtual methods of a class. The method assumes that the iterator is - // positioned correctly, and the iterator will be updated. - template - static FailureData VerifyMethods(Thread* self, - ClassLinker* linker, - const DexFile* dex_file, - const DexFile::ClassDef& class_def, - ClassDataItemIterator* it, - Handle dex_cache, - Handle class_loader, - CompilerCallbacks* callbacks, - bool allow_soft_failures, - HardFailLogMode log_level, - bool need_precise_constants, - std::string* error_string) - REQUIRES_SHARED(Locks::mutator_lock_); - /* * Perform verification on a single method. * @@ -691,7 +674,7 @@ class MethodVerifier { // non-precise reference will be returned. // Note: we reuse NO_CLASS as this will throw an exception at runtime, when the failing class is // actually touched. - const RegType& FromClass(const char* descriptor, mirror::Class* klass, bool precise) + const RegType& FromClass(const char* descriptor, ObjPtr klass, bool precise) REQUIRES_SHARED(Locks::mutator_lock_); ALWAYS_INLINE bool FailOrAbort(bool condition, const char* error_msg, uint32_t work_insn_idx); diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc index db3f093905a355f919d060260a1ee040c734d487..d1be9fa6f8b578a80140e940b5c79ff2d9628012 100644 --- a/runtime/verifier/method_verifier_test.cc +++ b/runtime/verifier/method_verifier_test.cc @@ -37,7 +37,7 @@ class MethodVerifierTest : public CommonRuntimeTest { REQUIRES_SHARED(Locks::mutator_lock_) { ASSERT_TRUE(descriptor != nullptr); Thread* self = Thread::Current(); - mirror::Class* klass = class_linker_->FindSystemClass(self, descriptor.c_str()); + ObjPtr klass = class_linker_->FindSystemClass(self, descriptor.c_str()); // Verify the class std::string error_msg; diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc index e7864a28a085b1f952206fc4875118e8faf9e4fa..4a3f9e63656bdaddf8d9ab6f4a83448fd421eb7a 100644 --- a/runtime/verifier/reg_type.cc +++ b/runtime/verifier/reg_type.cc @@ -54,17 +54,19 @@ const DoubleHiType* DoubleHiType::instance_ = nullptr; const IntegerType* IntegerType::instance_ = nullptr; const NullType* NullType::instance_ = nullptr; -PrimitiveType::PrimitiveType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) +PrimitiveType::PrimitiveType(ObjPtr klass, + const StringPiece& descriptor, + uint16_t cache_id) : RegType(klass, descriptor, cache_id) { CHECK(klass != nullptr); CHECK(!descriptor.empty()); } -Cat1Type::Cat1Type(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) +Cat1Type::Cat1Type(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) : PrimitiveType(klass, descriptor, cache_id) { } -Cat2Type::Cat2Type(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) +Cat2Type::Cat2Type(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) : PrimitiveType(klass, descriptor, cache_id) { } @@ -129,7 +131,7 @@ std::string IntegerType::Dump() const { return "Integer"; } -const DoubleHiType* DoubleHiType::CreateInstance(mirror::Class* klass, +const DoubleHiType* DoubleHiType::CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); @@ -144,7 +146,7 @@ void DoubleHiType::Destroy() { } } -const DoubleLoType* DoubleLoType::CreateInstance(mirror::Class* klass, +const DoubleLoType* DoubleLoType::CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); @@ -159,14 +161,16 @@ void DoubleLoType::Destroy() { } } -const LongLoType* LongLoType::CreateInstance(mirror::Class* klass, const StringPiece& descriptor, +const LongLoType* LongLoType::CreateInstance(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); instance_ = new LongLoType(klass, descriptor, cache_id); return instance_; } -const LongHiType* LongHiType::CreateInstance(mirror::Class* klass, const StringPiece& descriptor, +const LongHiType* LongHiType::CreateInstance(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); instance_ = new LongHiType(klass, descriptor, cache_id); @@ -187,7 +191,8 @@ void LongLoType::Destroy() { } } -const FloatType* FloatType::CreateInstance(mirror::Class* klass, const StringPiece& descriptor, +const FloatType* FloatType::CreateInstance(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); instance_ = new FloatType(klass, descriptor, cache_id); @@ -201,7 +206,8 @@ void FloatType::Destroy() { } } -const CharType* CharType::CreateInstance(mirror::Class* klass, const StringPiece& descriptor, +const CharType* CharType::CreateInstance(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); instance_ = new CharType(klass, descriptor, cache_id); @@ -215,7 +221,8 @@ void CharType::Destroy() { } } -const ShortType* ShortType::CreateInstance(mirror::Class* klass, const StringPiece& descriptor, +const ShortType* ShortType::CreateInstance(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); instance_ = new ShortType(klass, descriptor, cache_id); @@ -229,7 +236,8 @@ void ShortType::Destroy() { } } -const ByteType* ByteType::CreateInstance(mirror::Class* klass, const StringPiece& descriptor, +const ByteType* ByteType::CreateInstance(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); instance_ = new ByteType(klass, descriptor, cache_id); @@ -243,7 +251,8 @@ void ByteType::Destroy() { } } -const IntegerType* IntegerType::CreateInstance(mirror::Class* klass, const StringPiece& descriptor, +const IntegerType* IntegerType::CreateInstance(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); instance_ = new IntegerType(klass, descriptor, cache_id); @@ -257,7 +266,7 @@ void IntegerType::Destroy() { } } -const ConflictType* ConflictType::CreateInstance(mirror::Class* klass, +const ConflictType* ConflictType::CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); @@ -272,8 +281,9 @@ void ConflictType::Destroy() { } } -const BooleanType* BooleanType::CreateInstance(mirror::Class* klass, const StringPiece& descriptor, - uint16_t cache_id) { +const BooleanType* BooleanType::CreateInstance(ObjPtr klass, + const StringPiece& descriptor, + uint16_t cache_id) { CHECK(BooleanType::instance_ == nullptr); instance_ = new BooleanType(klass, descriptor, cache_id); return BooleanType::instance_; @@ -290,7 +300,7 @@ std::string UndefinedType::Dump() const REQUIRES_SHARED(Locks::mutator_lock_) { return "Undefined"; } -const UndefinedType* UndefinedType::CreateInstance(mirror::Class* klass, +const UndefinedType* UndefinedType::CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); @@ -305,7 +315,8 @@ void UndefinedType::Destroy() { } } -PreciseReferenceType::PreciseReferenceType(mirror::Class* klass, const StringPiece& descriptor, +PreciseReferenceType::PreciseReferenceType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) : RegType(klass, descriptor, cache_id) { // Note: no check for IsInstantiable() here. We may produce this in case an InstantiationError @@ -505,7 +516,7 @@ bool UnresolvedType::IsNonZeroReferenceTypes() const { const RegType& RegType::GetSuperClass(RegTypeCache* cache) const { if (!IsUnresolvedTypes()) { - mirror::Class* super_klass = GetClass()->GetSuperClass(); + ObjPtr super_klass = GetClass()->GetSuperClass(); if (super_klass != nullptr) { // A super class of a precise type isn't precise as a precise type indicates the register // holds exactly that type. @@ -543,7 +554,7 @@ bool RegType::IsObjectArrayTypes() const REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(descriptor_[1] == 'L' || descriptor_[1] == '['); return descriptor_[0] == '['; } else if (HasClass()) { - mirror::Class* type = GetClass(); + ObjPtr type = GetClass(); return type->IsArrayClass() && !type->GetComponentType()->IsPrimitive(); } else { return false; @@ -569,7 +580,7 @@ bool RegType::IsArrayTypes() const REQUIRES_SHARED(Locks::mutator_lock_) { bool RegType::IsJavaLangObjectArray() const { if (HasClass()) { - mirror::Class* type = GetClass(); + ObjPtr type = GetClass(); return type->IsArrayClass() && type->GetComponentType()->IsObjectClass(); } return false; @@ -712,11 +723,10 @@ const RegType& RegType::Merge(const RegType& incoming_type, // mechanics to continue. return reg_types->FromUnresolvedMerge(*this, incoming_type, verifier); } else { // Two reference types, compute Join - mirror::Class* c1 = GetClass(); - mirror::Class* c2 = incoming_type.GetClass(); - DCHECK(c1 != nullptr && !c1->IsPrimitive()); - DCHECK(c2 != nullptr && !c2->IsPrimitive()); - mirror::Class* join_class = ClassJoin(c1, c2); + // Do not cache the classes as ClassJoin() can suspend and invalidate ObjPtr<>s. + DCHECK(GetClass() != nullptr && !GetClass()->IsPrimitive()); + DCHECK(incoming_type.GetClass() != nullptr && !incoming_type.GetClass()->IsPrimitive()); + ObjPtr join_class = ClassJoin(GetClass(), incoming_type.GetClass()); if (UNLIKELY(join_class == nullptr)) { // Internal error joining the classes (e.g., OOME). Report an unresolved reference type. // We cannot report an unresolved merge type, as that will attempt to merge the resolved @@ -731,30 +741,37 @@ const RegType& RegType::Merge(const RegType& incoming_type, // (In that case, it is likely a misconfiguration of dex2oat.) if (!kIsTargetBuild && Runtime::Current()->IsAotCompiler()) { LOG(FATAL) << "Could not create class join of " - << c1->PrettyClass() + << GetClass()->PrettyClass() << " & " - << c2->PrettyClass(); + << incoming_type.GetClass()->PrettyClass(); UNREACHABLE(); } return reg_types->MakeUnresolvedReference(); } - // Record the dependency that both `c1` and `c2` are assignable to `join_class`. - // The `verifier` is null during unit tests. + // Record the dependency that both `GetClass()` and `incoming_type.GetClass()` + // are assignable to `join_class`. The `verifier` is null during unit tests. if (verifier != nullptr) { - VerifierDeps::MaybeRecordAssignability( - verifier->GetDexFile(), join_class, c1, true /* strict */, true /* is_assignable */); - VerifierDeps::MaybeRecordAssignability( - verifier->GetDexFile(), join_class, c2, true /* strict */, true /* is_assignable */); + VerifierDeps::MaybeRecordAssignability(verifier->GetDexFile(), + join_class, + GetClass(), + /* strict */ true, + /* is_assignable */ true); + VerifierDeps::MaybeRecordAssignability(verifier->GetDexFile(), + join_class, + incoming_type.GetClass(), + /* strict */ true, + /* is_assignable */ true); } - if (c1 == join_class && !IsPreciseReference()) { + if (GetClass() == join_class && !IsPreciseReference()) { return *this; - } else if (c2 == join_class && !incoming_type.IsPreciseReference()) { + } else if (incoming_type.GetClass() == join_class && !incoming_type.IsPreciseReference()) { return incoming_type; } else { std::string temp; - return reg_types->FromClass(join_class->GetDescriptor(&temp), join_class, false); + const char* descriptor = join_class->GetDescriptor(&temp); + return reg_types->FromClass(descriptor, join_class, /* precise */ false); } } } else { @@ -763,7 +780,7 @@ const RegType& RegType::Merge(const RegType& incoming_type, } // See comment in reg_type.h -mirror::Class* RegType::ClassJoin(mirror::Class* s, mirror::Class* t) { +ObjPtr RegType::ClassJoin(ObjPtr s, ObjPtr t) { DCHECK(!s->IsPrimitive()) << s->PrettyClass(); DCHECK(!t->IsPrimitive()) << t->PrettyClass(); if (s == t) { @@ -773,12 +790,12 @@ mirror::Class* RegType::ClassJoin(mirror::Class* s, mirror::Class* t) { } else if (t->IsAssignableFrom(s)) { return t; } else if (s->IsArrayClass() && t->IsArrayClass()) { - mirror::Class* s_ct = s->GetComponentType(); - mirror::Class* t_ct = t->GetComponentType(); + ObjPtr s_ct = s->GetComponentType(); + ObjPtr t_ct = t->GetComponentType(); if (s_ct->IsPrimitive() || t_ct->IsPrimitive()) { // Given the types aren't the same, if either array is of primitive types then the only // common parent is java.lang.Object - mirror::Class* result = s->GetSuperClass(); // short-cut to java.lang.Object + ObjPtr result = s->GetSuperClass(); // short-cut to java.lang.Object DCHECK(result->IsObjectClass()); return result; } @@ -788,8 +805,9 @@ mirror::Class* RegType::ClassJoin(mirror::Class* s, mirror::Class* t) { self->AssertPendingException(); return nullptr; } - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - mirror::Class* array_class = class_linker->FindArrayClass(self, &common_elem); + // Note: The following lookup invalidates existing ObjPtr<>s. + ObjPtr array_class = + Runtime::Current()->GetClassLinker()->FindArrayClass(self, common_elem); if (UNLIKELY(array_class == nullptr)) { self->AssertPendingException(); return nullptr; @@ -971,7 +989,7 @@ bool RegType::CanAssignArray(const RegType& src, return cmp1.CanAssignArray(cmp2, reg_types, class_loader, verifier, soft_error); } -const NullType* NullType::CreateInstance(mirror::Class* klass, +const NullType* NullType::CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h index 3e994074a127fe2d0b2c154b30830cce704e3240..29da376091b948cbc6a27a1de35475f1afaf4004 100644 --- a/runtime/verifier/reg_type.h +++ b/runtime/verifier/reg_type.h @@ -191,7 +191,7 @@ class RegType { !IsUnresolvedSuperClass())); return descriptor_; } - mirror::Class* GetClass() const REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr GetClass() const REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(!IsUnresolvedReference()); DCHECK(!klass_.IsNull()) << Dump(); DCHECK(HasClass()); @@ -318,7 +318,7 @@ class RegType { } protected: - RegType(mirror::Class* klass, + RegType(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : descriptor_(descriptor), @@ -365,7 +365,7 @@ class RegType { * * [1] Java bytecode verification: algorithms and formalizations, Xavier Leroy */ - static mirror::Class* ClassJoin(mirror::Class* s, mirror::Class* t) + static ObjPtr ClassJoin(ObjPtr s, ObjPtr t) REQUIRES_SHARED(Locks::mutator_lock_); static bool AssignableFrom(const RegType& lhs, @@ -388,7 +388,7 @@ class ConflictType FINAL : public RegType { static const ConflictType* GetInstance() PURE; // Create the singleton instance. - static const ConflictType* CreateInstance(mirror::Class* klass, + static const ConflictType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -401,7 +401,8 @@ class ConflictType FINAL : public RegType { } private: - ConflictType(mirror::Class* klass, const StringPiece& descriptor, + ConflictType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : RegType(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -423,7 +424,7 @@ class UndefinedType FINAL : public RegType { static const UndefinedType* GetInstance() PURE; // Create the singleton instance. - static const UndefinedType* CreateInstance(mirror::Class* klass, + static const UndefinedType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -436,7 +437,8 @@ class UndefinedType FINAL : public RegType { } private: - UndefinedType(mirror::Class* klass, const StringPiece& descriptor, + UndefinedType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : RegType(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -447,7 +449,8 @@ class UndefinedType FINAL : public RegType { class PrimitiveType : public RegType { public: - PrimitiveType(mirror::Class* klass, const StringPiece& descriptor, + PrimitiveType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); bool HasClassVirtual() const OVERRIDE { return true; } @@ -455,7 +458,7 @@ class PrimitiveType : public RegType { class Cat1Type : public PrimitiveType { public: - Cat1Type(mirror::Class* klass, const StringPiece& descriptor, + Cat1Type(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); }; @@ -463,7 +466,7 @@ class IntegerType FINAL : public Cat1Type { public: bool IsInteger() const OVERRIDE { return true; } std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); - static const IntegerType* CreateInstance(mirror::Class* klass, + static const IntegerType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -475,7 +478,8 @@ class IntegerType FINAL : public Cat1Type { } private: - IntegerType(mirror::Class* klass, const StringPiece& descriptor, + IntegerType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : Cat1Type(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -487,7 +491,7 @@ class BooleanType FINAL : public Cat1Type { public: bool IsBoolean() const OVERRIDE { return true; } std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); - static const BooleanType* CreateInstance(mirror::Class* klass, + static const BooleanType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -499,7 +503,8 @@ class BooleanType FINAL : public Cat1Type { } private: - BooleanType(mirror::Class* klass, const StringPiece& descriptor, + BooleanType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : Cat1Type(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -512,7 +517,7 @@ class ByteType FINAL : public Cat1Type { public: bool IsByte() const OVERRIDE { return true; } std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); - static const ByteType* CreateInstance(mirror::Class* klass, + static const ByteType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -524,7 +529,8 @@ class ByteType FINAL : public Cat1Type { } private: - ByteType(mirror::Class* klass, const StringPiece& descriptor, + ByteType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : Cat1Type(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -536,7 +542,7 @@ class ShortType FINAL : public Cat1Type { public: bool IsShort() const OVERRIDE { return true; } std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); - static const ShortType* CreateInstance(mirror::Class* klass, + static const ShortType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -548,7 +554,7 @@ class ShortType FINAL : public Cat1Type { } private: - ShortType(mirror::Class* klass, const StringPiece& descriptor, + ShortType(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : Cat1Type(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -560,7 +566,7 @@ class CharType FINAL : public Cat1Type { public: bool IsChar() const OVERRIDE { return true; } std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); - static const CharType* CreateInstance(mirror::Class* klass, + static const CharType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -572,7 +578,8 @@ class CharType FINAL : public Cat1Type { } private: - CharType(mirror::Class* klass, const StringPiece& descriptor, + CharType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : Cat1Type(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -584,7 +591,7 @@ class FloatType FINAL : public Cat1Type { public: bool IsFloat() const OVERRIDE { return true; } std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); - static const FloatType* CreateInstance(mirror::Class* klass, + static const FloatType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -596,7 +603,8 @@ class FloatType FINAL : public Cat1Type { } private: - FloatType(mirror::Class* klass, const StringPiece& descriptor, + FloatType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : Cat1Type(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -606,7 +614,8 @@ class FloatType FINAL : public Cat1Type { class Cat2Type : public PrimitiveType { public: - Cat2Type(mirror::Class* klass, const StringPiece& descriptor, + Cat2Type(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); }; @@ -615,7 +624,7 @@ class LongLoType FINAL : public Cat2Type { std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); bool IsLongLo() const OVERRIDE { return true; } bool IsLong() const OVERRIDE { return true; } - static const LongLoType* CreateInstance(mirror::Class* klass, + static const LongLoType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -627,7 +636,8 @@ class LongLoType FINAL : public Cat2Type { } private: - LongLoType(mirror::Class* klass, const StringPiece& descriptor, + LongLoType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : Cat2Type(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -639,7 +649,7 @@ class LongHiType FINAL : public Cat2Type { public: std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); bool IsLongHi() const OVERRIDE { return true; } - static const LongHiType* CreateInstance(mirror::Class* klass, + static const LongHiType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -651,7 +661,8 @@ class LongHiType FINAL : public Cat2Type { } private: - LongHiType(mirror::Class* klass, const StringPiece& descriptor, + LongHiType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : Cat2Type(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -664,7 +675,7 @@ class DoubleLoType FINAL : public Cat2Type { std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); bool IsDoubleLo() const OVERRIDE { return true; } bool IsDouble() const OVERRIDE { return true; } - static const DoubleLoType* CreateInstance(mirror::Class* klass, + static const DoubleLoType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -676,7 +687,8 @@ class DoubleLoType FINAL : public Cat2Type { } private: - DoubleLoType(mirror::Class* klass, const StringPiece& descriptor, + DoubleLoType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : Cat2Type(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -688,9 +700,9 @@ class DoubleHiType FINAL : public Cat2Type { public: std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); virtual bool IsDoubleHi() const OVERRIDE { return true; } - static const DoubleHiType* CreateInstance(mirror::Class* klass, - const StringPiece& descriptor, - uint16_t cache_id) + static const DoubleHiType* CreateInstance(ObjPtr klass, + const StringPiece& descriptor, + uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); static const DoubleHiType* GetInstance() PURE; static void Destroy(); @@ -700,7 +712,8 @@ class DoubleHiType FINAL : public Cat2Type { } private: - DoubleHiType(mirror::Class* klass, const StringPiece& descriptor, + DoubleHiType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : Cat2Type(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -872,7 +885,7 @@ class NullType FINAL : public RegType { static const NullType* GetInstance() PURE; // Create the singleton instance. - static const NullType* CreateInstance(mirror::Class* klass, + static const NullType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -892,7 +905,7 @@ class NullType FINAL : public RegType { } private: - NullType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) + NullType(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : RegType(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -906,8 +919,10 @@ class NullType FINAL : public RegType { // instructions and must be passed to a constructor. class UninitializedType : public RegType { public: - UninitializedType(mirror::Class* klass, const StringPiece& descriptor, - uint32_t allocation_pc, uint16_t cache_id) + UninitializedType(ObjPtr klass, + const StringPiece& descriptor, + uint32_t allocation_pc, + uint16_t cache_id) : RegType(klass, descriptor, cache_id), allocation_pc_(allocation_pc) {} bool IsUninitializedTypes() const OVERRIDE; @@ -929,9 +944,10 @@ class UninitializedType : public RegType { // Similar to ReferenceType but not yet having been passed to a constructor. class UninitializedReferenceType FINAL : public UninitializedType { public: - UninitializedReferenceType(mirror::Class* klass, + UninitializedReferenceType(ObjPtr klass, const StringPiece& descriptor, - uint32_t allocation_pc, uint16_t cache_id) + uint32_t allocation_pc, + uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : UninitializedType(klass, descriptor, allocation_pc, cache_id) { CheckConstructorInvariants(this); @@ -969,7 +985,7 @@ class UnresolvedUninitializedRefType FINAL : public UninitializedType { // of a constructor. class UninitializedThisReferenceType FINAL : public UninitializedType { public: - UninitializedThisReferenceType(mirror::Class* klass, + UninitializedThisReferenceType(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) @@ -1010,7 +1026,8 @@ class UnresolvedUninitializedThisRefType FINAL : public UninitializedType { // sub-class. class ReferenceType FINAL : public RegType { public: - ReferenceType(mirror::Class* klass, const StringPiece& descriptor, + ReferenceType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : RegType(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -1034,7 +1051,8 @@ class ReferenceType FINAL : public RegType { // type. class PreciseReferenceType FINAL : public RegType { public: - PreciseReferenceType(mirror::Class* klass, const StringPiece& descriptor, + PreciseReferenceType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/verifier/reg_type_cache-inl.h b/runtime/verifier/reg_type_cache-inl.h index 61f34afdacf55b72eb24c45142594f0e8c7d89f3..9f87adfa3117880d9269247d209e9303ba95b737 100644 --- a/runtime/verifier/reg_type_cache-inl.h +++ b/runtime/verifier/reg_type_cache-inl.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_VERIFIER_REG_TYPE_CACHE_INL_H_ #include "class_linker.h" +#include "class_root.h" #include "mirror/class-inl.h" #include "mirror/method_handle_impl.h" #include "mirror/method_type.h" @@ -123,36 +124,42 @@ inline const ImpreciseConstType& RegTypeCache::PosShortConstant() { } inline const PreciseReferenceType& RegTypeCache::JavaLangClass() { - const RegType* result = &FromClass("Ljava/lang/Class;", mirror::Class::GetJavaLangClass(), true); + const RegType* result = &FromClass("Ljava/lang/Class;", + GetClassRoot(), + /* precise */ true); DCHECK(result->IsPreciseReference()); return *down_cast(result); } inline const PreciseReferenceType& RegTypeCache::JavaLangString() { // String is final and therefore always precise. - const RegType* result = &FromClass("Ljava/lang/String;", mirror::String::GetJavaLangString(), - true); + const RegType* result = &FromClass("Ljava/lang/String;", + GetClassRoot(), + /* precise */ true); DCHECK(result->IsPreciseReference()); return *down_cast(result); } inline const PreciseReferenceType& RegTypeCache::JavaLangInvokeMethodHandle() { const RegType* result = &FromClass("Ljava/lang/invoke/MethodHandle;", - mirror::MethodHandle::StaticClass(), true); + GetClassRoot(), + /* precise */ true); DCHECK(result->IsPreciseReference()); return *down_cast(result); } inline const PreciseReferenceType& RegTypeCache::JavaLangInvokeMethodType() { const RegType* result = &FromClass("Ljava/lang/invoke/MethodType;", - mirror::MethodType::StaticClass(), true); + GetClassRoot(), + /* precise */ true); DCHECK(result->IsPreciseReference()); return *down_cast(result); } inline const RegType& RegTypeCache::JavaLangThrowable(bool precise) { const RegType* result = &FromClass("Ljava/lang/Throwable;", - mirror::Throwable::GetJavaLangThrowable(), precise); + GetClassRoot(), + precise); if (precise) { DCHECK(result->IsPreciseReference()); return *down_cast(result); @@ -163,8 +170,7 @@ inline const RegType& RegTypeCache::JavaLangThrowable(bool precise) { } inline const RegType& RegTypeCache::JavaLangObject(bool precise) { - const RegType* result = &FromClass("Ljava/lang/Object;", - mirror::Class::GetJavaLangClass()->GetSuperClass(), precise); + const RegType* result = &FromClass("Ljava/lang/Object;", GetClassRoot(), precise); if (precise) { DCHECK(result->IsPreciseReference()); return *down_cast(result); @@ -179,7 +185,7 @@ inline RegTypeType& RegTypeCache::AddEntry(RegTypeType* new_entry) { DCHECK(new_entry != nullptr); entries_.push_back(new_entry); if (new_entry->HasClass()) { - mirror::Class* klass = new_entry->GetClass(); + ObjPtr klass = new_entry->GetClass(); DCHECK(!klass->IsPrimitive()); klass_entries_.push_back(std::make_pair(GcRoot(klass), new_entry)); } diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index 87fc60bd23b885c78ae01339733d2da7ef0fc1b0..f1f3488a3c444e85f7602519d5174add7927a067 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -77,7 +77,7 @@ void RegTypeCache::FillPrimitiveAndSmallConstantTypes() { DCHECK_EQ(entries_.size(), primitive_count_); } -const RegType& RegTypeCache::FromDescriptor(mirror::ClassLoader* loader, +const RegType& RegTypeCache::FromDescriptor(ObjPtr loader, const char* descriptor, bool precise) { DCHECK(RegTypeCache::primitive_initialized_); @@ -149,14 +149,15 @@ bool RegTypeCache::MatchDescriptor(size_t idx, const StringPiece& descriptor, bo return true; } -mirror::Class* RegTypeCache::ResolveClass(const char* descriptor, mirror::ClassLoader* loader) { +ObjPtr RegTypeCache::ResolveClass(const char* descriptor, + ObjPtr loader) { // Class was not found, must create new type. // Try resolving class ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Thread* self = Thread::Current(); StackHandleScope<1> hs(self); Handle class_loader(hs.NewHandle(loader)); - mirror::Class* klass = nullptr; + ObjPtr klass = nullptr; if (can_load_classes_) { klass = class_linker->FindClass(self, descriptor, class_loader); } else { @@ -175,7 +176,7 @@ StringPiece RegTypeCache::AddString(const StringPiece& string_piece) { return StringPiece(ptr, string_piece.length()); } -const RegType& RegTypeCache::From(mirror::ClassLoader* loader, +const RegType& RegTypeCache::From(ObjPtr loader, const char* descriptor, bool precise) { StringPiece sp_descriptor(descriptor); @@ -188,7 +189,7 @@ const RegType& RegTypeCache::From(mirror::ClassLoader* loader, } // Class not found in the cache, will create a new type for that. // Try resolving class. - mirror::Class* klass = ResolveClass(descriptor, loader); + ObjPtr klass = ResolveClass(descriptor, loader); if (klass != nullptr) { // Class resolved, first look for the class in the list of entries // Class was not found, must create new type. @@ -234,7 +235,7 @@ const RegType& RegTypeCache::MakeUnresolvedReference() { return AddEntry(new (&allocator_) UnresolvedReferenceType(AddString("a"), entries_.size())); } -const RegType* RegTypeCache::FindClass(mirror::Class* klass, bool precise) const { +const RegType* RegTypeCache::FindClass(ObjPtr klass, bool precise) const { DCHECK(klass != nullptr); if (klass->IsPrimitive()) { // Note: precise isn't used for primitive classes. A char is assignable to an int. All @@ -242,7 +243,7 @@ const RegType* RegTypeCache::FindClass(mirror::Class* klass, bool precise) const return &RegTypeFromPrimitiveType(klass->GetPrimitiveType()); } for (auto& pair : klass_entries_) { - mirror::Class* const reg_klass = pair.first.Read(); + ObjPtr const reg_klass = pair.first.Read(); if (reg_klass == klass) { const RegType* reg_type = pair.second; if (MatchingPrecisionForClass(reg_type, precise)) { @@ -254,7 +255,7 @@ const RegType* RegTypeCache::FindClass(mirror::Class* klass, bool precise) const } const RegType* RegTypeCache::InsertClass(const StringPiece& descriptor, - mirror::Class* klass, + ObjPtr klass, bool precise) { // No reference to the class was found, create new reference. DCHECK(FindClass(klass, precise) == nullptr); @@ -265,7 +266,9 @@ const RegType* RegTypeCache::InsertClass(const StringPiece& descriptor, return &AddEntry(reg_type); } -const RegType& RegTypeCache::FromClass(const char* descriptor, mirror::Class* klass, bool precise) { +const RegType& RegTypeCache::FromClass(const char* descriptor, + ObjPtr klass, + bool precise) { DCHECK(klass != nullptr); const RegType* reg_type = FindClass(klass, precise); if (reg_type == nullptr) { @@ -342,7 +345,7 @@ void RegTypeCache::CreatePrimitiveAndSmallConstantTypes() { // code cannot leak to other users. auto create_primitive_type_instance = [&](auto type) REQUIRES_SHARED(Locks::mutator_lock_) { using Type = typename decltype(type)::type; - mirror::Class* klass = nullptr; + ObjPtr klass = nullptr; // Try loading the class from linker. DCHECK(type.descriptor != nullptr); if (strlen(type.descriptor) > 0) { @@ -500,7 +503,7 @@ const UninitializedType& RegTypeCache::Uninitialized(const RegType& type, uint32 allocation_pc, entries_.size()); } else { - mirror::Class* klass = type.GetClass(); + ObjPtr klass = type.GetClass(); for (size_t i = primitive_count_; i < entries_.size(); i++) { const RegType* cur_entry = entries_[i]; if (cur_entry->IsUninitializedReference() && @@ -532,7 +535,7 @@ const RegType& RegTypeCache::FromUninitialized(const RegType& uninit_type) { } entry = new (&allocator_) UnresolvedReferenceType(descriptor, entries_.size()); } else { - mirror::Class* klass = uninit_type.GetClass(); + ObjPtr klass = uninit_type.GetClass(); if (uninit_type.IsUninitializedThisReference() && !klass->IsFinal()) { // For uninitialized "this reference" look for reference types that are not precise. for (size_t i = primitive_count_; i < entries_.size(); i++) { @@ -583,7 +586,7 @@ const UninitializedType& RegTypeCache::UninitializedThisArgument(const RegType& } entry = new (&allocator_) UnresolvedUninitializedThisRefType(descriptor, entries_.size()); } else { - mirror::Class* klass = type.GetClass(); + ObjPtr klass = type.GetClass(); for (size_t i = primitive_count_; i < entries_.size(); i++) { const RegType* cur_entry = entries_[i]; if (cur_entry->IsUninitializedThisReference() && cur_entry->GetClass() == klass) { @@ -647,7 +650,8 @@ const ConstantType& RegTypeCache::FromCat2ConstHi(int32_t value, bool precise) { return AddEntry(entry); } -const RegType& RegTypeCache::GetComponentType(const RegType& array, mirror::ClassLoader* loader) { +const RegType& RegTypeCache::GetComponentType(const RegType& array, + ObjPtr loader) { if (!array.IsArrayTypes()) { return Conflict(); } else if (array.IsUnresolvedTypes()) { @@ -655,7 +659,7 @@ const RegType& RegTypeCache::GetComponentType(const RegType& array, mirror::Clas const std::string descriptor(array.GetDescriptor().as_string()); return FromDescriptor(loader, descriptor.c_str() + 1, false); } else { - mirror::Class* klass = array.GetClass()->GetComponentType(); + ObjPtr klass = array.GetClass()->GetComponentType(); std::string temp; const char* descriptor = klass->GetDescriptor(&temp); if (klass->IsErroneous()) { diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h index b32dc115a77cff02f9f972301789a548c3739abd..d66822290172bb3032ddf7e5e06a2923f62fbfa4 100644 --- a/runtime/verifier/reg_type_cache.h +++ b/runtime/verifier/reg_type_cache.h @@ -74,16 +74,18 @@ class RegTypeCache { } static void ShutDown(); const art::verifier::RegType& GetFromId(uint16_t id) const; - const RegType& From(mirror::ClassLoader* loader, const char* descriptor, bool precise) + const RegType& From(ObjPtr loader, const char* descriptor, bool precise) REQUIRES_SHARED(Locks::mutator_lock_); // Find a RegType, returns null if not found. - const RegType* FindClass(mirror::Class* klass, bool precise) const + const RegType* FindClass(ObjPtr klass, bool precise) const REQUIRES_SHARED(Locks::mutator_lock_); // Insert a new class with a specified descriptor, must not already be in the cache. - const RegType* InsertClass(const StringPiece& descriptor, mirror::Class* klass, bool precise) + const RegType* InsertClass(const StringPiece& descriptor, + ObjPtr klass, + bool precise) REQUIRES_SHARED(Locks::mutator_lock_); // Get or insert a reg type for a description, klass, and precision. - const RegType& FromClass(const char* descriptor, mirror::Class* klass, bool precise) + const RegType& FromClass(const char* descriptor, ObjPtr klass, bool precise) REQUIRES_SHARED(Locks::mutator_lock_); const ConstantType& FromCat1Const(int32_t value, bool precise) REQUIRES_SHARED(Locks::mutator_lock_); @@ -91,7 +93,9 @@ class RegTypeCache { REQUIRES_SHARED(Locks::mutator_lock_); const ConstantType& FromCat2ConstHi(int32_t value, bool precise) REQUIRES_SHARED(Locks::mutator_lock_); - const RegType& FromDescriptor(mirror::ClassLoader* loader, const char* descriptor, bool precise) + const RegType& FromDescriptor(ObjPtr loader, + const char* descriptor, + bool precise) REQUIRES_SHARED(Locks::mutator_lock_); const RegType& FromUnresolvedMerge(const RegType& left, const RegType& right, @@ -146,7 +150,7 @@ class RegTypeCache { const ImpreciseConstType& IntConstant() REQUIRES_SHARED(Locks::mutator_lock_); const ImpreciseConstType& PosByteConstant() REQUIRES_SHARED(Locks::mutator_lock_); const ImpreciseConstType& PosShortConstant() REQUIRES_SHARED(Locks::mutator_lock_); - const RegType& GetComponentType(const RegType& array, mirror::ClassLoader* loader) + const RegType& GetComponentType(const RegType& array, ObjPtr loader) REQUIRES_SHARED(Locks::mutator_lock_); void Dump(std::ostream& os) REQUIRES_SHARED(Locks::mutator_lock_); const RegType& RegTypeFromPrimitiveType(Primitive::Type) const; @@ -158,7 +162,7 @@ class RegTypeCache { private: void FillPrimitiveAndSmallConstantTypes() REQUIRES_SHARED(Locks::mutator_lock_); - mirror::Class* ResolveClass(const char* descriptor, mirror::ClassLoader* loader) + ObjPtr ResolveClass(const char* descriptor, ObjPtr loader) REQUIRES_SHARED(Locks::mutator_lock_); bool MatchDescriptor(size_t idx, const StringPiece& descriptor, bool precise) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc index ea30e05487f85dbd4e2184062b43f05142049197..1bbf5a693a35374523fe9c32a771a934faa86078 100644 --- a/runtime/verifier/register_line.cc +++ b/runtime/verifier/register_line.cc @@ -148,7 +148,9 @@ std::string RegisterLine::Dump(MethodVerifier* verifier) const { result += StringPrintf("{%d},", monitor); } for (auto& pairs : reg_to_lock_depths_) { - result += StringPrintf("<%d -> %x>", pairs.first, pairs.second); + result += StringPrintf("<%d -> %" PRIx64 ">", + pairs.first, + static_cast(pairs.second)); } return result; } @@ -337,7 +339,7 @@ void RegisterLine::PushMonitor(MethodVerifier* verifier, uint32_t reg_idx, int32 if (!reg_type.IsReferenceTypes()) { verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-enter on non-object (" << reg_type << ")"; - } else if (monitors_.size() >= 32) { + } else if (monitors_.size() >= kMaxMonitorStackDepth) { verifier->Fail(VERIFY_ERROR_LOCKING); if (kDumpLockFailures) { VLOG(verifier) << "monitor-enter stack overflow while verifying " diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h index 18ad6b5d0c53d1e0b7140867fbdc202d68f65a23..9bb60bb7c51e1e43adf80745b1df5fc718b270a9 100644 --- a/runtime/verifier/register_line.h +++ b/runtime/verifier/register_line.h @@ -17,11 +17,13 @@ #ifndef ART_RUNTIME_VERIFIER_REGISTER_LINE_H_ #define ART_RUNTIME_VERIFIER_REGISTER_LINE_H_ +#include #include #include #include +#include "base/mutex.h" #include "base/safe_map.h" #include "base/scoped_arena_containers.h" @@ -61,8 +63,14 @@ enum class LockOp { // stack of entered monitors (identified by code unit offset). class RegisterLine { public: + using RegisterStackMask = uint32_t; // A map from register to a bit vector of indices into the monitors_ stack. - using RegToLockDepthsMap = ScopedArenaSafeMap; + using RegToLockDepthsMap = ScopedArenaSafeMap; + + // Maximum number of nested monitors to track before giving up and + // taking the slow path. + static constexpr size_t kMaxMonitorStackDepth = + std::numeric_limits::digits; // Create a register line of num_regs registers. static RegisterLine* Create(size_t num_regs, MethodVerifier* verifier); @@ -390,7 +398,7 @@ class RegisterLine { } bool SetRegToLockDepth(size_t reg, size_t depth) { - CHECK_LT(depth, 32u); + CHECK_LT(depth, kMaxMonitorStackDepth); if (IsSetLockDepth(reg, depth)) { return false; // Register already holds lock so locking twice is erroneous. } diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc index 4772e538aa11b8f4f15d721f86debbe859d6ab51..fb9197678192a4fd211e950fd54d17f96aa15336 100644 --- a/runtime/verifier/verifier_deps.cc +++ b/runtime/verifier/verifier_deps.cc @@ -20,11 +20,11 @@ #include "art_field-inl.h" #include "art_method-inl.h" +#include "base/indenter.h" #include "base/leb128.h" #include "base/stl_util.h" #include "compiler_callbacks.h" #include "dex/dex_file-inl.h" -#include "indenter.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "obj_ptr-inl.h" @@ -77,8 +77,8 @@ const VerifierDeps::DexFileDeps* VerifierDeps::GetDexFileDeps(const DexFile& dex static constexpr uint32_t kAccVdexAccessFlags = kAccPublic | kAccPrivate | kAccProtected | kAccStatic | kAccInterface; -template -uint16_t VerifierDeps::GetAccessFlags(T* element) { +template +uint16_t VerifierDeps::GetAccessFlags(Ptr element) { static_assert(kAccJavaFlagsMask == 0xFFFF, "Unexpected value of a constant"); if (element == nullptr) { return VerifierDeps::kUnresolvedMarker; @@ -277,7 +277,7 @@ bool VerifierDeps::IsInClassPath(ObjPtr klass) const { void VerifierDeps::AddClassResolution(const DexFile& dex_file, dex::TypeIndex type_idx, - mirror::Class* klass) { + ObjPtr klass) { DexFileDeps* dex_deps = GetDexFileDeps(dex_file); if (dex_deps == nullptr) { // This invocation is from verification of a dex file which is not being compiled. @@ -336,12 +336,13 @@ void VerifierDeps::AddMethodResolution(const DexFile& dex_file, dex_deps->methods_.insert(method_tuple); } -mirror::Class* VerifierDeps::FindOneClassPathBoundaryForInterface(mirror::Class* destination, - mirror::Class* source) const { +ObjPtr VerifierDeps::FindOneClassPathBoundaryForInterface( + ObjPtr destination, + ObjPtr source) const { DCHECK(destination->IsInterface()); DCHECK(IsInClassPath(destination)); Thread* thread = Thread::Current(); - mirror::Class* current = source; + ObjPtr current = source; // Record the classes that are at the boundary between the compiled DEX files and // the classpath. We will check those classes later to find one class that inherits // `destination`. @@ -367,7 +368,7 @@ mirror::Class* VerifierDeps::FindOneClassPathBoundaryForInterface(mirror::Class* int32_t iftable_count = source->GetIfTableCount(); ObjPtr iftable = source->GetIfTable(); for (int32_t i = 0; i < iftable_count; ++i) { - mirror::Class* itf = iftable->GetInterface(i); + ObjPtr itf = iftable->GetInterface(i); if (!IsInClassPath(itf)) { for (size_t j = 0; j < itf->NumDirectInterfaces(); ++j) { ObjPtr direct = mirror::Class::GetDirectInterface(thread, itf, j); @@ -383,7 +384,7 @@ mirror::Class* VerifierDeps::FindOneClassPathBoundaryForInterface(mirror::Class* // Find a boundary making `source` inherit from `destination`. We must find one. for (const ObjPtr& boundary : boundaries) { if (destination->IsAssignableFrom(boundary)) { - return boundary.Ptr(); + return boundary; } } LOG(FATAL) << "Should have found a classpath boundary"; @@ -391,8 +392,8 @@ mirror::Class* VerifierDeps::FindOneClassPathBoundaryForInterface(mirror::Class* } void VerifierDeps::AddAssignability(const DexFile& dex_file, - mirror::Class* destination, - mirror::Class* source, + ObjPtr destination, + ObjPtr source, bool is_strict, bool is_assignable) { // Test that the method is only called on reference types. @@ -429,8 +430,8 @@ void VerifierDeps::AddAssignability(const DexFile& dex_file, // Both types are arrays. Break down to component types and add recursively. // This helps filter out destinations from compiled DEX files (see below) // and deduplicate entries with the same canonical component type. - mirror::Class* destination_component = destination->GetComponentType(); - mirror::Class* source_component = source->GetComponentType(); + ObjPtr destination_component = destination->GetComponentType(); + ObjPtr source_component = source->GetComponentType(); // Only perform the optimization if both types are resolved which guarantees // that they linked successfully, as required at the top of this method. @@ -511,7 +512,7 @@ void VerifierDeps::MaybeRecordVerificationStatus(const DexFile& dex_file, void VerifierDeps::MaybeRecordClassResolution(const DexFile& dex_file, dex::TypeIndex type_idx, - mirror::Class* klass) { + ObjPtr klass) { VerifierDeps* thread_deps = GetThreadLocalVerifierDeps(); if (thread_deps != nullptr) { thread_deps->AddClassResolution(dex_file, type_idx, klass); @@ -537,8 +538,8 @@ void VerifierDeps::MaybeRecordMethodResolution(const DexFile& dex_file, } void VerifierDeps::MaybeRecordAssignability(const DexFile& dex_file, - mirror::Class* destination, - mirror::Class* source, + ObjPtr destination, + ObjPtr source, bool is_strict, bool is_assignable) { VerifierDeps* thread_deps = GetThreadLocalVerifierDeps(); @@ -858,12 +859,12 @@ bool VerifierDeps::ValidateDependencies(Handle class_loader // TODO: share that helper with other parts of the compiler that have // the same lookup pattern. -static mirror::Class* FindClassAndClearException(ClassLinker* class_linker, - Thread* self, - const char* name, - Handle class_loader) +static ObjPtr FindClassAndClearException(ClassLinker* class_linker, + Thread* self, + const char* name, + Handle class_loader) REQUIRES_SHARED(Locks::mutator_lock_) { - mirror::Class* result = class_linker->FindClass(self, name, class_loader); + ObjPtr result = class_linker->FindClass(self, name, class_loader); if (result == nullptr) { DCHECK(self->IsExceptionPending()); self->ClearException(); @@ -971,7 +972,7 @@ bool VerifierDeps::VerifyFields(Handle class_loader, std::string expected_decl_klass = entry.IsResolved() ? GetStringFromId(dex_file, entry.GetDeclaringClassIndex()) : dex_file.StringByTypeIdx(field_id.class_idx_); - mirror::Class* cls = FindClassAndClearException( + ObjPtr cls = FindClassAndClearException( class_linker, self, expected_decl_klass.c_str(), class_loader); if (cls == nullptr) { LOG(INFO) << "VerifierDeps: Could not resolve class " << expected_decl_klass; @@ -1034,7 +1035,7 @@ bool VerifierDeps::VerifyMethods(Handle class_loader, ? GetStringFromId(dex_file, entry.GetDeclaringClassIndex()) : dex_file.StringByTypeIdx(method_id.class_idx_); - mirror::Class* cls = FindClassAndClearException( + ObjPtr cls = FindClassAndClearException( class_linker, self, expected_decl_klass.c_str(), class_loader); if (cls == nullptr) { LOG(INFO) << "VerifierDeps: Could not resolve class " << expected_decl_klass; diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h index 94441da7e244aa522bd0aef389d0352ceb23dc49..0146b17020802c000f2da7abea09c903a793d935 100644 --- a/runtime/verifier/verifier_deps.h +++ b/runtime/verifier/verifier_deps.h @@ -75,7 +75,7 @@ class VerifierDeps { // If `klass` is null, the class is assumed unresolved. static void MaybeRecordClassResolution(const DexFile& dex_file, dex::TypeIndex type_idx, - mirror::Class* klass) + ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::verifier_deps_lock_); @@ -99,8 +99,8 @@ class VerifierDeps { // to `destination` as defined by RegType::AssignableFrom. `dex_file` is the // owner of the method for which MethodVerifier performed the assignability test. static void MaybeRecordAssignability(const DexFile& dex_file, - mirror::Class* destination, - mirror::Class* source, + ObjPtr destination, + ObjPtr source, bool is_strict, bool is_assignable) REQUIRES_SHARED(Locks::mutator_lock_) @@ -218,8 +218,8 @@ class VerifierDeps { // Finds the class in the classpath that makes `source` inherit` from `destination`. // Returns null if a class defined in the compiled DEX files, and assignable to // `source`, direclty inherits from `destination`. - mirror::Class* FindOneClassPathBoundaryForInterface(mirror::Class* destination, - mirror::Class* source) const + ObjPtr FindOneClassPathBoundaryForInterface(ObjPtr destination, + ObjPtr source) const REQUIRES_SHARED(Locks::mutator_lock_); // Returns the index of `str`. If it is defined in `dex_file_`, this is the dex @@ -234,8 +234,8 @@ class VerifierDeps { // Returns the bytecode access flags of `element` (bottom 16 bits), or // `kUnresolvedMarker` if `element` is null. - template - static uint16_t GetAccessFlags(T* element) + template + static uint16_t GetAccessFlags(Ptr element) REQUIRES_SHARED(Locks::mutator_lock_); // Returns a string ID of the descriptor of the declaring class of `element`, @@ -256,7 +256,7 @@ class VerifierDeps { void AddClassResolution(const DexFile& dex_file, dex::TypeIndex type_idx, - mirror::Class* klass) + ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::verifier_deps_lock_); @@ -273,8 +273,8 @@ class VerifierDeps { REQUIRES(!Locks::verifier_deps_lock_); void AddAssignability(const DexFile& dex_file, - mirror::Class* destination, - mirror::Class* source, + ObjPtr destination, + ObjPtr source, bool is_strict, bool is_assignable) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/verify_object.cc b/runtime/verify_object.cc index a031a07a94d8ed17879e372f8ad407b4c1c0b539..70ca13f913a4dedd876a6abb4333757f65063ebe 100644 --- a/runtime/verify_object.cc +++ b/runtime/verify_object.cc @@ -17,8 +17,8 @@ #include "verify_object-inl.h" #include "base/bit_utils.h" +#include "base/globals.h" #include "gc/heap.h" -#include "globals.h" #include "mirror/object-inl.h" #include "obj_ptr-inl.h" #include "runtime.h" diff --git a/runtime/verify_object.h b/runtime/verify_object.h index 5c665b34c454c9de18b0943a49faa119536bd362..cc288e7a889a956e8422810edfb26f26201bc467 100644 --- a/runtime/verify_object.h +++ b/runtime/verify_object.h @@ -63,6 +63,10 @@ static inline void VerifyObject(ObjPtr obj) NO_THREAD_SAFETY_ANA } } +inline constexpr VerifyObjectFlags RemoveThisFlags(VerifyObjectFlags flags) { + return static_cast(flags & ~kVerifyThis); +} + // Check that c.getClass() == c.getClass().getClass(). ALWAYS_INLINE bool VerifyClassClass(ObjPtr c) NO_THREAD_SAFETY_ANALYSIS; diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 31d688aa80e85400de5b9234381df61a345d9eb7..206418fbc6f38c30adc30e340b9af2c32d01dc85 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -23,13 +23,17 @@ #include #include +#include "base/enums.h" +#include "class_linker.h" #include "entrypoints/quick/quick_entrypoints_enum.h" +#include "entrypoints/runtime_asm_entrypoints.h" #include "hidden_api.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/throwable.h" #include "nativehelper/scoped_local_ref.h" #include "obj_ptr-inl.h" +#include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" @@ -52,16 +56,10 @@ jclass WellKnownClasses::java_lang_ClassLoader; jclass WellKnownClasses::java_lang_ClassNotFoundException; jclass WellKnownClasses::java_lang_Daemons; jclass WellKnownClasses::java_lang_Error; -jclass WellKnownClasses::java_lang_invoke_MethodHandle; -jclass WellKnownClasses::java_lang_invoke_VarHandle; jclass WellKnownClasses::java_lang_IllegalAccessError; jclass WellKnownClasses::java_lang_NoClassDefFoundError; jclass WellKnownClasses::java_lang_Object; jclass WellKnownClasses::java_lang_OutOfMemoryError; -jclass WellKnownClasses::java_lang_reflect_Constructor; -jclass WellKnownClasses::java_lang_reflect_Executable; -jclass WellKnownClasses::java_lang_reflect_Field; -jclass WellKnownClasses::java_lang_reflect_Method; jclass WellKnownClasses::java_lang_reflect_Parameter; jclass WellKnownClasses::java_lang_reflect_Parameter__array; jclass WellKnownClasses::java_lang_reflect_Proxy; @@ -75,7 +73,6 @@ jclass WellKnownClasses::java_lang_ThreadGroup; jclass WellKnownClasses::java_lang_Throwable; jclass WellKnownClasses::java_nio_ByteBuffer; jclass WellKnownClasses::java_nio_DirectByteBuffer; -jclass WellKnownClasses::java_util_ArrayList; jclass WellKnownClasses::java_util_Collections; jclass WellKnownClasses::java_util_function_Consumer; jclass WellKnownClasses::libcore_reflect_AnnotationFactory; @@ -91,25 +88,22 @@ jmethodID WellKnownClasses::java_lang_Byte_valueOf; jmethodID WellKnownClasses::java_lang_Character_valueOf; jmethodID WellKnownClasses::java_lang_ClassLoader_loadClass; jmethodID WellKnownClasses::java_lang_ClassNotFoundException_init; -jmethodID WellKnownClasses::java_lang_Daemons_requestHeapTrim; jmethodID WellKnownClasses::java_lang_Daemons_start; jmethodID WellKnownClasses::java_lang_Daemons_stop; jmethodID WellKnownClasses::java_lang_Double_valueOf; jmethodID WellKnownClasses::java_lang_Float_valueOf; jmethodID WellKnownClasses::java_lang_Integer_valueOf; -jmethodID WellKnownClasses::java_lang_invoke_MethodHandle_invoke; -jmethodID WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact; jmethodID WellKnownClasses::java_lang_invoke_MethodHandles_lookup; jmethodID WellKnownClasses::java_lang_invoke_MethodHandles_Lookup_findConstructor; jmethodID WellKnownClasses::java_lang_Long_valueOf; jmethodID WellKnownClasses::java_lang_ref_FinalizerReference_add; jmethodID WellKnownClasses::java_lang_ref_ReferenceQueue_add; jmethodID WellKnownClasses::java_lang_reflect_Parameter_init; +jmethodID WellKnownClasses::java_lang_reflect_Proxy_init; jmethodID WellKnownClasses::java_lang_reflect_Proxy_invoke; jmethodID WellKnownClasses::java_lang_Runtime_nativeLoad; jmethodID WellKnownClasses::java_lang_Short_valueOf; jmethodID WellKnownClasses::java_lang_String_charAt; -jmethodID WellKnownClasses::java_lang_System_runFinalization = nullptr; jmethodID WellKnownClasses::java_lang_Thread_dispatchUncaughtException; jmethodID WellKnownClasses::java_lang_Thread_init; jmethodID WellKnownClasses::java_lang_Thread_run; @@ -145,8 +139,6 @@ jfieldID WellKnownClasses::java_lang_Throwable_detailMessage; jfieldID WellKnownClasses::java_lang_Throwable_stackTrace; jfieldID WellKnownClasses::java_lang_Throwable_stackState; jfieldID WellKnownClasses::java_lang_Throwable_suppressedExceptions; -jfieldID WellKnownClasses::java_lang_reflect_Executable_artMethod; -jfieldID WellKnownClasses::java_lang_reflect_Proxy_h; jfieldID WellKnownClasses::java_nio_ByteBuffer_address; jfieldID WellKnownClasses::java_nio_ByteBuffer_hb; jfieldID WellKnownClasses::java_nio_ByteBuffer_isReadOnly; @@ -154,8 +146,6 @@ jfieldID WellKnownClasses::java_nio_ByteBuffer_limit; jfieldID WellKnownClasses::java_nio_ByteBuffer_offset; jfieldID WellKnownClasses::java_nio_DirectByteBuffer_capacity; jfieldID WellKnownClasses::java_nio_DirectByteBuffer_effectiveDirectAddress; -jfieldID WellKnownClasses::java_util_ArrayList_array; -jfieldID WellKnownClasses::java_util_ArrayList_size; jfieldID WellKnownClasses::java_util_Collections_EMPTY_LIST; jfieldID WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT; jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data; @@ -236,19 +226,28 @@ static jmethodID CachePrimitiveBoxingMethod(JNIEnv* env, char prim_name, const c V(java_lang_String_init_StringBuilder, "(Ljava/lang/StringBuilder;)V", newStringFromStringBuilder, "newStringFromStringBuilder", "(Ljava/lang/StringBuilder;)Ljava/lang/String;", NewStringFromStringBuilder) \ #define STATIC_STRING_INIT(init_runtime_name, init_signature, new_runtime_name, ...) \ - static ArtMethod* init_runtime_name; \ - static ArtMethod* new_runtime_name; + static ArtMethod* init_runtime_name = nullptr; \ + static ArtMethod* new_runtime_name = nullptr; STRING_INIT_LIST(STATIC_STRING_INIT) #undef STATIC_STRING_INIT -void WellKnownClasses::InitStringInit(JNIEnv* env) { - ScopedObjectAccess soa(Thread::Current()); - #define LOAD_STRING_INIT(init_runtime_name, init_signature, new_runtime_name, \ - new_java_name, new_signature, ...) \ - init_runtime_name = jni::DecodeArtMethod( \ - CacheMethod(env, java_lang_String, false, "", init_signature)); \ - new_runtime_name = jni::DecodeArtMethod( \ - CacheMethod(env, java_lang_StringFactory, true, new_java_name, new_signature)); +void WellKnownClasses::InitStringInit(ObjPtr string_class, + ObjPtr string_builder_class) { + PointerSize p_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); + auto find_method = [p_size](ObjPtr klass, + const char* name, + const char* sig, + bool expext_static) REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* ret = klass->FindClassMethod(name, sig, p_size); + CHECK(ret != nullptr); + CHECK_EQ(expext_static, ret->IsStatic()); + return ret; + }; + + #define LOAD_STRING_INIT(init_runtime_name, init_signature, new_runtime_name, \ + new_java_name, new_signature, ...) \ + init_runtime_name = find_method(string_class, "", init_signature, false); \ + new_runtime_name = find_method(string_builder_class, new_java_name, new_signature, true); STRING_INIT_LIST(LOAD_STRING_INIT) #undef LOAD_STRING_INIT } @@ -257,6 +256,7 @@ void Thread::InitStringEntryPoints() { QuickEntryPoints* qpoints = &tlsPtr_.quick_entrypoints; #define SET_ENTRY_POINT(init_runtime_name, init_signature, new_runtime_name, \ new_java_name, new_signature, entry_point_name) \ + DCHECK(!Runtime::Current()->IsStarted() || (new_runtime_name) != nullptr); \ qpoints->p ## entry_point_name = reinterpret_cast(new_runtime_name); STRING_INIT_LIST(SET_ENTRY_POINT) #undef SET_ENTRY_POINT @@ -265,7 +265,9 @@ void Thread::InitStringEntryPoints() { ArtMethod* WellKnownClasses::StringInitToStringFactory(ArtMethod* string_init) { #define TO_STRING_FACTORY(init_runtime_name, init_signature, new_runtime_name, \ new_java_name, new_signature, entry_point_name) \ + DCHECK((init_runtime_name) != nullptr); \ if (string_init == (init_runtime_name)) { \ + DCHECK((new_runtime_name) != nullptr); \ return (new_runtime_name); \ } STRING_INIT_LIST(TO_STRING_FACTORY) @@ -313,13 +315,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_OutOfMemoryError = CacheClass(env, "java/lang/OutOfMemoryError"); java_lang_Error = CacheClass(env, "java/lang/Error"); java_lang_IllegalAccessError = CacheClass(env, "java/lang/IllegalAccessError"); - java_lang_invoke_MethodHandle = CacheClass(env, "java/lang/invoke/MethodHandle"); - java_lang_invoke_VarHandle = CacheClass(env, "java/lang/invoke/VarHandle"); java_lang_NoClassDefFoundError = CacheClass(env, "java/lang/NoClassDefFoundError"); - java_lang_reflect_Constructor = CacheClass(env, "java/lang/reflect/Constructor"); - java_lang_reflect_Executable = CacheClass(env, "java/lang/reflect/Executable"); - java_lang_reflect_Field = CacheClass(env, "java/lang/reflect/Field"); - java_lang_reflect_Method = CacheClass(env, "java/lang/reflect/Method"); java_lang_reflect_Parameter = CacheClass(env, "java/lang/reflect/Parameter"); java_lang_reflect_Parameter__array = CacheClass(env, "[Ljava/lang/reflect/Parameter;"); java_lang_reflect_Proxy = CacheClass(env, "java/lang/reflect/Proxy"); @@ -333,7 +329,6 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_Throwable = CacheClass(env, "java/lang/Throwable"); java_nio_ByteBuffer = CacheClass(env, "java/nio/ByteBuffer"); java_nio_DirectByteBuffer = CacheClass(env, "java/nio/DirectByteBuffer"); - java_util_ArrayList = CacheClass(env, "java/util/ArrayList"); java_util_Collections = CacheClass(env, "java/util/Collections"); java_util_function_Consumer = CacheClass(env, "java/util/function/Consumer"); libcore_reflect_AnnotationFactory = CacheClass(env, "libcore/reflect/AnnotationFactory"); @@ -347,11 +342,8 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_ClassNotFoundException_init = CacheMethod(env, java_lang_ClassNotFoundException, false, "", "(Ljava/lang/String;Ljava/lang/Throwable;)V"); java_lang_ClassLoader_loadClass = CacheMethod(env, java_lang_ClassLoader, false, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); - java_lang_Daemons_requestHeapTrim = CacheMethod(env, java_lang_Daemons, true, "requestHeapTrim", "()V"); java_lang_Daemons_start = CacheMethod(env, java_lang_Daemons, true, "start", "()V"); java_lang_Daemons_stop = CacheMethod(env, java_lang_Daemons, true, "stop", "()V"); - java_lang_invoke_MethodHandle_invoke = CacheMethod(env, java_lang_invoke_MethodHandle, false, "invoke", "([Ljava/lang/Object;)Ljava/lang/Object;"); - java_lang_invoke_MethodHandle_invokeExact = CacheMethod(env, java_lang_invoke_MethodHandle, false, "invokeExact", "([Ljava/lang/Object;)Ljava/lang/Object;"); java_lang_invoke_MethodHandles_lookup = CacheMethod(env, "java/lang/invoke/MethodHandles", true, "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;"); java_lang_invoke_MethodHandles_Lookup_findConstructor = CacheMethod(env, "java/lang/invoke/MethodHandles$Lookup", false, "findConstructor", "(Ljava/lang/Class;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;"); @@ -395,7 +387,6 @@ void WellKnownClasses::Init(JNIEnv* env) { 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_reflect_Executable_artMethod = CacheField(env, java_lang_reflect_Executable, false, "artMethod", "J"); 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"); @@ -403,8 +394,6 @@ void WellKnownClasses::Init(JNIEnv* env) { java_nio_ByteBuffer_offset = CacheField(env, java_nio_ByteBuffer, false, "offset", "I"); 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_ArrayList_array = CacheField(env, java_util_ArrayList, false, "elementData", "[Ljava/lang/Object;"); - java_util_ArrayList_size = CacheField(env, java_util_ArrayList, false, "size", "I"); 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"); @@ -420,9 +409,6 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_Integer_valueOf = CachePrimitiveBoxingMethod(env, 'I', "java/lang/Integer"); java_lang_Long_valueOf = CachePrimitiveBoxingMethod(env, 'J', "java/lang/Long"); java_lang_Short_valueOf = CachePrimitiveBoxingMethod(env, 'S', "java/lang/Short"); - - InitStringInit(env); - Thread::Current()->InitStringEntryPoints(); } void WellKnownClasses::LateInit(JNIEnv* env) { @@ -434,13 +420,18 @@ void WellKnownClasses::LateInit(JNIEnv* env) { CacheMethod(env, java_lang_Runtime.get(), true, "nativeLoad", "(Ljava/lang/String;Ljava/lang/ClassLoader;)" "Ljava/lang/String;"); + java_lang_reflect_Proxy_init = + CacheMethod(env, java_lang_reflect_Proxy, false, "", + "(Ljava/lang/reflect/InvocationHandler;)V"); + // This invariant is important since otherwise we will have the entire proxy invoke system + // confused. + DCHECK_NE( + jni::DecodeArtMethod(java_lang_reflect_Proxy_init)->GetEntryPointFromQuickCompiledCode(), + GetQuickInstrumentationEntryPoint()); java_lang_reflect_Proxy_invoke = CacheMethod(env, java_lang_reflect_Proxy, true, "invoke", "(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;" "[Ljava/lang/Object;)Ljava/lang/Object;"); - java_lang_reflect_Proxy_h = - CacheField(env, java_lang_reflect_Proxy, false, "h", - "Ljava/lang/reflect/InvocationHandler;"); } void WellKnownClasses::Clear() { @@ -462,15 +453,9 @@ void WellKnownClasses::Clear() { java_lang_Daemons = nullptr; java_lang_Error = nullptr; java_lang_IllegalAccessError = nullptr; - java_lang_invoke_MethodHandle = nullptr; - java_lang_invoke_VarHandle = nullptr; java_lang_NoClassDefFoundError = nullptr; java_lang_Object = nullptr; java_lang_OutOfMemoryError = nullptr; - java_lang_reflect_Constructor = nullptr; - java_lang_reflect_Executable = nullptr; - java_lang_reflect_Field = nullptr; - java_lang_reflect_Method = nullptr; java_lang_reflect_Parameter = nullptr; java_lang_reflect_Parameter__array = nullptr; java_lang_reflect_Proxy = nullptr; @@ -482,7 +467,6 @@ void WellKnownClasses::Clear() { java_lang_Thread = nullptr; java_lang_ThreadGroup = nullptr; java_lang_Throwable = nullptr; - java_util_ArrayList = nullptr; java_util_Collections = nullptr; java_nio_ByteBuffer = nullptr; java_nio_DirectByteBuffer = nullptr; @@ -499,25 +483,22 @@ void WellKnownClasses::Clear() { java_lang_Character_valueOf = nullptr; java_lang_ClassLoader_loadClass = nullptr; java_lang_ClassNotFoundException_init = nullptr; - java_lang_Daemons_requestHeapTrim = nullptr; java_lang_Daemons_start = nullptr; java_lang_Daemons_stop = nullptr; java_lang_Double_valueOf = nullptr; java_lang_Float_valueOf = nullptr; java_lang_Integer_valueOf = nullptr; - java_lang_invoke_MethodHandle_invoke = nullptr; - java_lang_invoke_MethodHandle_invokeExact = nullptr; java_lang_invoke_MethodHandles_lookup = nullptr; java_lang_invoke_MethodHandles_Lookup_findConstructor = nullptr; java_lang_Long_valueOf = nullptr; java_lang_ref_FinalizerReference_add = nullptr; java_lang_ref_ReferenceQueue_add = nullptr; java_lang_reflect_Parameter_init = nullptr; + java_lang_reflect_Proxy_init = nullptr; java_lang_reflect_Proxy_invoke = nullptr; java_lang_Runtime_nativeLoad = nullptr; java_lang_Short_valueOf = nullptr; java_lang_String_charAt = nullptr; - java_lang_System_runFinalization = nullptr; java_lang_Thread_dispatchUncaughtException = nullptr; java_lang_Thread_init = nullptr; java_lang_Thread_run = nullptr; @@ -534,8 +515,7 @@ void WellKnownClasses::Clear() { dalvik_system_DexFile_fileName = nullptr; dalvik_system_DexPathList_dexElements = nullptr; dalvik_system_DexPathList__Element_dexFile = nullptr; - java_lang_reflect_Executable_artMethod = nullptr; - java_lang_reflect_Proxy_h = nullptr; + dalvik_system_VMRuntime_nonSdkApiUsageConsumer = nullptr; java_lang_Thread_daemon = nullptr; java_lang_Thread_group = nullptr; java_lang_Thread_lock = nullptr; @@ -560,8 +540,6 @@ void WellKnownClasses::Clear() { java_nio_ByteBuffer_offset = nullptr; java_nio_DirectByteBuffer_capacity = nullptr; java_nio_DirectByteBuffer_effectiveDirectAddress = nullptr; - java_util_ArrayList_array = nullptr; - java_util_ArrayList_size = nullptr; java_util_Collections_EMPTY_LIST = nullptr; libcore_util_EmptyArray_STACK_TRACE_ELEMENT = nullptr; org_apache_harmony_dalvik_ddmc_Chunk_data = nullptr; diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index 7b1a2943d2174b226cf083e78489276654629f9b..ce5ab1df84748fa94e70964cc7ae7887afb32b09 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -40,6 +40,9 @@ struct WellKnownClasses { static void Clear(); + static void InitStringInit(ObjPtr string_class, + ObjPtr string_builder_class) + REQUIRES_SHARED(Locks::mutator_lock_); static ArtMethod* StringInitToStringFactory(ArtMethod* method); static uint32_t StringInitToEntryPoint(ArtMethod* method); @@ -63,15 +66,9 @@ struct WellKnownClasses { static jclass java_lang_Daemons; static jclass java_lang_Error; static jclass java_lang_IllegalAccessError; - static jclass java_lang_invoke_MethodHandle; - static jclass java_lang_invoke_VarHandle; static jclass java_lang_NoClassDefFoundError; static jclass java_lang_Object; static jclass java_lang_OutOfMemoryError; - static jclass java_lang_reflect_Constructor; - static jclass java_lang_reflect_Executable; - static jclass java_lang_reflect_Field; - static jclass java_lang_reflect_Method; static jclass java_lang_reflect_Parameter; static jclass java_lang_reflect_Parameter__array; static jclass java_lang_reflect_Proxy; @@ -83,7 +80,6 @@ struct WellKnownClasses { static jclass java_lang_Thread; static jclass java_lang_ThreadGroup; static jclass java_lang_Throwable; - static jclass java_util_ArrayList; static jclass java_util_Collections; static jclass java_util_function_Consumer; static jclass java_nio_ByteBuffer; @@ -101,25 +97,22 @@ struct WellKnownClasses { static jmethodID java_lang_Character_valueOf; static jmethodID java_lang_ClassLoader_loadClass; static jmethodID java_lang_ClassNotFoundException_init; - static jmethodID java_lang_Daemons_requestHeapTrim; static jmethodID java_lang_Daemons_start; static jmethodID java_lang_Daemons_stop; static jmethodID java_lang_Double_valueOf; static jmethodID java_lang_Float_valueOf; static jmethodID java_lang_Integer_valueOf; - static jmethodID java_lang_invoke_MethodHandle_invoke; - static jmethodID java_lang_invoke_MethodHandle_invokeExact; static jmethodID java_lang_invoke_MethodHandles_lookup; static jmethodID java_lang_invoke_MethodHandles_Lookup_findConstructor; static jmethodID java_lang_Long_valueOf; static jmethodID java_lang_ref_FinalizerReference_add; static jmethodID java_lang_ref_ReferenceQueue_add; static jmethodID java_lang_reflect_Parameter_init; + static jmethodID java_lang_reflect_Proxy_init; static jmethodID java_lang_reflect_Proxy_invoke; static jmethodID java_lang_Runtime_nativeLoad; static jmethodID java_lang_Short_valueOf; static jmethodID java_lang_String_charAt; - static jmethodID java_lang_System_runFinalization; static jmethodID java_lang_Thread_dispatchUncaughtException; static jmethodID java_lang_Thread_init; static jmethodID java_lang_Thread_run; @@ -138,8 +131,6 @@ struct WellKnownClasses { static jfieldID dalvik_system_DexPathList_dexElements; static jfieldID dalvik_system_DexPathList__Element_dexFile; static jfieldID dalvik_system_VMRuntime_nonSdkApiUsageConsumer; - static jfieldID java_lang_reflect_Executable_artMethod; - static jfieldID java_lang_reflect_Proxy_h; static jfieldID java_lang_Thread_daemon; static jfieldID java_lang_Thread_group; static jfieldID java_lang_Thread_lock; @@ -165,17 +156,12 @@ struct WellKnownClasses { static jfieldID java_nio_DirectByteBuffer_capacity; static jfieldID java_nio_DirectByteBuffer_effectiveDirectAddress; - static jfieldID java_util_ArrayList_array; - static jfieldID java_util_ArrayList_size; 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; static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_type; - - private: - static void InitStringInit(JNIEnv* env); }; } // namespace art diff --git a/runtime/write_barrier-inl.h b/runtime/write_barrier-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..af8c1be828674cd79953927ce6fb457a83fe322a --- /dev/null +++ b/runtime/write_barrier-inl.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_WRITE_BARRIER_INL_H_ +#define ART_RUNTIME_WRITE_BARRIER_INL_H_ + +#include "write_barrier.h" + +#include "gc/accounting/card_table-inl.h" +#include "gc/heap.h" +#include "obj_ptr-inl.h" +#include "runtime.h" + +namespace art { + +template +inline void WriteBarrier::ForFieldWrite(ObjPtr dst, + MemberOffset offset ATTRIBUTE_UNUSED, + ObjPtr new_value) { + if (kNullCheck == kWithNullCheck && new_value == nullptr) { + return; + } + DCHECK(new_value != nullptr); + GetCardTable()->MarkCard(dst.Ptr()); +} + +inline void WriteBarrier::ForArrayWrite(ObjPtr dst, + int start_offset ATTRIBUTE_UNUSED, + size_t length ATTRIBUTE_UNUSED) { + GetCardTable()->MarkCard(dst.Ptr()); +} + +inline void WriteBarrier::ForEveryFieldWrite(ObjPtr obj) { + GetCardTable()->MarkCard(obj.Ptr()); +} + +inline gc::accounting::CardTable* WriteBarrier::GetCardTable() { + return Runtime::Current()->GetHeap()->GetCardTable(); +} + +} // namespace art + +#endif // ART_RUNTIME_WRITE_BARRIER_INL_H_ diff --git a/runtime/write_barrier.h b/runtime/write_barrier.h new file mode 100644 index 0000000000000000000000000000000000000000..112154e14afb7c1ca321975086c8eccd7cc38435 --- /dev/null +++ b/runtime/write_barrier.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_WRITE_BARRIER_H_ +#define ART_RUNTIME_WRITE_BARRIER_H_ + +#include "base/macros.h" + +namespace art { + +namespace gc { +namespace accounting { +class CardTable; +} // namespace accounting +} // namespace gc + +class WriteBarrier { + public: + enum NullCheck { + kWithoutNullCheck, + kWithNullCheck, + }; + + // Must be called if a reference field of an Object in the heap changes, and before any GC + // safe-point. The call is not needed if null is stored in the field. + template + ALWAYS_INLINE static void ForFieldWrite(ObjPtr dst, + MemberOffset offset ATTRIBUTE_UNUSED, + ObjPtr new_value ATTRIBUTE_UNUSED) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Must be called if a reference field of an ObjectArray in the heap changes, and before any GC + // safe-point. The call is not needed if null is stored in the field. + ALWAYS_INLINE static void ForArrayWrite(ObjPtr dst, + int start_offset ATTRIBUTE_UNUSED, + size_t length ATTRIBUTE_UNUSED) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Write barrier for every reference field in an object. + ALWAYS_INLINE static void ForEveryFieldWrite(ObjPtr obj) + REQUIRES_SHARED(Locks::mutator_lock_); + + private: + ALWAYS_INLINE static gc::accounting::CardTable* GetCardTable(); +}; + +} // namespace art + +#endif // ART_RUNTIME_WRITE_BARRIER_H_ diff --git a/sigchainlib/Android.bp b/sigchainlib/Android.bp index 1f490cd3b941359472bc7aeb4b62c718b2f0ed40..a151d7a6bca24d7e851de1399b8def949c338566 100644 --- a/sigchainlib/Android.bp +++ b/sigchainlib/Android.bp @@ -35,7 +35,7 @@ cc_library { }, android: { - shared_libs: ["liblog"], + whole_static_libs: ["libasync_safe"], }, }, // Sigchainlib is whole-statically linked into binaries. For Android.mk-based binaries, @@ -56,7 +56,7 @@ cc_library_static { srcs: ["sigchain_dummy.cc"], target: { android: { - shared_libs: ["liblog"], + whole_static_libs: ["libasync_safe"], }, }, } diff --git a/sigchainlib/log.h b/sigchainlib/log.h new file mode 100644 index 0000000000000000000000000000000000000000..d689930c1e98613021400e45ee0f5a02c8b75f34 --- /dev/null +++ b/sigchainlib/log.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_SIGCHAINLIB_LOG_H_ +#define ART_SIGCHAINLIB_LOG_H_ + +#if defined(__ANDROID__) + +#include + +#define log(...) async_safe_format_log(ANDROID_LOG_ERROR, "libsigchain", __VA_ARGS__) +#define fatal async_safe_fatal + +#else + +#include +#include + +static void log(const char* format, ...) { + va_list ap; + va_start(ap, format); + vprintf(format, ap); + va_end(ap); + + printf("\n"); +} + +#define fatal(...) log(__VA_ARGS__); abort() + +#endif + +#endif // ART_SIGCHAINLIB_LOG_H_ diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc index 61346b1487485743ed71e6f50e80e61cb44cb050..2e5f46ca69ae2c4aa93ab96cdb9ac33ea384c701 100644 --- a/sigchainlib/sigchain.cc +++ b/sigchainlib/sigchain.cc @@ -14,13 +14,6 @@ * limitations under the License. */ -#ifdef ART_TARGET_ANDROID -#include -#else -#include -#include -#endif - #include #include #include @@ -35,6 +28,7 @@ #include #include +#include "log.h" #include "sigchain.h" #if defined(__APPLE__) @@ -65,22 +59,7 @@ // doesn't have SA_RESTART, and raise the signal to avoid restarting syscalls that are // expected to be interrupted? -static void log(const char* format, ...) { - char buf[256]; - va_list ap; - va_start(ap, format); - vsnprintf(buf, sizeof(buf), format, ap); -#ifdef ART_TARGET_ANDROID - __android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf); -#else - std::cout << buf << "\n"; -#endif - va_end(ap); -} - -#define fatal(...) log(__VA_ARGS__); abort() - -#if defined(__BIONIC__) && !defined(__LP64__) +#if defined(__BIONIC__) && !defined(__LP64__) && !defined(__mips__) static int sigismember(const sigset64_t* sigset, int signum) { return sigismember64(sigset, signum); } @@ -223,7 +202,9 @@ class SignalChain { SigactionType result; result.sa_flags = action_.sa_flags; result.sa_handler = action_.sa_handler; +#if defined(SA_RESTORER) result.sa_restorer = action_.sa_restorer; +#endif memcpy(&result.sa_mask, &action_.sa_mask, std::min(sizeof(action_.sa_mask), sizeof(result.sa_mask))); return result; @@ -237,7 +218,9 @@ class SignalChain { } else { action_.sa_flags = new_action->sa_flags; action_.sa_handler = new_action->sa_handler; +#if defined(SA_RESTORER) action_.sa_restorer = new_action->sa_restorer; +#endif sigemptyset(&action_.sa_mask); memcpy(&action_.sa_mask, &new_action->sa_mask, std::min(sizeof(action_.sa_mask), sizeof(new_action->sa_mask))); diff --git a/sigchainlib/sigchain_dummy.cc b/sigchainlib/sigchain_dummy.cc index edce965e33b5089d46bcaa5e4823647ebd730814..c2745309876aaa2c3d342c83e838b8224191d122 100644 --- a/sigchainlib/sigchain_dummy.cc +++ b/sigchainlib/sigchain_dummy.cc @@ -17,13 +17,7 @@ #include #include -#ifdef ART_TARGET_ANDROID -#include -#else -#include -#include -#endif - +#include "log.h" #include "sigchain.h" #define ATTRIBUTE_UNUSED __attribute__((__unused__)) @@ -33,19 +27,6 @@ #pragma GCC diagnostic ignored "-Wunknown-pragmas" #pragma GCC diagnostic ignored "-Wmissing-noreturn" -static void log(const char* format, ...) { - char buf[256]; - va_list ap; - va_start(ap, format); - vsnprintf(buf, sizeof(buf), format, ap); -#ifdef ART_TARGET_ANDROID - __android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf); -#else - std::cout << buf << "\n"; -#endif - va_end(ap); -} - namespace art { extern "C" void EnsureFrontOfChain(int signal ATTRIBUTE_UNUSED) { diff --git a/sigchainlib/sigchain_test.cc b/sigchainlib/sigchain_test.cc index 9f338ad3a2c900a627cc2f95e9c2a7b50ebde941..9584ded65f5dfa2218008858b165c8a776e8734f 100644 --- a/sigchainlib/sigchain_test.cc +++ b/sigchainlib/sigchain_test.cc @@ -50,7 +50,7 @@ static int sigismember64(sigset64_t* set, int member) { static int RealSigprocmask(int how, const sigset64_t* new_sigset, sigset64_t* old_sigset) { // glibc's sigset_t is overly large, so sizeof(*new_sigset) doesn't work. - return syscall(__NR_rt_sigprocmask, how, new_sigset, old_sigset, 8); + return syscall(__NR_rt_sigprocmask, how, new_sigset, old_sigset, NSIG/8); } class SigchainTest : public ::testing::Test { @@ -70,7 +70,7 @@ class SigchainTest : public ::testing::Test { }; -static void TestSignalBlocking(std::function fn) { +static void TestSignalBlocking(const std::function& fn) { // Unblock SIGSEGV, make sure it stays unblocked. sigset64_t mask; sigemptyset64(&mask); diff --git a/simulator/Android.bp b/simulator/Android.bp index 74b5a900b454fdbd70c90a9663821ec49f2a736a..86904264664afda6352db6ee9ea702e3a77f4d95 100644 --- a/simulator/Android.bp +++ b/simulator/Android.bp @@ -44,6 +44,7 @@ art_cc_library { defaults: ["libart_simulator_defaults"], shared_libs: [ "libart", + "libartbase", "libvixl-arm64", ], } @@ -56,6 +57,7 @@ art_cc_library { ], shared_libs: [ "libartd", + "libartbased", "libvixld-arm64", ], } @@ -80,6 +82,7 @@ art_cc_library { name: "libart-simulator-container", defaults: ["libart_simulator_container_defaults"], shared_libs: [ + "libartbase", "libart", ], } @@ -91,6 +94,7 @@ art_cc_library { "libart_simulator_container_defaults", ], shared_libs: [ + "libartbased", "libartd", ], } diff --git a/simulator/code_simulator_container.cc b/simulator/code_simulator_container.cc index 9f52b320f2b9c9d498f4aa3dbf7289a983a14a15..3206bc784411adf3560cf846cf42ad782060e46e 100644 --- a/simulator/code_simulator_container.cc +++ b/simulator/code_simulator_container.cc @@ -18,9 +18,9 @@ #include "code_simulator_container.h" +#include "base/globals.h" #include "base/logging.h" // For VLOG. #include "code_simulator.h" -#include "globals.h" namespace art { diff --git a/test.py b/test.py index 047d8125fbd0e7f0ccb7e7021cadebb86f5cbc50..bc15736bd888c09061c31bf252d3adcfed4dd59b 100755 --- a/test.py +++ b/test.py @@ -64,11 +64,8 @@ if options.gtest or not options.run_test: build_command = 'make' build_command += ' -j' + str(options.n_threads) - build_command += ' -C ' + ANDROID_BUILD_TOP build_command += ' ' + build_target - # Add 'dist' to avoid Jack issues b/36169180. - build_command += ' dist' print build_command diff --git a/test/001-HelloWorld/src/Main.java b/test/001-HelloWorld/src/Main.java index 401e8529cdce6bc99c9f3e92b655d290f709c52f..1ef62895596b3aa677ae966104bcf4b6290c61fe 100644 --- a/test/001-HelloWorld/src/Main.java +++ b/test/001-HelloWorld/src/Main.java @@ -14,37 +14,8 @@ * limitations under the License. */ -import java.util.concurrent.*; - -interface I { -} - -class A implements I { - static int x = (int)(10*Math.random()); // Suppress initialization. -} - public class Main { - public static void main(String[] args) throws Exception { - - final CountDownLatch first = new CountDownLatch(1); - final CountDownLatch second = new CountDownLatch(1); - - new Thread(new Runnable() { - public void run() { - try { - synchronized(I.class) { - first.countDown(); - second.await(); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - }).start(); - - first.await(); - new A(); - second.countDown(); - System.out.println("Hello, world!"); + public static void main(String[] args) { + System.out.println("Hello, world!"); } } diff --git a/test/003-omnibus-opcodes/build b/test/003-omnibus-opcodes/build index dba3549b1acdb51ec13ce6a89a9d94aff6b00ac8..c2e611259bee7ae740ab6102d681255030ca91ca 100644 --- a/test/003-omnibus-opcodes/build +++ b/test/003-omnibus-opcodes/build @@ -17,20 +17,20 @@ # Stop if something fails. set -e -mkdir classes -${JAVAC} -d classes `find src -name '*.java'` -rm classes/UnresClass.class -${JAVAC} -d classes `find src2 -name '*.java'` +export ORIGINAL_JAVAC="$JAVAC" -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - ${JACK} --import classes.jill.jar --output-dex . -else - if [ ${NEED_DEX} = "true" ]; then - ${DX} -JXmx256m --debug --dex --output=classes.dex classes - fi -fi +# Wrapper function for javac which invokes the compiler and applies +# additional setup steps for the test. +function javac_wrapper { + set -e # Stop on error - the caller script may not have this set. -if [ ${NEED_DEX} = "true" ]; then - zip $TEST_NAME.jar classes.dex -fi + $ORIGINAL_JAVAC "$@" + rm -f classes/UnresClass.class +} + +export -f javac_wrapper +export JAVAC=javac_wrapper + +###################################################################### + +./default-build "$@" diff --git a/test/004-JniTest/build b/test/004-JniTest/build index e563d734c26b5672533dbc0c67fb048dd9c518ba..a786b8bc62d3526c6defdd5765dbf0559be0a424 100755 --- a/test/004-JniTest/build +++ b/test/004-JniTest/build @@ -23,16 +23,18 @@ # This enables the test to compile with vanilla RI javac and work on either ART or RI. # +# Stop on failure. +set -e + export ORIGINAL_JAVAC="$JAVAC" -# Delete CriticalNative.java, FastNative.java annotations after building the .class files. +# Wrapper function for javac which invokes the compiler and applies +# additional setup steps for the test. function javac_wrapper { + set -e # Stop on error - the caller script may not have this set. $ORIGINAL_JAVAC "$@" - local stat=$? - - [[ -d classes ]] && (find classes/dalvik -name '*.class' | xargs rm -rf) - - return $stat + # Delete CriticalNative.java, FastNative.java annotations after building the .class files. + find classes/dalvik -name '*.class' -exec rm {} \; } export -f javac_wrapper @@ -40,28 +42,6 @@ export JAVAC=javac_wrapper ###################################################################### -# Use the original dx with no extra magic or pessimizing flags. -# This ensures that any default optimizations that dx do would not break JNI. - -export ORIGINAL_DX="$DX" - -# Filter out --debug flag from dx. -function dx_wrapper { - local args=("$@") - local args_filtered=() - for i in "${args[@]}"; do - case "$i" in - --debug) - ;; - *) - args_filtered+=("$i") - ;; - esac - done - "$ORIGINAL_DX" "${args_filtered[@]}" -} - -export -f dx_wrapper -export DX=dx_wrapper - +# Use release mode to check optimizations do not break JNI. +export D8_FLAGS=--release ./default-build "$@" diff --git a/test/004-ReferenceMap/build b/test/004-ReferenceMap/build index 08987b556cdb9c4a231a1b7886c356cbf55fe56b..d928cd7daf878dc9b64a431e39148a0647890a49 100644 --- a/test/004-ReferenceMap/build +++ b/test/004-ReferenceMap/build @@ -17,10 +17,26 @@ # Stop if something fails. set -e -# The test relies on DEX file produced by javac+dx so keep building with them for now -# (see b/19467889) -mkdir classes -${JAVAC} -d classes `find src -name '*.java'` -${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex \ - --dump-width=1000 ${DX_FLAGS} classes -zip $TEST_NAME.jar classes.dex +# This test depends on the exact format of the DEX file. Since dx is deprecated, +# the classes.dex file is packaged as a test input. It was created with: +# +# $ javac -g -Xlint:-options -source 1.7 -target 1.7 -d classes src/Main.java +# $ dx --debug --dex --output=classes.dex classes + +# Wrapper function for javac which for this test does nothing as the +# test uses a pre-built DEX file. +function javac_wrapper { + # Nothing to compile, using dx generated classes.dex. + return 0 +} + +export -f javac_wrapper +export JAVAC=javac_wrapper + +# Do not invoke D8 for this test. +export D8=':' + +###################################################################### + +jar -cf classes.jar classes.dex +./default-build "$@" diff --git a/test/004-ReferenceMap/classes.dex b/test/004-ReferenceMap/classes.dex new file mode 100644 index 0000000000000000000000000000000000000000..993c077e436cbfd7f151113b3f80dd7c28bd098f Binary files /dev/null and b/test/004-ReferenceMap/classes.dex differ diff --git a/test/004-SignalTest/signaltest.cc b/test/004-SignalTest/signaltest.cc index 67118d5e63c9439513b3ca5390c1894c0a81a658..49fe369b619b1891a4d1c2f07c597810c1b90ce4 100644 --- a/test/004-SignalTest/signaltest.cc +++ b/test/004-SignalTest/signaltest.cc @@ -102,10 +102,14 @@ static struct sigaction oldaction; bool compare_sigaction(const struct sigaction* lhs, const struct sigaction* rhs) { // bionic's definition of `struct sigaction` has internal padding bytes, so we can't just do a // naive memcmp of the entire struct. +#if defined(SA_RESTORER) + if (lhs->sa_restorer != rhs->sa_restorer) { + return false; + } +#endif return memcmp(&lhs->sa_mask, &rhs->sa_mask, sizeof(lhs->sa_mask)) == 0 && lhs->sa_sigaction == rhs->sa_sigaction && - lhs->sa_flags == rhs->sa_flags && - lhs->sa_restorer == rhs->sa_restorer; + lhs->sa_flags == rhs->sa_flags; } extern "C" JNIEXPORT void JNICALL Java_Main_initSignalTest(JNIEnv*, jclass) { diff --git a/test/004-StackWalk/build b/test/004-StackWalk/build index 08987b556cdb9c4a231a1b7886c356cbf55fe56b..eeecbfcc40448652172541ea9f6e8760bb8dc86e 100644 --- a/test/004-StackWalk/build +++ b/test/004-StackWalk/build @@ -17,10 +17,25 @@ # Stop if something fails. set -e -# The test relies on DEX file produced by javac+dx so keep building with them for now -# (see b/19467889) -mkdir classes -${JAVAC} -d classes `find src -name '*.java'` -${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex \ - --dump-width=1000 ${DX_FLAGS} classes -zip $TEST_NAME.jar classes.dex +# This test depends on the exact format of the DEX file. Since dx is deprecated, +# the classes.dex file is packaged as a test input. It was created with: +# +# $ javac -g -Xlint:-options -source 1.7 -target 1.7 -d classes src/Main.java +# $ dx --debug --dex --output=classes.dex classes + +# Wrapper function for javac which for this test does nothing as the +# test uses a pre-built DEX file. +function javac_wrapper { + return 0 +} + +export -f javac_wrapper +export JAVAC=javac_wrapper + +# Do not invoke D8 for this test. +export D8=':' + +###################################################################### + +jar -cf classes.jar classes.dex +./default-build "$@" diff --git a/test/004-StackWalk/classes.dex b/test/004-StackWalk/classes.dex new file mode 100644 index 0000000000000000000000000000000000000000..ad452960c3cce1d76ce55916c02b5c584a498193 Binary files /dev/null and b/test/004-StackWalk/classes.dex differ diff --git a/test/004-ThreadStress/run b/test/004-ThreadStress/run index 8004036868070073f00e7dd162fbffa3992526a9..067e0d0407d5ac02d03ff159266b21fd9bf91b64 100755 --- a/test/004-ThreadStress/run +++ b/test/004-ThreadStress/run @@ -15,7 +15,29 @@ # limitations under the License. # Enable lock contention logging. -${RUN} --runtime-option -Xlockprofthreshold:10 "${@}" +if [[ "x$ART_DEFAULT_GC_TYPE" = xGSS ]]; then + # NonMovingAlloc operations fail an assertion with the Generational + # Semi-Space (GSS) collector (see b/72738921); disable them for now + # by explicitly assigning frequencies to operations when the GSS + # collector is used. + # + # Note: The trick to use command substitution to have comments within + # a multi-line command is from https://stackoverflow.com/a/12797512. + ${RUN} --runtime-option -Xlockprofthreshold:10 "${@}" Main \ + -oom:0.005 `# 1/200` \ + -sigquit:0.095 `# 19/200` \ + -alloc:0.225 `# 45/200` \ + -largealloc:0.05 `# 10/200` \ + -nonmovingalloc:0.0 `# 0/200` \ + -stacktrace:0.1 `# 20/200` \ + -exit:0.225 `# 45/200` \ + -sleep:0.125 `# 25/200` \ + -timedwait:0.05 `# 10/200` \ + -wait:0.075 `# 15/200` \ + -queuedwait:0.05 `# 10/200` +else + ${RUN} --runtime-option -Xlockprofthreshold:10 "${@}" +fi return_status1=$? # Run locks-only mode with stack-dump lock profiling. Reduce the number of total operations from diff --git a/test/004-ThreadStress/src-art/Main.java b/test/004-ThreadStress/src-art/Main.java index a1429346381ebd029108403376b52cb05b020466..3a89f4f166f19c7173647cfdcbda66bf0b7897ef 100644 --- a/test/004-ThreadStress/src-art/Main.java +++ b/test/004-ThreadStress/src-art/Main.java @@ -315,11 +315,9 @@ public class Main implements Runnable { Map frequencyMap = new HashMap(); frequencyMap.put(new OOM(), 0.005); // 1/200 frequencyMap.put(new SigQuit(), 0.095); // 19/200 - frequencyMap.put(new Alloc(), 0.225); // 45/200 + frequencyMap.put(new Alloc(), 0.2); // 40/200 frequencyMap.put(new LargeAlloc(), 0.05); // 10/200 - // TODO: NonMovingAlloc operations fail an assertion with the - // GSS collector (see b/72738921); disable them for now. - frequencyMap.put(new NonMovingAlloc(), 0.0); // 0/200 + frequencyMap.put(new NonMovingAlloc(), 0.025); // 5/200 frequencyMap.put(new StackTrace(), 0.1); // 20/200 frequencyMap.put(new Exit(), 0.225); // 45/200 frequencyMap.put(new Sleep(), 0.125); // 25/200 @@ -379,6 +377,8 @@ public class Main implements Runnable { op = new Alloc(); } else if (split[0].equals("-largealloc")) { op = new LargeAlloc(); + } else if (split[0].equals("-nonmovingalloc")) { + op = new NonMovingAlloc(); } else if (split[0].equals("-stacktrace")) { op = new StackTrace(); } else if (split[0].equals("-exit")) { diff --git a/test/005-annotations/build b/test/005-annotations/build index 8b9f55065ee59c85855a394a6ce77c17d6d4aa6a..5342eea4c40b704d22863959b96dd46730db2487 100644 --- a/test/005-annotations/build +++ b/test/005-annotations/build @@ -17,27 +17,28 @@ # Stop if something fails. set -e -mkdir classes - -# android.test.anno.MissingAnnotation is available at compile time... -${JAVAC} -d classes `find src -name '*.java'` -# overwrite RenamedEnum -${JAVAC} -d classes `find src2 -name '*.java'` - -# ...but not at run time. -rm 'classes/android/test/anno/MissingAnnotation.class' -rm 'classes/android/test/anno/ClassWithInnerAnnotationClass$MissingInnerAnnotationClass.class' - -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - # Jack needs to emit annotations with CLASS retention. - ${JACK} -D jack.dex.annotation.class-retention=true --import classes.jill.jar --output-dex . -else - if [ ${NEED_DEX} = "true" ]; then - ${DX} -JXmx256m --debug --dex --output=classes.dex classes +export ORIGINAL_JAVAC="$JAVAC" + +# Wrapper function for javac which invokes the compiler and applies +# additional setup steps for the test. +function javac_wrapper { + set -e # Stop on error - the caller script may not have this set. + + $ORIGINAL_JAVAC "$@" + + # Classes available at compile time, but not at runtime. + rm -f classes/android/test/anno/MissingAnnotation.class + rm -f 'classes/android/test/anno/ClassWithInnerAnnotationClass$MissingInnerAnnotationClass.class' + + # overwrite RenamedEnum in classes + if [ -f classes2/android/test/anno/RenamedEnumClass.java ] ; then + mv classes2/android/test/anno/RenamedEnumClass.java classes/android/test/anno/RenamedEnumClass.java fi -fi +} + +export -f javac_wrapper +export JAVAC=javac_wrapper + +###################################################################### -if [ ${NEED_DEX} = "true" ]; then - zip $TEST_NAME.jar classes.dex -fi +./default-build "$@" diff --git a/test/022-interface/build b/test/022-interface/build deleted file mode 100644 index 5cfc7f25b7c7d621003b63a85a52f86d4f7b276a..0000000000000000000000000000000000000000 --- a/test/022-interface/build +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2012 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Stop if something fails. -set -e - -# Use classes that are compiled with ecj that exposes an invokeinterface -# issue when interfaces override methods in Object -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - ${JACK} --import classes.jill.jar --output-dex . -else - ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes -fi - -zip $TEST_NAME.jar classes.dex diff --git a/test/023-many-interfaces/build b/test/023-many-interfaces/build index b4b5bd4c4a77ddcb5cb57e7881362b5cddda733c..faa3ce7178d6e82c14d7479af8f591ecb7f0c068 100644 --- a/test/023-many-interfaces/build +++ b/test/023-many-interfaces/build @@ -21,16 +21,4 @@ set -e gcc -Wall -Werror -o iface-gen iface-gen.c ./iface-gen -if [ ${USE_JACK} = "true" ]; then - # Use the default Jack commands - ./default-build -else - mkdir classes - ${JAVAC} -d classes src/*.java - - # dx needs more memory for that test so do not pass Xmx option here. - if [ ${NEED_DEX} = "true" ]; then - ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes - zip $TEST_NAME.jar classes.dex - fi -fi +./default-build "$@" diff --git a/test/036-finalizer/src/Main.java b/test/036-finalizer/src/Main.java index 9bfd74f91b553a6243a02ec553f2820c9203984d..be7ae4a8c2a6294028abd8148d30f046ca1f8b8b 100644 --- a/test/036-finalizer/src/Main.java +++ b/test/036-finalizer/src/Main.java @@ -71,16 +71,18 @@ public class Main { return s[0]; } - private static void printWeakReference(WeakReference wimp) { + public static void main(String[] args) { + WeakReference wimp = makeRef(); // Reference ft so we are sure the WeakReference cannot be cleared. + // Note: This is very fragile. It was previously in a helper function but that + // doesn't work for JIT-on-first-use with --gcstress where the object would be + // collected when JIT internally allocates an array. Also adding a scope around + // the keepLive lifetime somehow keeps a non-null `keepLive` around and makes + // the test fail (even when keeping the `null` assignment). b/76454261 FinalizerTest keepLive = wimp.get(); System.out.println("wimp: " + wimpString(wimp)); Reference.reachabilityFence(keepLive); - } - - public static void main(String[] args) { - WeakReference wimp = makeRef(); - printWeakReference(wimp); + keepLive = null; // Clear the reference. /* this will try to collect and finalize ft */ System.out.println("gc"); diff --git a/test/056-const-string-jumbo/build b/test/056-const-string-jumbo/build index 5344ac38eb984b8bc61aedffd5c34b6d334c09c0..c1d711b436acee2fbaf6b91817724194e466abec 100644 --- a/test/056-const-string-jumbo/build +++ b/test/056-const-string-jumbo/build @@ -39,17 +39,4 @@ function writeFile(name, start, end) { printf("}\n") > fileName; }' -if [ ${USE_JACK} = "true" ]; then - ${JACK} --output-dex . src -else - mkdir classes - ${JAVAC} -d classes src/*.java - - if [ ${NEED_DEX} = "true" ]; then - ${DX} -JXmx500m --debug --dex --no-optimize --positions=none --no-locals --output=classes.dex classes - fi -fi - -if [ ${NEED_DEX} = "true" ]; then - zip $TEST_NAME.jar classes.dex -fi +./default-build "$@" diff --git a/test/530-checker-lse/build b/test/066-mismatched-super/build old mode 100755 new mode 100644 similarity index 83% rename from test/530-checker-lse/build rename to test/066-mismatched-super/build index 10ffcc537db7c13099a0423b069305bbcafac7ac..c1c9ed304e5f7191d7e985991d3715bfeb7ac34a --- a/test/530-checker-lse/build +++ b/test/066-mismatched-super/build @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright 2017 The Android Open Source Project +# Copyright 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,7 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -# See b/65168732 -export USE_D8=false - -./default-build "$@" +DESUGAR=false ./default-build "$@" diff --git a/test/080-oom-throw/run b/test/080-oom-throw/run index eb473782a582eee416c1189d3c122a95c8b5567e..08db73bba08aee8b2f9afc9795ba8384d812c154 100644 --- a/test/080-oom-throw/run +++ b/test/080-oom-throw/run @@ -14,4 +14,18 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Ensure the minimum log severity is at least 'WARNING' to display the +# stack trace shown before exception +# +# "java.lang.OutOfMemoryError: OutOfMemoryError thrown while trying +# to throw OutOfMemoryError; no stack trace available" +# +# is set, to try to understand a recurring crash in this test (b/77567088). +case "$ANDROID_LOG_TAGS" in + # Lower the minimum log severity to WARNING if it was initialy set + # to a higher level ('ERROR', 'FATAL' or 'SILENT' -- see + # https://developer.android.com/studio/command-line/logcat#filteringOutput). + (\*:[efs]) export ANDROID_LOG_TAGS='*:w';; +esac + exec ${RUN} $@ --runtime-option -Xmx16m diff --git a/test/089-many-methods/build b/test/089-many-methods/build index ff77c60f648fb0ebb1803ea24ca937ac33a0c031..5b4cda87c15962d253b150f63399faf6371af2a7 100644 --- a/test/089-many-methods/build +++ b/test/089-many-methods/build @@ -43,8 +43,9 @@ function writeFileMethod(name) { printf("}\n") > fileName; }' -# The test relies on the error message produced by dx, not jack, so keep building with dx for now -# (b/19467889). -mkdir classes -${JAVAC} -d classes `find src -name '*.java'` -${DX} -JXmx1024m --dex --no-optimize classes +# Force DEX generation so test also passes with --jvm. +export NEED_DEX=true + +# Specify old API level as d8 automagically produces a multidex file +# when the API level is above 20. Failing the build here is deliberate. +./default-build --api-level 20 "$@" diff --git a/test/089-many-methods/check b/test/089-many-methods/check index 65b71397b8ddc9aa916370138ce5bdd7e94dbf08..1f71e8e0a031bcbf86eb968a882d60c7faa7c995 100755 --- a/test/089-many-methods/check +++ b/test/089-many-methods/check @@ -14,7 +14,5 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Strip build error debug messages, as they are environment-specific. -sed -e '/^Failed to build/d' -e '/^Non-canonical tmpdir/d' -e '/^Args:/d' -e '/^Max filename/d' -e '/^Max pathlength/d' "$2" > "$2.tmp" - -diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null \ No newline at end of file +grep Error "$2" > "$2.tmp" +diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null diff --git a/test/089-many-methods/expected.txt b/test/089-many-methods/expected.txt index 786df7c76d796646209707dd82cf142750fdd797..bb6ba3c7bc3f94464637df38c4eafc54bf68433e 100644 --- a/test/089-many-methods/expected.txt +++ b/test/089-many-methods/expected.txt @@ -1,6 +1 @@ - -trouble writing output: Too many field references to fit in one dex file: 131000; max is 65536. -You may try using multi-dex. If multi-dex is enabled then the list of classes for the main dex list is too large. -References by package: -131000 default -build exit status: 2 +Error: Cannot fit requested classes in a single dex file (# fields: 131000 > 65536) diff --git a/test/091-override-package-private-method/build b/test/091-override-package-private-method/build index 073a4ba9bca4bec88e4d80b118a1f0c9139535db..8257d9215658465f3e5e5e9bd00f902bc9c63b49 100755 --- a/test/091-override-package-private-method/build +++ b/test/091-override-package-private-method/build @@ -17,25 +17,20 @@ # Stop if something fails. set -e -mkdir classes -${JAVAC} -d classes `find src -name '*.java'` +export ORIGINAL_JAVAC="$JAVAC" -mkdir classes-ex -mv classes/OverridePackagePrivateMethodSuper.class classes-ex +# Wrapper function for javac which invokes the compiler and applies +# additional setup steps for the test. +function javac_wrapper { + set -e # Stop on error - the caller script may not have this set. + $ORIGINAL_JAVAC "$@" + mkdir -p classes-ex + mv classes/OverridePackagePrivateMethodSuper.class classes-ex +} -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - jar cf classes-ex.jill.jar -C classes-ex . +export -f javac_wrapper +export JAVAC=javac_wrapper - ${JACK} --import classes.jill.jar --output-dex . - zip $TEST_NAME.jar classes.dex - ${JACK} --import classes-ex.jill.jar --output-dex . - zip ${TEST_NAME}-ex.jar classes.dex -else - if [ ${NEED_DEX} = "true" ]; then - ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes - zip $TEST_NAME.jar classes.dex - ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex - zip ${TEST_NAME}-ex.jar classes.dex - fi -fi +###################################################################### + +./default-build "$@" diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt index 6a9bf61d259e46212fb4d230b6ca5c665b0f86af..2b5782446499c4cd4fedf744997e3c75aa5cb01e 100644 --- a/test/100-reflect2/expected.txt +++ b/test/100-reflect2/expected.txt @@ -33,7 +33,7 @@ z (class java.lang.Character) 14 (class java.lang.Short) [java.lang.String(int,int,char[]), public java.lang.String(), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder)] [private final int java.lang.String.count, private int java.lang.String.hash, private static final java.io.ObjectStreamField[] java.lang.String.serialPersistentFields, private static final long java.lang.String.serialVersionUID, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER] -[native void java.lang.String.getCharsNoCheck(int,int,char[],int), private boolean java.lang.String.nonSyncContentEquals(java.lang.AbstractStringBuilder), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private native java.lang.String java.lang.String.doReplace(char,char), private native java.lang.String java.lang.String.fastSubstring(int,int), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.Iterable), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(char[],int,int,char[],int,int,int), static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int), void java.lang.String.getChars(char[],int)] +[native void java.lang.String.getCharsNoCheck(int,int,char[],int), private boolean java.lang.String.nonSyncContentEquals(java.lang.AbstractStringBuilder), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private native java.lang.String java.lang.String.doReplace(char,char), private native java.lang.String java.lang.String.fastSubstring(int,int), private static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), private static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.Iterable), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(char[],int,int,char[],int,int,int), static int java.lang.String.indexOf(char[],int,int,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static int java.lang.String.lastIndexOf(char[],int,int,java.lang.String,int), void java.lang.String.getChars(char[],int)] [] [interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence] 0 diff --git a/test/111-unresolvable-exception/build b/test/111-unresolvable-exception/build index cf19f60d513802e8dd662d736b5548d12d4bff75..1c275aa2feb5742089fe2665f88b53008ffc9f03 100644 --- a/test/111-unresolvable-exception/build +++ b/test/111-unresolvable-exception/build @@ -17,19 +17,22 @@ # Stop if something fails. set -e -mkdir classes -${JAVAC} -d classes `find src -name '*.java'` -rm classes/TestException.class - -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - ${JACK} --import classes.jill.jar --output-dex . -else - if [ ${NEED_DEX} = "true" ]; then - ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes - fi -fi - -if [ ${NEED_DEX} = "true" ]; then - zip $TEST_NAME.jar classes.dex -fi +export ORIGINAL_JAVAC="$JAVAC" + +# Wrapper function for javac which invokes the compiler and applies +# additional setup steps for the test. +function javac_wrapper { + set -e # Stop on error - the caller script may not have this set. + + $ORIGINAL_JAVAC "$@" + + # Remove class available at compile time but not at run time. + rm classes/TestException.class +} + +export -f javac_wrapper +export JAVAC=javac_wrapper + +###################################################################### + +./default-build "$@" diff --git a/test/113-multidex/build b/test/113-multidex/build deleted file mode 100644 index b980e501be4ece7f194cc5da06b6a3d2c37549f1..0000000000000000000000000000000000000000 --- a/test/113-multidex/build +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2014 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Stop if something fails. -set -e - -# All except Main -mkdir classes -${JAVAC} -d classes `find src -name '*.java'` -rm classes/Main.class - -# Only Main -mkdir classes2 -${JAVAC} -d classes2 `find src -name '*.java'` -rm classes2/Second.class classes2/FillerA.class classes2/FillerB.class classes2/Inf*.class - -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - jar cf classes2.jill.jar -C classes2 . - - ${JACK} --import classes.jill.jar --output-dex . - mv classes.dex classes-1.dex - ${JACK} --import classes2.jill.jar --output-dex . - mv classes.dex classes2.dex - mv classes-1.dex classes.dex -else - if [ ${NEED_DEX} = "true" ]; then - # All except Main - ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes - - # Only Main - ${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex classes2 - fi -fi - -if [ ${NEED_DEX} = "true" ]; then - zip $TEST_NAME.jar classes.dex classes2.dex -fi diff --git a/test/113-multidex/src/Main.java b/test/113-multidex/src-multidex/Main.java similarity index 100% rename from test/113-multidex/src/Main.java rename to test/113-multidex/src-multidex/Main.java diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc index 3b352096df057b1677756696b656f571cfc02908..a74f7638bd3b68e9c98ea7f3f003d6e3e53108f2 100644 --- a/test/115-native-bridge/nativebridge.cc +++ b/test/115-native-bridge/nativebridge.cc @@ -229,7 +229,9 @@ static jint trampoline_Java_Main_testSignal(JNIEnv*, jclass) { struct sigaction64 tmp2; sigemptyset64(&tmp2.sa_mask); tmp2.sa_sigaction = test_sigaction_handler; +#if defined(SA_RESTORER) tmp2.sa_restorer = nullptr; +#endif sigaction64(SIGSEGV, &tmp2, nullptr); sigaction64(SIGILL, &tmp2, nullptr); diff --git a/test/117-nopatchoat/nopatchoat.cc b/test/117-nopatchoat/nopatchoat.cc index 7c382588a4592666e5578484bfa9980866967b1a..a8a895a6b32f25243cf5ef8bb5871a37fa36ddb0 100644 --- a/test/117-nopatchoat/nopatchoat.cc +++ b/test/117-nopatchoat/nopatchoat.cc @@ -28,7 +28,7 @@ namespace art { class NoPatchoatTest { public: - static const OatFile::OatDexFile* getOatDexFile(jclass cls) { + static const OatDexFile* getOatDexFile(jclass cls) { ScopedObjectAccess soa(Thread::Current()); ObjPtr klass = soa.Decode(cls); const DexFile& dex_file = klass->GetDexFile(); @@ -42,13 +42,13 @@ class NoPatchoatTest { } static bool hasExecutableOat(jclass cls) { - const OatFile::OatDexFile* oat_dex_file = getOatDexFile(cls); + const OatDexFile* oat_dex_file = getOatDexFile(cls); return oat_dex_file != nullptr && oat_dex_file->GetOatFile()->IsExecutable(); } static bool needsRelocation(jclass cls) { - const OatFile::OatDexFile* oat_dex_file = getOatDexFile(cls); + const OatDexFile* oat_dex_file = getOatDexFile(cls); if (oat_dex_file == nullptr) { return false; diff --git a/test/121-modifiers/classes/A$B.class b/test/121-modifiers/classes/A$B.class deleted file mode 100644 index bd7ebfe11dbd3399503f49a6e329521a883e94ef..0000000000000000000000000000000000000000 Binary files a/test/121-modifiers/classes/A$B.class and /dev/null differ diff --git a/test/121-modifiers/classes/A$C.class b/test/121-modifiers/classes/A$C.class deleted file mode 100644 index 3ae872e3560da3021de3ec4995230a59e660624d..0000000000000000000000000000000000000000 Binary files a/test/121-modifiers/classes/A$C.class and /dev/null differ diff --git a/test/121-modifiers/classes/A.class b/test/121-modifiers/classes/A.class deleted file mode 100644 index d89d029796acd0acd2e2071c2a4904230c6377d4..0000000000000000000000000000000000000000 Binary files a/test/121-modifiers/classes/A.class and /dev/null differ diff --git a/test/121-modifiers/classes/Inf.class b/test/121-modifiers/classes/Inf.class deleted file mode 100644 index e8dd68029df6b8b7f647cb2b79a104c648f12fa8..0000000000000000000000000000000000000000 Binary files a/test/121-modifiers/classes/Inf.class and /dev/null differ diff --git a/test/121-modifiers/classes/Main.class b/test/121-modifiers/classes/Main.class deleted file mode 100644 index e044074269bee130be5d85722915e06b0c5edd4a..0000000000000000000000000000000000000000 Binary files a/test/121-modifiers/classes/Main.class and /dev/null differ diff --git a/test/121-modifiers/classes/NonInf.class b/test/121-modifiers/classes/NonInf.class deleted file mode 100644 index 0f1e826fb74781d9d93abdd3af58822c1db633b1..0000000000000000000000000000000000000000 Binary files a/test/121-modifiers/classes/NonInf.class and /dev/null differ diff --git a/test/121-modifiers/info.txt b/test/121-modifiers/info.txt index 335df53f3d5e31c29414620fe74cc16e74a34a3d..7dba1133d1e2f2ab0f53a247e67b30df71ae0b77 100644 --- a/test/121-modifiers/info.txt +++ b/test/121-modifiers/info.txt @@ -10,9 +10,9 @@ Finally, compile with jack/jill or dx, and run baksmali. javac Inf.java NonInf.java Main.java javac -cp asm.jar:asm-tree.jar:. Asm.java java -cp asm.jar:asm-tree.jar:. Asm -mv Inf.out classes/Inf.class -mv NonInf.out classes/NonInf.class -mv Main.class A.class A\$B.class A\$C.class classes/ +mv Inf.out classes_tmp/Inf.class +mv NonInf.out classes_tmp/NonInf.class +mv Main.class A.class A\$B.class A\$C.class classes_tmp/ dx --debug --dex --output=classes.dex classes baksmali disassemble classes.dex mv out/*.smali smali/ diff --git a/test/124-missing-classes/build b/test/124-missing-classes/build index ea45cd27e5d1f04524f9f1bd9df4a61af72dd781..ec4ec84e09f0f14c99e743532e1b550781707d4a 100644 --- a/test/124-missing-classes/build +++ b/test/124-missing-classes/build @@ -17,24 +17,24 @@ # Stop if something fails. set -e -mkdir classes - -# Some classes are available at compile time... -${JAVAC} -d classes `find src -name '*.java'` - -# ...but not at run time. -rm 'classes/MissingClass.class' -rm 'classes/Main$MissingInnerClass.class' - -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - ${JACK} --import classes.jill.jar --output-dex . -else - if [ ${NEED_DEX} = "true" ]; then - ${DX} -JXmx256m --debug --dex --output=classes.dex classes - fi -fi - -if [ ${NEED_DEX} = "true" ]; then - zip $TEST_NAME.jar classes.dex -fi +export ORIGINAL_JAVAC="$JAVAC" + +# Wrapper function for javac which invokes the compiler and applies +# additional setup steps for the test. + +function javac_wrapper { + set -e # Stop on error - the caller script may not have this set. + + # Some classes are available at compile time... + $ORIGINAL_JAVAC "$@" + + # ...but not at run time. + rm 'classes/MissingClass.class' 'classes/Main$MissingInnerClass.class' +} + +export -f javac_wrapper +export JAVAC=javac_wrapper + +###################################################################### + +./default-build "$@" diff --git a/test/126-miranda-multidex/build b/test/126-miranda-multidex/build index 2a5e7daa129ffc35d29d6f6aa16ef664db03d146..7b44863fa97f8ffa54c2e7a81ff7efe4a86a11ab 100644 --- a/test/126-miranda-multidex/build +++ b/test/126-miranda-multidex/build @@ -17,35 +17,30 @@ # Stop if something fails. set -e -# All except MirandaInterface -mkdir classes -${JAVAC} -d classes `find src -name '*.java'` -rm classes/MirandaInterface.class +export ORIGINAL_JAVAC="$JAVAC" -# Only MirandaInterface -mkdir classes2 -${JAVAC} -d classes2 `find src -name '*.java'` -rm classes2/Main.class classes2/MirandaAbstract.class classes2/MirandaClass*.class classes2/MirandaInterface2*.class +# Wrapper function for javac which invokes the compiler and applies +# additional setup steps for the test. +function javac_wrapper { + set -e # Stop on error - the caller script may not have this set. -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - jar cf classes2.jill.jar -C classes2 . + if [[ "$*" != *"classes2"* ]]; then + # First invocation: compile src/ files. + $ORIGINAL_JAVAC "$@" + else + # Second invocation: move MirandaInterface.class for placement in + # a secondary dex file. There are no other source files for the + # secondary DEX so no compilation required. + mv classes/MirandaInterface.class classes2 + fi + return $? +} - ${JACK} --import classes.jill.jar --output-dex . - mv classes.dex classes-1.dex - ${JACK} --import classes2.jill.jar --output-dex . - mv classes.dex classes2.dex - mv classes-1.dex classes.dex -else - if [ ${NEED_DEX} = "true" ]; then - # All except Main - ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes +export -f javac_wrapper +export JAVAC=javac_wrapper - # Only Main - ${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex classes2 - fi -fi +###################################################################### -if [ ${NEED_DEX} = "true" ]; then - zip $TEST_NAME.jar classes.dex classes2.dex -fi +# Signal to default-build that this is a multidex test. +mkdir src-multidex +./default-build "$@" diff --git a/test/127-checker-secondarydex/build b/test/127-checker-secondarydex/build index 7ce46acfed9eaade2eebee7cc200c5ba59b7454a..3135681eecf33fb8011bd98a23ae43af228ef570 100755 --- a/test/127-checker-secondarydex/build +++ b/test/127-checker-secondarydex/build @@ -17,25 +17,22 @@ # Stop if something fails. set -e -mkdir classes -${JAVAC} -d classes `find src -name '*.java'` - -mkdir classes-ex -mv classes/Super.class classes-ex - -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - jar cf classes-ex.jill.jar -C classes-ex . - - ${JACK} --import classes.jill.jar --output-dex . - zip $TEST_NAME.jar classes.dex - ${JACK} --import classes-ex.jill.jar --output-dex . - zip ${TEST_NAME}-ex.jar classes.dex -else - if [ ${NEED_DEX} = "true" ]; then - ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes - zip $TEST_NAME.jar classes.dex - ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex - zip ${TEST_NAME}-ex.jar classes.dex - fi -fi +export ORIGINAL_JAVAC="$JAVAC" + +# Wrapper function for javac which invokes the compiler and applies +# additional setup steps for the test. +function javac_wrapper { + set -e # Stop on error - the caller script may not have this set. + + $ORIGINAL_JAVAC "$@" + + mkdir classes-ex + mv classes/Super.class classes-ex +} + +export -f javac_wrapper +export JAVAC=javac_wrapper + +###################################################################### + +./default-build "$@" diff --git a/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc index f01b82553dab3ea5c929c25e05625e0355b6866d..d9ade931d2cd2527a5446250abbd04734a869ef8 100644 --- a/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc +++ b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc @@ -19,8 +19,8 @@ #include "base/casts.h" #include "base/macros.h" -#include "java_vm_ext.h" -#include "jni_env_ext.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_env_ext.h" #include "thread-current-inl.h" namespace art { diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc index 49db0c82b55e16ca28c37d3c98ca035061432c7b..985d27309ec8195342defaea4f0fb70e368e7ff6 100644 --- a/test/137-cfi/cfi.cc +++ b/test/137-cfi/cfi.cc @@ -54,12 +54,38 @@ static void CauseSegfault() { #endif } -extern "C" JNIEXPORT jboolean JNICALL Java_Main_sleep(JNIEnv*, jobject, jint, jboolean, jdouble) { - // Keep pausing. - printf("Going to sleep\n"); - for (;;) { - sleep(1); +extern "C" JNIEXPORT jint JNICALL Java_Main_startSecondaryProcess(JNIEnv*, jclass) { +#if __linux__ + // Get our command line so that we can use it to start identical process. + std::string cmdline; // null-separated and null-terminated arguments. + ReadFileToString("/proc/self/cmdline", &cmdline); + cmdline = cmdline + "--secondary" + '\0'; // Let the child know it is a helper. + + // Split the string into individual arguments suitable for execv. + std::vector argv; + for (size_t i = 0; i < cmdline.size(); i += strlen(&cmdline[i]) + 1) { + argv.push_back(&cmdline[i]); } + argv.push_back(nullptr); // Terminate the list. + + pid_t pid = fork(); + if (pid < 0) { + LOG(FATAL) << "Fork failed"; + } else if (pid == 0) { + execv(argv[0], argv.data()); + exit(1); + } + return pid; +#else + return 0; +#endif +} + +extern "C" JNIEXPORT jboolean JNICALL Java_Main_sigstop(JNIEnv*, jclass) { +#if __linux__ + raise(SIGSTOP); +#endif + return true; // Prevent the compiler from tail-call optimizing this method away. } // Helper to look for a sequence in the stack trace. @@ -104,15 +130,8 @@ static void MoreErrorInfo(pid_t pid, bool sig_quit_on_fail) { } #endif -extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess( - JNIEnv*, - jobject, - jboolean, - jint, - jboolean) { +extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess(JNIEnv*, jclass) { #if __linux__ - // TODO: What to do on Valgrind? - std::unique_ptr bt(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, GetTid())); if (!bt->Unwind(0, nullptr)) { printf("Cannot unwind in process.\n"); @@ -127,10 +146,10 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess( // only unique functions are being expected. // "mini-debug-info" does not include parameters to save space. std::vector seq = { - "Java_Main_unwindInProcess", // This function. - "java.util.Arrays.binarySearch0", // Framework method. - "Base.runBase", // Method in other dex file. - "Main.main" // The Java entry method. + "Java_Main_unwindInProcess", // This function. + "java.util.Arrays.binarySearch0", // Framework method. + "Base.runTest", // Method in other dex file. + "Main.main" // The Java entry method. }; bool result = CheckStack(bt.get(), seq); @@ -149,8 +168,8 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess( } #if __linux__ -static constexpr int kSleepTimeMicroseconds = 50000; // 0.05 seconds -static constexpr int kMaxTotalSleepTimeMicroseconds = 1000000; // 1 second +static constexpr int kSleepTimeMicroseconds = 50000; // 0.05 seconds +static constexpr int kMaxTotalSleepTimeMicroseconds = 10000000; // 10 seconds // Wait for a sigstop. This code is copied from libbacktrace. int wait_for_sigstop(pid_t tid, int* total_sleep_time_usec, bool* detach_failed ATTRIBUTE_UNUSED) { @@ -182,18 +201,12 @@ int wait_for_sigstop(pid_t tid, int* total_sleep_time_usec, bool* detach_failed } #endif -extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess( - JNIEnv*, - jobject, - jboolean, - jint pid_int) { +extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess(JNIEnv*, jclass, jint pid_int) { #if __linux__ - // TODO: What to do on Valgrind? pid_t pid = static_cast(pid_int); - // OK, this is painful. debuggerd uses ptrace to unwind other processes. - - if (ptrace(PTRACE_ATTACH, pid, 0, 0)) { + // SEIZE is like ATTACH, but it does not stop the process (we let it stop itself). + if (ptrace(PTRACE_SEIZE, pid, 0, 0)) { // Were not able to attach, bad. printf("Failed to attach to other process.\n"); PLOG(ERROR) << "Failed to attach."; @@ -201,13 +214,12 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess( return JNI_FALSE; } - kill(pid, SIGSTOP); - bool detach_failed = false; int total_sleep_time_usec = 0; int signal = wait_for_sigstop(pid, &total_sleep_time_usec, &detach_failed); - if (signal == -1) { + if (signal != SIGSTOP) { LOG(WARNING) << "wait_for_sigstop failed."; + return JNI_FALSE; } std::unique_ptr bt(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD)); @@ -224,10 +236,10 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess( // See comment in unwindInProcess for non-exact stack matching. // "mini-debug-info" does not include parameters to save space. std::vector seq = { - "Java_Main_sleep", // The sleep function in the other process. - "java.util.Arrays.binarySearch0", // Framework method. - "Base.runBase", // Method in other dex file. - "Main.main" // The Java entry method. + "Java_Main_sigstop", // The stop function in the other process. + "java.util.Arrays.binarySearch0", // Framework method. + "Base.runTest", // Method in other dex file. + "Main.main" // The Java entry method. }; result = CheckStack(bt.get(), seq); diff --git a/test/137-cfi/expected.txt b/test/137-cfi/expected.txt index 8db7853696207b7358d3d5a4256e0b80d1a0e8e4..eedae8f51f4cd7f312b4e6d1946dcf81d0940660 100644 --- a/test/137-cfi/expected.txt +++ b/test/137-cfi/expected.txt @@ -1,2 +1,7 @@ JNI_OnLoad called +Unwind in process: PASS JNI_OnLoad called +Unwind other process: PASS +JNI_OnLoad called +JNI_OnLoad called +Unwind other process: PASS diff --git a/test/137-cfi/run b/test/137-cfi/run index 9190b1cf10ba79a1e57faf339ef68eee8a237057..4096b895eedcef348b63873e4a94feb7a15043ea 100755 --- a/test/137-cfi/run +++ b/test/137-cfi/run @@ -20,7 +20,7 @@ # there will be JITed frames on the callstack (it synchronously JITs on first use). ${RUN} "$@" -Xcompiler-option --generate-debug-info \ --runtime-option -Xjitthreshold:0 \ - --args --full-signatures --args --test-local --args --test-remote + --args --test-local --args --test-remote return_status1=$? # Test with minimal compressed debugging information. diff --git a/test/137-cfi/src-multidex/Base.java b/test/137-cfi/src-multidex/Base.java index d3f8a5681d1c36c6f154bcc798f12984182be795..986a3c2226635b7b79050126f29c7509ddbfe3f1 100644 --- a/test/137-cfi/src-multidex/Base.java +++ b/test/137-cfi/src-multidex/Base.java @@ -15,8 +15,12 @@ */ public abstract class Base { - abstract public void runImpl(); - public void runBase() { - runImpl(); + public void runTest() throws Exception { + // Conditionally throw exception to prevent the compiler from inlining the code. + if (!this.getClass().getName().equals("Main")) { + throw new Exception("Who is calling?"); + } + test(); } + abstract public void test(); } diff --git a/test/137-cfi/src/Main.java b/test/137-cfi/src/Main.java index 9a2e352b8cbbb68236bf48e7a37650f35dc9ba9f..5b32d8e1fedb546554e49477a1caaee2a5b9187e 100644 --- a/test/137-cfi/src/Main.java +++ b/test/137-cfi/src/Main.java @@ -22,181 +22,68 @@ import java.util.Comparator; public class Main extends Base implements Comparator
{ // Whether to test local unwinding. - private boolean testLocal; + private static boolean testLocal; // Unwinding another process, modelling debuggerd. - private boolean testRemote; + private static boolean testRemote; // We fork ourself to create the secondary process for remote unwinding. - private boolean secondary; + private static boolean secondary; - // Expect the symbols to contain full method signatures including parameters. - private boolean fullSignatures; - - private boolean passed; - - public Main(String[] args) throws Exception { + public static void main(String[] args) throws Exception { System.loadLibrary(args[0]); - for (String arg : args) { - if (arg.equals("--test-local")) { + for (int i = 1; i < args.length; i++) { + if (args[i].equals("--test-local")) { testLocal = true; - } - if (arg.equals("--test-remote")) { + } else if (args[i].equals("--test-remote")) { testRemote = true; - } - if (arg.equals("--secondary")) { + } else if (args[i].equals("--secondary")) { secondary = true; + } else { + System.out.println("Unknown argument: " + args[i]); + System.exit(1); } - if (arg.equals("--full-signatures")) { - fullSignatures = true; - } - } - if (!testLocal && !testRemote) { - System.out.println("No test selected."); - } - } - - public static void main(String[] args) throws Exception { - new Main(args).runBase(); - } - - public void runImpl() { - if (secondary) { - if (!testRemote) { - throw new RuntimeException("Should not be running secondary!"); - } - runSecondary(); - } else { - runPrimary(); - } - } - - private void runSecondary() { - foo(); - throw new RuntimeException("Didn't expect to get back..."); - } - - private void runPrimary() { - // First do the in-process unwinding. - if (testLocal && !foo()) { - System.out.println("Unwinding self failed."); - } - - if (!testRemote) { - // Skip the remote step. - return; - } - - // Fork the secondary. - String[] cmdline = getCmdLine(); - String[] secCmdLine = new String[cmdline.length + 1]; - System.arraycopy(cmdline, 0, secCmdLine, 0, cmdline.length); - secCmdLine[secCmdLine.length - 1] = "--secondary"; - Process p = exec(secCmdLine); - - try { - int pid = getPid(p); - if (pid <= 0) { - throw new RuntimeException("Couldn't parse process"); - } - - // Wait until the forked process had time to run until its sleep phase. - BufferedReader lineReader; - try { - InputStreamReader stdout = new InputStreamReader(p.getInputStream(), "UTF-8"); - lineReader = new BufferedReader(stdout); - while (!lineReader.readLine().contains("Going to sleep")) { - } - } catch (Exception e) { - throw new RuntimeException(e); - } - - if (!unwindOtherProcess(fullSignatures, pid)) { - System.out.println("Unwinding other process failed."); - - // In this case, log all the output. - // Note: this is potentially non-terminating code, if the secondary is totally stuck. - // We rely on the run-test timeout infrastructure to terminate the primary in - // such a case. - try { - String tmp; - System.out.println("Output from the secondary:"); - while ((tmp = lineReader.readLine()) != null) { - System.out.println("Secondary: " + tmp); - } - } catch (Exception e) { - e.printStackTrace(System.out); - } - } - - try { - lineReader.close(); - } catch (Exception e) { - e.printStackTrace(System.out); - } - } finally { - // Kill the forked process if it is not already dead. - p.destroy(); } - } - private static Process exec(String[] args) { - try { - return Runtime.getRuntime().exec(args); - } catch (Exception exc) { - throw new RuntimeException(exc); - } + // Call test() via base class to test unwinding through multidex. + new Main().runTest(); } - private static int getPid(Process p) { - // Could do reflection for the private pid field, but String parsing is easier. - String s = p.toString(); - if (s.startsWith("Process[pid=")) { - return Integer.parseInt(s.substring("Process[pid=".length(), s.indexOf(","))); - } else { - return -1; - } - } - - // Read /proc/self/cmdline to find the invocation command line (so we can fork another runtime). - private static String[] getCmdLine() { - try { - BufferedReader in = new BufferedReader(new FileReader("/proc/self/cmdline")); - String s = in.readLine(); - in.close(); - return s.split("\0"); - } catch (Exception exc) { - throw new RuntimeException(exc); - } - } - - public boolean foo() { - // Call bar via Arrays.binarySearch. - // This tests that we can unwind from framework code. + public void test() { + // Call unwind() via Arrays.binarySearch to test unwinding through framework. Main[] array = { this, this, this }; Arrays.binarySearch(array, 0, 3, this /* value */, this /* comparator */); - return passed; } public int compare(Main lhs, Main rhs) { - passed = bar(secondary); + unwind(); // Returning "equal" ensures that we terminate search - // after first item and thus call bar() only once. + // after first item and thus call unwind() only once. return 0; } - public boolean bar(boolean b) { - if (b) { - return sleep(2, b, 1.0); - } else { - return unwindInProcess(fullSignatures, 1, b); + public void unwind() { + if (secondary) { + sigstop(); // This is helper child process. Stop and wait for unwinding. + return; // Don't run the tests again in the secondary helper process. } - } - // Native functions. Note: to avoid deduping, they must all have different signatures. + if (testLocal) { + String result = unwindInProcess() ? "PASS" : "FAIL"; + System.out.println("Unwind in process: " + result); + } - public native boolean sleep(int i, boolean b, double dummy); + if (testRemote) { + // Start a secondary helper process. It will stop itself when it is ready. + int pid = startSecondaryProcess(); + // Wait for the secondary process to stop and then unwind it remotely. + String result = unwindOtherProcess(pid) ? "PASS" : "FAIL"; + System.out.println("Unwind other process: " + result); + } + } - public native boolean unwindInProcess(boolean fullSignatures, int i, boolean b); - public native boolean unwindOtherProcess(boolean fullSignatures, int pid); + public static native int startSecondaryProcess(); + public static native boolean sigstop(); + public static native boolean unwindInProcess(); + public static native boolean unwindOtherProcess(int pid); } diff --git a/test/138-duplicate-classes-check2/build b/test/138-duplicate-classes-check2/build index d346251edb1eb5b3632675bf3aaaedf68879bfe2..4ab732069996ca78c3818ecb35819bf48abd170c 100755 --- a/test/138-duplicate-classes-check2/build +++ b/test/138-duplicate-classes-check2/build @@ -17,24 +17,22 @@ # Stop if something fails. set -e -mkdir classes -${JAVAC} -d classes `find src -name '*.java'` - -mkdir classes-ex -${JAVAC} -d classes-ex `find src-ex -name '*.java'` -rm classes-ex/A.class - -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - ${JACK} --import classes.jill.jar --output-dex . - zip ${TEST_NAME}.jar classes.dex - - jar cf classes-ex.jill.jar -C classes-ex . - ${JACK} --import classes-ex.jill.jar --output-dex . - zip ${TEST_NAME}-ex.jar classes.dex -elif [ ${NEED_DEX} = "true" ]; then - ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes - zip ${TEST_NAME}.jar classes.dex - ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex - zip ${TEST_NAME}-ex.jar classes.dex -fi +export ORIGINAL_JAVAC="$JAVAC" + +# Wrapper function for javac which invokes the compiler and applies +# additional setup steps for the test. +function javac_wrapper { + set -e # Stop on error - the caller script may not have this set. + + $ORIGINAL_JAVAC "$@" + + # Remove one A.class from classes-ex + rm -f classes-ex/A.class +} + +export -f javac_wrapper +export JAVAC=javac_wrapper + +###################################################################### + +./default-build "$@" diff --git a/test/151-OpenFileLimit/src/Main.java b/test/151-OpenFileLimit/src/Main.java index de5890cbfa56e771409e8813cd6e3f9f3a197a2d..9b16090fbba05ef3ee8043b505c98591b22c39fd 100644 --- a/test/151-OpenFileLimit/src/Main.java +++ b/test/151-OpenFileLimit/src/Main.java @@ -38,7 +38,8 @@ public class Main { if (e.getMessage().contains("Too many open files")) { System.out.println("Message includes \"Too many open files\""); } else { - System.out.println(e.getMessage()); + System.out.println("Unexpected exception:"); + e.printStackTrace(); } } diff --git a/test/161-final-abstract-class/smali/Main.smali b/test/161-final-abstract-class/smali/Main.smali new file mode 100644 index 0000000000000000000000000000000000000000..588854cf5283c08b8199f88f5c1eb1958a2037c8 --- /dev/null +++ b/test/161-final-abstract-class/smali/Main.smali @@ -0,0 +1,214 @@ +# Created with baksmali. + +# Java file for reference. + +# import java.lang.reflect.InvocationTargetException; +# import java.lang.reflect.Method; +# +# public class Main { +# public static void main(String[] args) { +# try { +# // Make sure that the abstract final class is marked as erroneous. +# Class.forName("AbstractFinal"); +# System.out.println("UNREACHABLE!"); +# } catch (VerifyError expected) { +# } catch (Throwable t) { +# t.printStackTrace(System.out); +# } +# try { +# // Verification of TestClass.test() used to crash when processing +# // the final abstract (erroneous) class. +# Class tc = Class.forName("TestClass"); +# Method test = tc.getDeclaredMethod("test"); +# test.invoke(null); +# System.out.println("UNREACHABLE!"); +# } catch (InvocationTargetException ite) { +# if (ite.getCause() instanceof InstantiationError) { +# System.out.println( +# ite.getCause().getClass().getName() + ": " + ite.getCause().getMessage()); +# } else { +# ite.printStackTrace(System.out); +# } +# } catch (Throwable t) { +# t.printStackTrace(System.out); +# } +# } +# } + +.class public LMain; +.super Ljava/lang/Object; +.source "Main.java" + + +# direct methods +.method public constructor ()V + .registers 1 + + .line 20 + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method + +.method public static main([Ljava/lang/String;)V + .registers 4 + + .line 24 + :try_start_0 + const-string p0, "AbstractFinal" + + invoke-static {p0}, Ljava/lang/Class;->forName(Ljava/lang/String;)Ljava/lang/Class; + + .line 25 + sget-object p0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + const-string v0, "UNREACHABLE!" + + invoke-virtual {p0, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + :try_end_c + .catch Ljava/lang/VerifyError; {:try_start_0 .. :try_end_c} :catch_14 + .catch Ljava/lang/Throwable; {:try_start_0 .. :try_end_c} :catch_d + + goto :goto_15 + + .line 27 + :catch_d + move-exception p0 + + .line 28 + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + invoke-virtual {p0, v0}, Ljava/lang/Throwable;->printStackTrace(Ljava/io/PrintStream;)V + + goto :goto_16 + + .line 26 + :catch_14 + move-exception p0 + + .line 29 + :goto_15 + nop + + .line 33 + :goto_16 + :try_start_16 + const-string p0, "TestClass" + + invoke-static {p0}, Ljava/lang/Class;->forName(Ljava/lang/String;)Ljava/lang/Class; + + move-result-object p0 + + .line 34 + const-string v0, "test" + + const/4 v1, 0x0 + + new-array v2, v1, [Ljava/lang/Class; + + invoke-virtual {p0, v0, v2}, Ljava/lang/Class;->getDeclaredMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method; + + move-result-object p0 + + .line 35 + const/4 v0, 0x0 + + new-array v1, v1, [Ljava/lang/Object; + + invoke-virtual {p0, v0, v1}, Ljava/lang/reflect/Method;->invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; + + .line 36 + sget-object p0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + const-string v0, "UNREACHABLE!" + + invoke-virtual {p0, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + :try_end_32 + .catch Ljava/lang/reflect/InvocationTargetException; {:try_start_16 .. :try_end_32} :catch_3a + .catch Ljava/lang/Throwable; {:try_start_16 .. :try_end_32} :catch_33 + + goto :goto_76 + + .line 44 + :catch_33 + move-exception p0 + + .line 45 + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + invoke-virtual {p0, v0}, Ljava/lang/Throwable;->printStackTrace(Ljava/io/PrintStream;)V + + goto :goto_77 + + .line 37 + :catch_3a + move-exception p0 + + .line 38 + invoke-virtual {p0}, Ljava/lang/reflect/InvocationTargetException;->getCause()Ljava/lang/Throwable; + + move-result-object v0 + + instance-of v0, v0, Ljava/lang/InstantiationError; + + if-eqz v0, :cond_71 + + .line 39 + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + new-instance v1, Ljava/lang/StringBuilder; + + invoke-direct {v1}, Ljava/lang/StringBuilder;->()V + + .line 40 + invoke-virtual {p0}, Ljava/lang/reflect/InvocationTargetException;->getCause()Ljava/lang/Throwable; + + move-result-object v2 + + invoke-virtual {v2}, Ljava/lang/Object;->getClass()Ljava/lang/Class; + + move-result-object v2 + + invoke-virtual {v2}, Ljava/lang/Class;->getName()Ljava/lang/String; + + move-result-object v2 + + invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + const-string v2, ": " + + invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + invoke-virtual {p0}, Ljava/lang/reflect/InvocationTargetException;->getCause()Ljava/lang/Throwable; + + move-result-object p0 + + invoke-virtual {p0}, Ljava/lang/Throwable;->getMessage()Ljava/lang/String; + + move-result-object p0 + + invoke-virtual {v1, p0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; + + move-result-object p0 + + .line 39 + invoke-virtual {v0, p0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + + goto :goto_76 + + .line 42 + :cond_71 + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + invoke-virtual {p0, v0}, Ljava/lang/reflect/InvocationTargetException;->printStackTrace(Ljava/io/PrintStream;)V + + .line 46 + :goto_76 + nop + + .line 47 + :goto_77 + return-void +.end method diff --git a/test/161-final-abstract-class/src/Main.java b/test/161-final-abstract-class/src/Main.java deleted file mode 100644 index 245249022625ba18d5d2f1b7ff7bfbcde2436975..0000000000000000000000000000000000000000 --- a/test/161-final-abstract-class/src/Main.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -public class Main { - public static void main(String[] args) { - try { - // Make sure that the abstract final class is marked as erroneous. - Class.forName("AbstractFinal"); - System.out.println("UNREACHABLE!"); - } catch (VerifyError expected) { - } catch (Throwable t) { - t.printStackTrace(System.out); - } - try { - // Verification of TestClass.test() used to crash when processing - // the final abstract (erroneous) class. - Class tc = Class.forName("TestClass"); - Method test = tc.getDeclaredMethod("test"); - test.invoke(null); - System.out.println("UNREACHABLE!"); - } catch (InvocationTargetException ite) { - if (ite.getCause() instanceof InstantiationError) { - System.out.println( - ite.getCause().getClass().getName() + ": " + ite.getCause().getMessage()); - } else { - ite.printStackTrace(System.out); - } - } catch (Throwable t) { - t.printStackTrace(System.out); - } - } -} diff --git a/test/166-bad-interface-super/build b/test/166-bad-interface-super/build new file mode 100644 index 0000000000000000000000000000000000000000..bba6184e167d7befc417aca9578de6ef6687c0e7 --- /dev/null +++ b/test/166-bad-interface-super/build @@ -0,0 +1,27 @@ +#!/bin/bash +# +# Copyright 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Use the jasmin sources for JVM, otherwise the smali sources. +extra_arg="--no-jasmin" + +for arg in "$@"; do + if [[ "$arg" == "--jvm" ]]; then + extra_arg="--no-smali" + break + fi +done + +./default-build "$@" "$extra_arg" diff --git a/test/476-checker-ctor-fence-redun-elim/build b/test/166-bad-interface-super/smali/BadSuper1.smali similarity index 80% rename from test/476-checker-ctor-fence-redun-elim/build rename to test/166-bad-interface-super/smali/BadSuper1.smali index 10ffcc537db7c13099a0423b069305bbcafac7ac..6233403897bf9b8aeddea5935dbd150e71079abb 100644 --- a/test/476-checker-ctor-fence-redun-elim/build +++ b/test/166-bad-interface-super/smali/BadSuper1.smali @@ -1,6 +1,4 @@ -#!/bin/bash -# -# Copyright 2017 The Android Open Source Project +# Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# See b/65168732 -export USE_D8=false - -./default-build "$@" +.class public interface LBadSuper1; +.super LBaseInterface; +.source "BadSuper1.j" diff --git a/test/565-checker-rotate/build b/test/166-bad-interface-super/smali/BadSuper2.smali similarity index 80% rename from test/565-checker-rotate/build rename to test/166-bad-interface-super/smali/BadSuper2.smali index 10ffcc537db7c13099a0423b069305bbcafac7ac..8e410cf1b4cc65662bfbddc49a8dec62d4903acd 100644 --- a/test/565-checker-rotate/build +++ b/test/166-bad-interface-super/smali/BadSuper2.smali @@ -1,6 +1,4 @@ -#!/bin/bash -# -# Copyright 2017 The Android Open Source Project +# Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# See b/65168732 -export USE_D8=false - -./default-build "$@" +.class public interface LBadSuper2; +.super LBaseClass; +.source "BadSuper2.j" diff --git a/test/172-app-image-twice/check b/test/172-app-image-twice/check new file mode 100755 index 0000000000000000000000000000000000000000..26a97a48ae6758093b3ac19cc30826d22a7e05a6 --- /dev/null +++ b/test/172-app-image-twice/check @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Remove all lines not containing "passed". +grep "^passed" "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null diff --git a/test/172-app-image-twice/debug_print_class.cc b/test/172-app-image-twice/debug_print_class.cc new file mode 100644 index 0000000000000000000000000000000000000000..6c3de20f2dd074cf079f513ea8a1daad97390af2 --- /dev/null +++ b/test/172-app-image-twice/debug_print_class.cc @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "debug_print.h" +#include "dex/dex_file.h" +#include "mirror/class-inl.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-current-inl.h" + +namespace art { + +extern "C" JNIEXPORT void JNICALL Java_Main_debugPrintClass(JNIEnv*, jclass, jclass cls) { + ScopedObjectAccess soa(Thread::Current()); + ObjPtr klass = soa.Decode(cls); + LOG(ERROR) << "klass: " << klass.Ptr() << " dex_file: " << klass->GetDexFile().GetLocation() + << "/" << static_cast(&klass->GetDexFile()) + << " " << DescribeSpace(klass); +} + +} // namespace art diff --git a/test/651-checker-byte-simd-minmax/expected.txt b/test/172-app-image-twice/expected.txt similarity index 100% rename from test/651-checker-byte-simd-minmax/expected.txt rename to test/172-app-image-twice/expected.txt diff --git a/test/172-app-image-twice/info.txt b/test/172-app-image-twice/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..028046e872077c3dec9bf1dae5839696dc4f3a0c --- /dev/null +++ b/test/172-app-image-twice/info.txt @@ -0,0 +1 @@ +Regression test for loading the same app image twice. diff --git a/test/172-app-image-twice/profile b/test/172-app-image-twice/profile new file mode 100644 index 0000000000000000000000000000000000000000..70cb2efbb5977561430ef6e922438c12e86b9cdb --- /dev/null +++ b/test/172-app-image-twice/profile @@ -0,0 +1 @@ +LTestClass; diff --git a/test/172-app-image-twice/run b/test/172-app-image-twice/run new file mode 100644 index 0000000000000000000000000000000000000000..aa2819075fe109968a2815209d29e8039ed911cc --- /dev/null +++ b/test/172-app-image-twice/run @@ -0,0 +1,28 @@ +#!/bin/bash +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Build an app image with TestClass (specified by profile) and class loader +# context that skips the duplicate class checks. + +# Target and host use a different shell, and we need to special case the +# passing of the class loader context marker. +if [[ "$@" = *" --host "* ]]; then + ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \ + -Xcompiler-option --class-loader-context=\& +else + ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \ + -Xcompiler-option '--class-loader-context=\&' +fi diff --git a/test/172-app-image-twice/src/Main.java b/test/172-app-image-twice/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..a1c151a6bcef29df8119569db2f03120268c410f --- /dev/null +++ b/test/172-app-image-twice/src/Main.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Method; + +public class Main { + private static String TEST_NAME = "172-app-image-twice"; + + public static void main(String args[]) throws Exception { + System.loadLibrary(args[0]); + + Class tc1 = Class.forName("TestClass"); + + String dexPath = System.getenv("DEX_LOCATION") + "/" + TEST_NAME + ".jar"; + Class bdcl = Class.forName("dalvik.system.BaseDexClassLoader"); + Method addDexPathMethod = bdcl.getDeclaredMethod("addDexPath", String.class); + addDexPathMethod.invoke(Main.class.getClassLoader(), dexPath); + + Class tc2 = Class.forName("TestClass"); + + // Add extra logging to simulate libcore logging, this logging should not be compared + // against. + System.out.println("Extra logging"); + + if (tc1 != tc2) { + System.out.println("Class mismatch!"); + debugPrintClass(tc1); + debugPrintClass(tc2); + } else { + System.out.println("passed"); + } + } + + public static native void debugPrintClass(Class cls); +} diff --git a/runtime/globals.h b/test/172-app-image-twice/src/TestClass.java similarity index 70% rename from runtime/globals.h rename to test/172-app-image-twice/src/TestClass.java index bdc2177c7c172e167186adbc8da64a82c72ef5d7..5381718f6e3aed3a6e79bb21326ea8d22e6e4244 100644 --- a/runtime/globals.h +++ b/test/172-app-image-twice/src/TestClass.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,5 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_GLOBALS_H_ -#define ART_RUNTIME_GLOBALS_H_ - -// TODO: remove this file in favor of libartbase/base/globals.h -#include "base/globals.h" - -#endif // ART_RUNTIME_GLOBALS_H_ +public class TestClass { +} diff --git a/test/651-checker-char-simd-minmax/expected.txt b/test/173-missing-field-type/expected.txt similarity index 100% rename from test/651-checker-char-simd-minmax/expected.txt rename to test/173-missing-field-type/expected.txt diff --git a/test/173-missing-field-type/info.txt b/test/173-missing-field-type/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..4e20b4c8e0fff828521142d23ff498fcc97fe03f --- /dev/null +++ b/test/173-missing-field-type/info.txt @@ -0,0 +1 @@ +Tests handling of fields where the field type is missing (b/79751666). diff --git a/test/173-missing-field-type/smali/BadField.smali b/test/173-missing-field-type/smali/BadField.smali new file mode 100644 index 0000000000000000000000000000000000000000..851e8fe9c6e9a28aea258ebd51e2599852740e6f --- /dev/null +++ b/test/173-missing-field-type/smali/BadField.smali @@ -0,0 +1,34 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class public LBadField; +.super Ljava/lang/Object; + +# This is a bad field since there is no class Widget in this test. +.field public static widget:LWidget; + +.method public constructor ()V + .registers 2 + invoke-direct {v1}, Ljava/lang/Object;->()V + return-void +.end method + +.method public static constructor ()V + .registers 1 + new-instance v0, Ljava/lang/Object; + invoke-direct {v0}, Ljava/lang/Object;->()V + sput-object v0, LBadField;->widget:LWidget; + return-void +.end method \ No newline at end of file diff --git a/test/173-missing-field-type/src-art/Main.java b/test/173-missing-field-type/src-art/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..61bb918813e8f7100839bc177ebedf772e1e14ac --- /dev/null +++ b/test/173-missing-field-type/src-art/Main.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +public class Main { + public static void main(String[] args) throws Throwable { + try { + // Class BadField is defined in BadField.smali. + Class c = Class.forName("BadField"); + System.out.println("Not reached"); + c.newInstance(); + } catch (NoClassDefFoundError expected) { + // The NoClassDefFoundError is for the field widget in class BadField. + if (expected.getMessage().equals("Failed resolution of: LWidget;")) { + System.out.println("passed"); + } else { + System.out.println("failed: " + expected.getMessage()); + } + } + } +} diff --git a/runtime/jobject_comparator.h b/test/173-missing-field-type/src/Main.java similarity index 63% rename from runtime/jobject_comparator.h rename to test/173-missing-field-type/src/Main.java index 698d6678d69e8850526dcf8e124f031a446d02a9..172d6a62146fff9fd8ac1e322c0017403fb6047c 100644 --- a/runtime/jobject_comparator.h +++ b/test/173-missing-field-type/src/Main.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,17 +14,9 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_JOBJECT_COMPARATOR_H_ -#define ART_RUNTIME_JOBJECT_COMPARATOR_H_ - -#include - -namespace art { - -struct JobjectComparator { - bool operator()(jobject jobj1, jobject jobj2) const; -}; - -} // namespace art - -#endif // ART_RUNTIME_JOBJECT_COMPARATOR_H_ +public class Main { + // Allow test to pass on RI without adding to knownfailures.json file. + public static void main(String args[]) throws Exception { + System.out.println("passed"); + } +} diff --git a/test/174-escaping-instance-of-bad-class/expected.txt b/test/174-escaping-instance-of-bad-class/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..e287759d4467044439698abec70e795f092d6bdc --- /dev/null +++ b/test/174-escaping-instance-of-bad-class/expected.txt @@ -0,0 +1,18 @@ +Bad.foo() +Bad.instanceValue = 33 +Caught NoClassDefFoundError. +BadSuper.foo() +BadSuper.superInstanceValue = 1 +Caught NoClassDefFoundError. +BadSub.bar() +BadSub.subInstanceValue = 11 +BadSub.subStaticValue = 4242 +BadSuper.superInstanceValue = 111 +Caught NoClassDefFoundError. +BadSub.bar() +BadSub.subInstanceValue = -1 +BadSub.subStaticValue = 4242 +BadSuper.superInstanceValue = -2 +Caught NoClassDefFoundError. +BadSub.allocSuper(.) +Caught NoClassDefFoundError. diff --git a/test/174-escaping-instance-of-bad-class/info.txt b/test/174-escaping-instance-of-bad-class/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..55b4fed2cd41a5b1ccc83645c68227f68e90b788 --- /dev/null +++ b/test/174-escaping-instance-of-bad-class/info.txt @@ -0,0 +1 @@ +Regression test for an escaping instance of an erroneous class. diff --git a/test/174-escaping-instance-of-bad-class/src/Main.java b/test/174-escaping-instance-of-bad-class/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..4346152f6cc00128b3c80a77196edfd1632f78b7 --- /dev/null +++ b/test/174-escaping-instance-of-bad-class/src/Main.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String args[]) { + simpleTest(); + hierarchyTest(); + } + + public static void simpleTest() { + // Partial initialization of Bad; ignoring the error. + Error badClinit = null; + try { + new Bad(11); + } catch (Error e) { + badClinit = e; + } + // Call foo() on the escaped instance of Bad. + try { + bad.foo(); + } catch (NoClassDefFoundError ncdfe) { + // On RI, the NCDFE has no cause. On ART, the badClinit is the cause. + if (ncdfe.getCause() == badClinit || ncdfe.getCause() == null) { + System.out.println("Caught NoClassDefFoundError."); + } else { + ncdfe.printStackTrace(); + } + } + } + + public static void hierarchyTest() { + // Partial initialization of BadSuper; ignoring the error. Fully initializes BadSub. + Error badClinit = null; + try { + new BadSuper(0); + } catch (Error e) { + badClinit = e; + } + // Call BadSuper.foo() on the escaped instance of BadSuper. + try { + badSuper.foo(); + } catch (NoClassDefFoundError ncdfe) { + // On RI, the NCDFE has no cause. On ART, the badClinit is the cause. + if (ncdfe.getCause() == badClinit || ncdfe.getCause() == null) { + System.out.println("Caught NoClassDefFoundError."); + } else { + ncdfe.printStackTrace(); + } + } + + // Call BadSub.bar() on the escaped instance of BadSub. + try { + badSub.bar(); + } catch (NoClassDefFoundError ncdfe) { + // On RI, the NCDFE has no cause. On ART, the badClinit is the cause. + if (ncdfe.getCause() == badClinit || ncdfe.getCause() == null) { + System.out.println("Caught NoClassDefFoundError."); + } else { + ncdfe.printStackTrace(); + } + } + + // Test that we can even create instances of BadSub with erroneous superclass BadSuper. + try { + new BadSub(-1, -2).bar(); + } catch (NoClassDefFoundError ncdfe) { + // On RI, the NCDFE has no cause. On ART, the badClinit is the cause. + if (ncdfe.getCause() == badClinit || ncdfe.getCause() == null) { + System.out.println("Caught NoClassDefFoundError."); + } else { + ncdfe.printStackTrace(); + } + } + + // Test that we cannot create instances of BadSuper from BadSub. + try { + badSub.allocSuper(11111); // Should throw. + System.out.println("Allocated BadSuper!"); + } catch (NoClassDefFoundError ncdfe) { + // On RI, the NCDFE has no cause. On ART, the badClinit is the cause. + if (ncdfe.getCause() == badClinit || ncdfe.getCause() == null) { + System.out.println("Caught NoClassDefFoundError."); + } else { + ncdfe.printStackTrace(); + } + } + } + + public static Bad bad; + + public static BadSuper badSuper; + public static BadSub badSub; +} + +class Bad { + static { + // Create an instance of Bad and let it escape in Main.bad. + Main.bad = new Bad(33); + staticValue = 42; + if (true) { throw new Error("Bad "); } + } + public void foo() { + System.out.println("Bad.foo()"); + System.out.println("Bad.instanceValue = " + instanceValue); + System.out.println("Bad.staticValue = " + staticValue); + } + public Bad(int iv) { instanceValue = iv; } + public int instanceValue; + public static int staticValue; +} + +class BadSuper { + static { + Main.badSuper = new BadSuper(1); + Main.badSub = new BadSub(11, 111); // Fully initializes BadSub. + BadSuper.superStaticValue = 42; + BadSub.subStaticValue = 4242; + if (true) { throw new Error("Bad "); } + } + public void foo() { + System.out.println("BadSuper.foo()"); + System.out.println("BadSuper.superInstanceValue = " + superInstanceValue); + System.out.println("BadSuper.superStaticValue = " + superStaticValue); + } + public BadSuper(int superiv) { superInstanceValue = superiv; } + public int superInstanceValue; + public static int superStaticValue; +} + +// Note: If we tried to initialize BadSub before BadSuper, it would end up erroneous +// because the superclass fails initialization. However, since we start initializing the +// BadSuper first, BadSub is initialized successfully while BadSuper is "initializing" +// and remains initialized after the BadSuper's class initializer throws. +class BadSub extends BadSuper { + public void bar() { + System.out.println("BadSub.bar()"); + System.out.println("BadSub.subInstanceValue = " + subInstanceValue); + System.out.println("BadSub.subStaticValue = " + subStaticValue); + System.out.println("BadSuper.superInstanceValue = " + superInstanceValue); + System.out.println("BadSuper.superStaticValue = " + superStaticValue); + } + public BadSuper allocSuper(int superiv) { + System.out.println("BadSub.allocSuper(.)"); + return new BadSuper(superiv); + } + public BadSub(int subiv, int superiv) { + super(superiv); + subInstanceValue = subiv; + } + public int subInstanceValue; + public static int subStaticValue; +} diff --git a/test/1929-exception-catch-exception/expected.txt b/test/1929-exception-catch-exception/expected.txt index bc5608ac4e943ecf5805aa40ca45bb243a654e75..a82b732eda8e9328c07d67e2fc9125e7961e492f 100644 --- a/test/1929-exception-catch-exception/expected.txt +++ b/test/1929-exception-catch-exception/expected.txt @@ -1,11 +1,11 @@ Test "art.Test1929$DoThrowClass": Running breakpoint with handler "art.Test1929$DoNothingHandler" -main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: doThrow +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 298 caught class art.Test1929$TestException: doThrow Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.run() throws java.lang.Exception @ line = 283 + public static void art.Test1929.run() throws java.lang.Exception @ line = 298 Test "art.Test1929$DoThrowClass": Caught error art.Test1929$TestException:"doThrow" with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowClass": Finished running with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowCatchBaseTestException": Running breakpoint with handler "art.Test1929$DoNothingHandler" @@ -17,71 +17,71 @@ main: public static void art.Test1929.throwCatchBaseTestException() @ line = 140 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 public static void art.Test1929.throwCatchBaseTestException() @ line = 140 public void art.Test1929$DoThrowCatchBaseTestException.run() @ line = 149 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Doing nothing! Caught art.Test1929$TestException: "throwCatchBaseTestException" Test "art.Test1929$DoThrowCatchBaseTestException": No error caught with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowCatchBaseTestException": Finished running with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Running breakpoint with handler "art.Test1929$DoNothingHandler" -main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice +main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 157 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 - public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 197 - public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 201 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 157 + public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 203 + public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 210 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Caught art.Test1929$TestException: "throwCatchBaseTestExceptionTwice" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": No error caught with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Finished running with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowCatchTestException": Running breakpoint with handler "art.Test1929$DoNothingHandler" -main: public static void art.Test1929.throwCatchTestException() @ line = 207 caught class art.Test1929$TestException: throwCatchTestException +main: public static void art.Test1929.throwCatchTestException() @ line = 216 caught class art.Test1929$TestException: throwCatchTestException Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.throwCatchTestException() @ line = 207 - public void art.Test1929$DoThrowCatchTestException.run() @ line = 216 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929.throwCatchTestException() @ line = 216 + public void art.Test1929$DoThrowCatchTestException.run() @ line = 225 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Doing nothing! Caught art.Test1929$TestException: "throwCatchTestException" Test "art.Test1929$DoThrowCatchTestException": No error caught with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowCatchTestException": Finished running with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowCatchTestExceptionTwice": Running breakpoint with handler "art.Test1929$DoNothingHandler" -main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 caught class art.Test1929$TestException: throwCatchTestExceptionTwice +main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 175 caught class art.Test1929$TestException: throwCatchTestExceptionTwice Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 - public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 222 - public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 226 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 175 + public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 234 + public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 241 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Caught art.Test1929$TestException: "throwCatchTestExceptionTwice" Test "art.Test1929$DoThrowCatchTestExceptionTwice": No error caught with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowCatchTestExceptionTwice": Finished running with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Running breakpoint with handler "art.Test1929$DoNothingHandler" -main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 298 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.run() throws java.lang.Exception @ line = 283 + public static void art.Test1929.run() throws java.lang.Exception @ line = 298 Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Caught error art.Test1929$TestException:"throwCatchTestExceptionNoRethrow" with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Finished running with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowClass": Running breakpoint with handler "art.Test1929$ThrowCatchBase" -main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: doThrow +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 298 caught class art.Test1929$TestException: doThrow Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.run() throws java.lang.Exception @ line = 283 + public static void art.Test1929.run() throws java.lang.Exception @ line = 298 Test "art.Test1929$DoThrowClass": Caught error art.Test1929$TestException:"doThrow" with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowClass": Finished running with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowCatchBaseTestException": Running breakpoint with handler "art.Test1929$ThrowCatchBase" @@ -93,73 +93,73 @@ main: public static void art.Test1929.throwCatchBaseTestException() @ line = 140 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 public static void art.Test1929.throwCatchBaseTestException() @ line = 140 public void art.Test1929$DoThrowCatchBaseTestException.run() @ line = 149 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Throwing BaseTestException and catching it! Caught art.Test1929$BaseTestException: "ThrowBaseHandler during throw from public static void art.Test1929.throwCatchBaseTestException() @ line = 140" Caught art.Test1929$TestException: "throwCatchBaseTestException" Test "art.Test1929$DoThrowCatchBaseTestException": No error caught with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowCatchBaseTestException": Finished running with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowCatchBase" -main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice +main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 157 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 - public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 197 - public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 201 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 157 + public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 203 + public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 210 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Caught art.Test1929$TestException: "throwCatchBaseTestExceptionTwice" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": No error caught with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Finished running with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowCatchTestException": Running breakpoint with handler "art.Test1929$ThrowCatchBase" -main: public static void art.Test1929.throwCatchTestException() @ line = 207 caught class art.Test1929$TestException: throwCatchTestException +main: public static void art.Test1929.throwCatchTestException() @ line = 216 caught class art.Test1929$TestException: throwCatchTestException Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.throwCatchTestException() @ line = 207 - public void art.Test1929$DoThrowCatchTestException.run() @ line = 216 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929.throwCatchTestException() @ line = 216 + public void art.Test1929$DoThrowCatchTestException.run() @ line = 225 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Throwing BaseTestException and catching it! -Caught art.Test1929$BaseTestException: "ThrowBaseHandler during throw from public static void art.Test1929.throwCatchTestException() @ line = 207" +Caught art.Test1929$BaseTestException: "ThrowBaseHandler during throw from public static void art.Test1929.throwCatchTestException() @ line = 216" Caught art.Test1929$TestException: "throwCatchTestException" Test "art.Test1929$DoThrowCatchTestException": No error caught with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowCatchTestException": Finished running with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowCatchTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowCatchBase" -main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 caught class art.Test1929$TestException: throwCatchTestExceptionTwice +main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 175 caught class art.Test1929$TestException: throwCatchTestExceptionTwice Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 - public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 222 - public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 226 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 175 + public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 234 + public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 241 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Caught art.Test1929$TestException: "throwCatchTestExceptionTwice" Test "art.Test1929$DoThrowCatchTestExceptionTwice": No error caught with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowCatchTestExceptionTwice": Finished running with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Running breakpoint with handler "art.Test1929$ThrowCatchBase" -main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 298 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.run() throws java.lang.Exception @ line = 283 + public static void art.Test1929.run() throws java.lang.Exception @ line = 298 Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Caught error art.Test1929$TestException:"throwCatchTestExceptionNoRethrow" with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Finished running with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowClass": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler" -main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: doThrow +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 298 caught class art.Test1929$TestException: doThrow Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.run() throws java.lang.Exception @ line = 283 + public static void art.Test1929.run() throws java.lang.Exception @ line = 298 Test "art.Test1929$DoThrowClass": Caught error art.Test1929$TestException:"doThrow" with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowClass": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowCatchBaseTestException": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler" @@ -171,69 +171,69 @@ main: public static void art.Test1929.throwCatchBaseTestException() @ line = 140 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 public static void art.Test1929.throwCatchBaseTestException() @ line = 140 public void art.Test1929$DoThrowCatchBaseTestException.run() @ line = 149 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Throwing BaseTestException! Test "art.Test1929$DoThrowCatchBaseTestException": Caught error art.Test1929$BaseTestException:"ThrowBaseHandler during throw from public static void art.Test1929.throwCatchBaseTestException() @ line = 140" with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowCatchBaseTestException": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler" -main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice +main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 157 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 - public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 197 - public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 201 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 157 + public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 203 + public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 210 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Caught art.Test1929$TestException: "throwCatchBaseTestExceptionTwice" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": No error caught with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowCatchTestException": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler" -main: public static void art.Test1929.throwCatchTestException() @ line = 207 caught class art.Test1929$TestException: throwCatchTestException +main: public static void art.Test1929.throwCatchTestException() @ line = 216 caught class art.Test1929$TestException: throwCatchTestException Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.throwCatchTestException() @ line = 207 - public void art.Test1929$DoThrowCatchTestException.run() @ line = 216 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929.throwCatchTestException() @ line = 216 + public void art.Test1929$DoThrowCatchTestException.run() @ line = 225 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Throwing BaseTestException! -Test "art.Test1929$DoThrowCatchTestException": Caught error art.Test1929$BaseTestException:"ThrowBaseHandler during throw from public static void art.Test1929.throwCatchTestException() @ line = 207" with handler "art.Test1929$ThrowBaseTestExceptionHandler" +Test "art.Test1929$DoThrowCatchTestException": Caught error art.Test1929$BaseTestException:"ThrowBaseHandler during throw from public static void art.Test1929.throwCatchTestException() @ line = 216" with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowCatchTestException": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowCatchTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler" -main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 caught class art.Test1929$TestException: throwCatchTestExceptionTwice +main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 175 caught class art.Test1929$TestException: throwCatchTestExceptionTwice Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 - public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 222 - public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 226 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 175 + public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 234 + public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 241 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Caught art.Test1929$TestException: "throwCatchTestExceptionTwice" Test "art.Test1929$DoThrowCatchTestExceptionTwice": No error caught with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowCatchTestExceptionTwice": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler" -main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 298 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.run() throws java.lang.Exception @ line = 283 + public static void art.Test1929.run() throws java.lang.Exception @ line = 298 Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Caught error art.Test1929$TestException:"throwCatchTestExceptionNoRethrow" with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowClass": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" -main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: doThrow +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 298 caught class art.Test1929$TestException: doThrow Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.run() throws java.lang.Exception @ line = 283 + public static void art.Test1929.run() throws java.lang.Exception @ line = 298 Test "art.Test1929$DoThrowClass": Caught error art.Test1929$TestException:"doThrow" with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowClass": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowCatchBaseTestException": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" @@ -245,58 +245,58 @@ main: public static void art.Test1929.throwCatchBaseTestException() @ line = 140 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 public static void art.Test1929.throwCatchBaseTestException() @ line = 140 public void art.Test1929$DoThrowCatchBaseTestException.run() @ line = 149 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Throwing TestExceptionNoRethrow! Test "art.Test1929$DoThrowCatchBaseTestException": Caught error art.Test1929$TestExceptionNoRethrow:"ThrowTestExceptionNoRethrowHandler during throw from public static void art.Test1929.throwCatchBaseTestException() @ line = 140" with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowCatchBaseTestException": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" -main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice +main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 157 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 - public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 197 - public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 201 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 157 + public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 203 + public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 210 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Caught art.Test1929$TestException: "throwCatchBaseTestExceptionTwice" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": No error caught with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowCatchTestException": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" -main: public static void art.Test1929.throwCatchTestException() @ line = 207 caught class art.Test1929$TestException: throwCatchTestException +main: public static void art.Test1929.throwCatchTestException() @ line = 216 caught class art.Test1929$TestException: throwCatchTestException Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.throwCatchTestException() @ line = 207 - public void art.Test1929$DoThrowCatchTestException.run() @ line = 216 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929.throwCatchTestException() @ line = 216 + public void art.Test1929$DoThrowCatchTestException.run() @ line = 225 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Throwing TestExceptionNoRethrow! -Test "art.Test1929$DoThrowCatchTestException": Caught error art.Test1929$TestExceptionNoRethrow:"ThrowTestExceptionNoRethrowHandler during throw from public static void art.Test1929.throwCatchTestException() @ line = 207" with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" +Test "art.Test1929$DoThrowCatchTestException": Caught error art.Test1929$TestExceptionNoRethrow:"ThrowTestExceptionNoRethrowHandler during throw from public static void art.Test1929.throwCatchTestException() @ line = 216" with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowCatchTestException": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowCatchTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" -main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 caught class art.Test1929$TestException: throwCatchTestExceptionTwice +main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 175 caught class art.Test1929$TestException: throwCatchTestExceptionTwice Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 - public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 222 - public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 226 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 175 + public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 234 + public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 241 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Caught art.Test1929$TestException: "throwCatchTestExceptionTwice" Test "art.Test1929$DoThrowCatchTestExceptionTwice": No error caught with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowCatchTestExceptionTwice": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" -main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 298 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.run() throws java.lang.Exception @ line = 283 + public static void art.Test1929.run() throws java.lang.Exception @ line = 298 Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Caught error art.Test1929$TestException:"throwCatchTestExceptionNoRethrow" with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" diff --git a/test/1929-exception-catch-exception/smali/art/Test1929$Impl.smali b/test/1929-exception-catch-exception/smali/art/Test1929$Impl.smali index 4edd56f690ab55df8cbd04d27c17992f467cd26a..605b408d91a1bb65272568183feb27f37e3953a6 100644 --- a/test/1929-exception-catch-exception/smali/art/Test1929$Impl.smali +++ b/test/1929-exception-catch-exception/smali/art/Test1929$Impl.smali @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# The standard dx/jack/d8 all would leave the move-exception instructions outside of either catch +# The standard dx/d8 would leave the move-exception instructions outside of either catch # block. This is different from the RI which will leave the corresponding aload. # # See b/65203529 for more information. diff --git a/test/1929-exception-catch-exception/src/art/Test1929.java b/test/1929-exception-catch-exception/src/art/Test1929.java index 07d2087a0f57c63d160961a825b0503851993ac0..c0995a8468005a2f99e01ee91a98baba178c9c3c 100644 --- a/test/1929-exception-catch-exception/src/art/Test1929.java +++ b/test/1929-exception-catch-exception/src/art/Test1929.java @@ -149,52 +149,61 @@ public class Test1929 { public void run() { throwCatchBaseTestException(); } } - // dx/d8/jack all do an optimization around catch blocks that (while legal) breaks assumptions + // dx and d8 do an optimization around catch blocks that (while legal) breaks assumptions // this test relies on so we have the actual implementation be corrected smali. This does work // for RI however. - public static final class Impl { - private Impl() {} - public static void throwCatchBaseTestExceptionTwiceImpl() { - try { - try { - throw new TestException("throwCatchBaseTestExceptionTwice"); - } catch (BaseTestException t) { - System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); - if (PRINT_FULL_EXCEPTION) { - t.printStackTrace(System.out); - } - } - } catch (BaseTestException t) { - System.out.println("2nd Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); - if (PRINT_FULL_EXCEPTION) { - t.printStackTrace(System.out); - } - } - } - public static void throwCatchTestExceptionTwiceImpl() { - try { - try { - throw new TestException("throwCatchTestExceptionTwice"); - } catch (TestException t) { - System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); - if (PRINT_FULL_EXCEPTION) { - t.printStackTrace(System.out); - } - } - } catch (TestException t) { - System.out.println("2nd Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); - if (PRINT_FULL_EXCEPTION) { - t.printStackTrace(System.out); - } - } - } - } + // For reference: + + // public static final class Impl { + // private Impl() {} + // public static void throwCatchBaseTestExceptionTwiceImpl() { + // try { + // try { + // throw new TestException("throwCatchBaseTestExceptionTwice"); + // } catch (BaseTestException t) { + // System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); + // if (PRINT_FULL_EXCEPTION) { + // t.printStackTrace(System.out); + // } + // } + // } catch (BaseTestException t) { + // System.out.println("2nd Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); + // if (PRINT_FULL_EXCEPTION) { + // t.printStackTrace(System.out); + // } + // } + // } + + // public static void throwCatchTestExceptionTwiceImpl() { + // try { + // try { + // throw new TestException("throwCatchTestExceptionTwice"); + // } catch (TestException t) { + // System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); + // if (PRINT_FULL_EXCEPTION) { + // t.printStackTrace(System.out); + // } + // } + // } catch (TestException t) { + // System.out.println("2nd Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); + // if (PRINT_FULL_EXCEPTION) { + // t.printStackTrace(System.out); + // } + // } + // } + // } public static void throwCatchBaseTestExceptionTwice() { // The implementation of this has to change depending upon the runtime slightly due to compiler // optimizations present in DX/D8/Jack. - Impl.throwCatchBaseTestExceptionTwiceImpl(); + try { + Class Impl = Class.forName("art.Test1929$Impl"); + Method m = Impl.getMethod("throwCatchBaseTestExceptionTwiceImpl"); + m.invoke(null); + } catch (Exception e) { + e.printStackTrace(System.out); + } } public static class DoThrowCatchBaseTestExceptionTwice implements Runnable { @@ -219,7 +228,13 @@ public class Test1929 { public static void throwCatchTestExceptionTwice() { // The implementation of this has to change depending upon the runtime slightly due to compiler // optimizations present in DX/D8/Jack. - Impl.throwCatchTestExceptionTwiceImpl(); + try { + Class Impl = Class.forName("art.Test1929$Impl"); + Method m = Impl.getMethod("throwCatchTestExceptionTwiceImpl"); + m.invoke(null); + } catch (Exception e) { + e.printStackTrace(System.out); + } } public static class DoThrowCatchTestExceptionTwice implements Runnable { diff --git a/test/1934-jvmti-signal-thread/src/art/Test1934.java b/test/1934-jvmti-signal-thread/src/art/Test1934.java index 2a3f8db5f86c54623d87f84d66a148134380a20e..308f17b961b4202f5119569716d19f9e5704ace4 100644 --- a/test/1934-jvmti-signal-thread/src/art/Test1934.java +++ b/test/1934-jvmti-signal-thread/src/art/Test1934.java @@ -25,6 +25,8 @@ public class Test1934 { public static final boolean PRINT_STACK_TRACE = false; public static void run() throws Exception { + ensureClassesLoaded(); + System.out.println("Interrupt before start"); testInterruptBeforeStart(); @@ -53,6 +55,22 @@ public class Test1934 { testStopInNative(); } + private static void ensureInitialized(Class c) { + try { + Class.forName(c.getName()); + } catch (Exception e) { + throw new Error("Failed to initialize " + c, e); + } + } + + private static void ensureClassesLoaded() { + // Depending on timing this class might (or might not) be loaded during testing of Stop. If it + // is and the StopThread occurs inside of it we will get a ExceptionInInitializerError which is + // not what we are looking for. In order to avoid this ever happening simply initialize the + // class that can cause it early. + ensureInitialized(java.util.concurrent.locks.LockSupport.class); + } + public static void testStopBeforeStart() throws Exception { final Throwable[] out_err = new Throwable[] { null, }; final Object tst = new Object(); diff --git a/test/1935-get-set-current-frame-jit/src/Main.java b/test/1935-get-set-current-frame-jit/src/Main.java index 378aaf7a944ba031f359967a3a23f9c1a7b48028..97f09738235a8df201ab5b54d6684645f35cc7d3 100644 --- a/test/1935-get-set-current-frame-jit/src/Main.java +++ b/test/1935-get-set-current-frame-jit/src/Main.java @@ -58,6 +58,10 @@ public class Main { } public void run() { int TARGET = 42; + if (hasJit() && expectOsr && !Main.isInterpreted()) { + System.out.println("Unexpectedly in jit code prior to restarting the JIT!"); + } + startJit(); // We will suspend the thread during this loop. while (continueBusyLoop) { inBusyLoop = true; @@ -74,9 +78,11 @@ public class Main { if (hasJit()) { boolean inOsr = Main.isInOsrCode("run"); if (expectOsr && !inOsr) { - throw new Error("Expected to be in OSR but was not."); + throw new Error( + "Expected to be in OSR but was not. interpreter: " + Main.isInterpreted()); } else if (!expectOsr && inOsr) { - throw new Error("Expected not to be in OSR but was."); + throw new Error( + "Expected not to be in OSR but was. interpreter: " + Main.isInterpreted()); } } reportValue(TARGET); @@ -89,7 +95,9 @@ public class Main { public static void runGet() throws Exception { Method target = IntRunner.class.getDeclaredMethod("run"); - // Get Int + // Stop jit temporarily. It will be restarted by the test itself. + stopJit(); + // Get Int. IntRunner int_runner = new IntRunner(true); Thread target_get = new Thread(int_runner, "GetLocalInt - Target"); target_get.start(); @@ -119,7 +127,9 @@ public class Main { public static void runSet() throws Exception { Method target = IntRunner.class.getDeclaredMethod("run"); - // Set Int + // Stop jit temporarily. It will be restarted by the test itself. + stopJit(); + // Set Int. Even if we start out in JIT code somehow we should be pushed out of it. IntRunner int_runner = new IntRunner(false); Thread target_set = new Thread(int_runner, "SetLocalInt - Target"); target_set.start(); @@ -171,5 +181,7 @@ public class Main { public static native boolean isInterpreted(); public static native boolean isInOsrCode(String methodName); + public static native boolean stopJit(); + public static native boolean startJit(); public static native boolean hasJit(); } diff --git a/test/1940-ddms-ext/check b/test/1940-ddms-ext/check deleted file mode 100755 index d2c03841fc9b16ef113d2b9e4add2eece239c693..0000000000000000000000000000000000000000 --- a/test/1940-ddms-ext/check +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Need to pull out the describeException ouput since that won't be there on -# device. -sed -e '/\t.*$/d' "$2" | sed -e '/java.lang.ArrayIndexOutOfBoundsException:.*$/d' > "$2.tmp" - -./default-check "$1" "$2.tmp" diff --git a/test/1940-ddms-ext/expected.txt b/test/1940-ddms-ext/expected.txt index 1a457a01a5cb2e212497f426dc57b99f71b37dee..5af111676bf093ab34d58478bc68ccc98912c042 100644 --- a/test/1940-ddms-ext/expected.txt +++ b/test/1940-ddms-ext/expected.txt @@ -16,6 +16,10 @@ JVMTI returned chunk: Chunk(Type: 0xFADE7357, Len: 0, data: []) Sending data [1] to chunk handler 305419896 MyDdmHandler: Chunk received: Chunk(Type: 0x12345678, Len: 1, data: [1]) Got error: JVMTI_ERROR_INTERNAL +threadNotify started! +Target thread started! +Target thread finished! +threadNotify Disabled! Saw expected thread events. Expected chunk type published: 1213221190 Expected chunk type published: 1297109829 diff --git a/test/1940-ddms-ext/src-art/art/Test1940.java b/test/1940-ddms-ext/src-art/art/Test1940.java index 226fe350bd525a7b492805a8f717335ee764af86..2957f632bafdf0ac63c5bd376ce4527aa8857800 100644 --- a/test/1940-ddms-ext/src-art/art/Test1940.java +++ b/test/1940-ddms-ext/src-art/art/Test1940.java @@ -178,10 +178,14 @@ public class Test1940 { } }; DdmVmInternal.threadNotify(true); + System.out.println("threadNotify started!"); final Thread thr = new Thread(() -> { return; }, "THREAD"); thr.start(); + System.out.println("Target thread started!"); thr.join(); + System.out.println("Target thread finished!"); DdmVmInternal.threadNotify(false); + System.out.println("threadNotify Disabled!"); // Make sure we saw at least one of Thread-create, Thread name, & thread death. if (!types_seen[0] || !types_seen[1] || !types_seen[2]) { System.out.println("Didn't see expected chunks for thread creation! got: " + diff --git a/test/1947-breakpoint-redefine-deopt/check_deopt.cc b/test/1947-breakpoint-redefine-deopt/check_deopt.cc index b40b201c9c84270951e7feeac2ef27b17ce68f9e..667d8be684389d286e3bd0674fff8b2da59372b7 100644 --- a/test/1947-breakpoint-redefine-deopt/check_deopt.cc +++ b/test/1947-breakpoint-redefine-deopt/check_deopt.cc @@ -16,7 +16,7 @@ #include "jni.h" #include "art_method-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "instrumentation.h" #include "scoped_thread_state_change-inl.h" diff --git a/test/1948-obsolete-const-method-handle/build b/test/1948-obsolete-const-method-handle/build index ac0dcd97b819eb1d1799f16b7c379902d88c6b61..d0e7a8c0d8978be2b762e196ea0adfa058c16e98 100644 --- a/test/1948-obsolete-const-method-handle/build +++ b/test/1948-obsolete-const-method-handle/build @@ -20,6 +20,4 @@ set -e mkdir classes ./util-src/build-classes $PWD/classes -${DX} --dex --min-sdk-version=28 --output=classes.dex classes - -zip $TEST_NAME.jar classes.dex +./default-build --api-level 28 "$@" diff --git a/test/303-verification-stress/build b/test/303-verification-stress/build index b67eaf22619c5b2c24666fcc2b4afe825dfbaf64..87a4a851d7c6d3602daa796041789c565feb8e89 100644 --- a/test/303-verification-stress/build +++ b/test/303-verification-stress/build @@ -21,16 +21,4 @@ set -e gcc -Wall -Werror -o classes-gen classes-gen.c ./classes-gen -if [ ${USE_JACK} = "true" ]; then - # Use the default Jack commands - ./default-build -else - mkdir classes - ${JAVAC} -d classes src/*.java - - # dx needs more memory for that test so do not pass Xmx option here. - if [ ${NEED_DEX} = "true" ]; then - ${DX} --debug --dex --output=classes.dex classes - zip $TEST_NAME.jar classes.dex - fi -fi +./default-build "$@" diff --git a/test/305-other-fault-handler/fault_handler.cc b/test/305-other-fault-handler/fault_handler.cc index f04832613b5fbe30f6272404b86aef1eda22eb3b..211d142a2be551fc709abecce7efc744b10b2e42 100644 --- a/test/305-other-fault-handler/fault_handler.cc +++ b/test/305-other-fault-handler/fault_handler.cc @@ -23,9 +23,9 @@ #include #include +#include "base/globals.h" +#include "base/mem_map.h" #include "fault_handler.h" -#include "globals.h" -#include "mem_map.h" namespace art { diff --git a/test/411-optimizing-arith-mul/expected.txt b/test/411-checker-hdiv-hrem-pow2/expected.txt similarity index 100% rename from test/411-optimizing-arith-mul/expected.txt rename to test/411-checker-hdiv-hrem-pow2/expected.txt diff --git a/test/411-checker-hdiv-hrem-pow2/info.txt b/test/411-checker-hdiv-hrem-pow2/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..df1c988052fe87f4ed98f6695e1a7d18d412fd7d --- /dev/null +++ b/test/411-checker-hdiv-hrem-pow2/info.txt @@ -0,0 +1,2 @@ +Test the optimization of integer division and remainder instructions when +the denominator is power of 2 on arm64. diff --git a/test/411-checker-hdiv-hrem-pow2/src/DivTest.java b/test/411-checker-hdiv-hrem-pow2/src/DivTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a3882e7c15a0ba64c9ce04737f31872a8ba17d0f --- /dev/null +++ b/test/411-checker-hdiv-hrem-pow2/src/DivTest.java @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class DivTest { + + public static void expectEquals(T expected, T result) { + if (!expected.equals(result)) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void main() { + divInt(); + divLong(); + } + + private static void divInt() { + expectEquals(0, $noinline$IntDivBy2(0)); + expectEquals(0, $noinline$IntDivBy2(1)); + expectEquals(0, $noinline$IntDivBy2(-1)); + expectEquals(1, $noinline$IntDivBy2(2)); + expectEquals(-1, $noinline$IntDivBy2(-2)); + expectEquals(1, $noinline$IntDivBy2(3)); + expectEquals(-1, $noinline$IntDivBy2(-3)); + expectEquals(3, $noinline$IntDivBy2(7)); + expectEquals(-3, $noinline$IntDivBy2(-7)); + expectEquals(4, $noinline$IntDivBy2(8)); + expectEquals(-4, $noinline$IntDivBy2(-8)); + expectEquals(7, $noinline$IntDivBy2(0x0f)); + expectEquals(0x007f, $noinline$IntDivBy2(0x00ff)); + expectEquals(0x07ff, $noinline$IntDivBy2(0x0fff)); + expectEquals(0x007fff, $noinline$IntDivBy2(0x00ffff)); + expectEquals(0x3fffffff, $noinline$IntDivBy2(Integer.MAX_VALUE)); + expectEquals(0xc0000000, $noinline$IntDivBy2(Integer.MIN_VALUE)); + + expectEquals(0, $noinline$IntDivByMinus2(0)); + expectEquals(0, $noinline$IntDivByMinus2(1)); + expectEquals(0, $noinline$IntDivByMinus2(-1)); + expectEquals(-1, $noinline$IntDivByMinus2(2)); + expectEquals(1, $noinline$IntDivByMinus2(-2)); + expectEquals(-1, $noinline$IntDivByMinus2(3)); + expectEquals(1, $noinline$IntDivByMinus2(-3)); + expectEquals(-3, $noinline$IntDivByMinus2(7)); + expectEquals(3, $noinline$IntDivByMinus2(-7)); + expectEquals(-4, $noinline$IntDivByMinus2(8)); + expectEquals(4, $noinline$IntDivByMinus2(-8)); + expectEquals(-7, $noinline$IntDivByMinus2(0x0f)); + expectEquals(0xffffff81, $noinline$IntDivByMinus2(0x00ff)); + expectEquals(0xfffff801, $noinline$IntDivByMinus2(0x0fff)); + expectEquals(0xffff8001, $noinline$IntDivByMinus2(0x00ffff)); + expectEquals(0xc0000001, $noinline$IntDivByMinus2(Integer.MAX_VALUE)); + expectEquals(0x40000000, $noinline$IntDivByMinus2(Integer.MIN_VALUE)); + + expectEquals(0, $noinline$IntDivBy16(0)); + expectEquals(1, $noinline$IntDivBy16(16)); + expectEquals(-1, $noinline$IntDivBy16(-16)); + expectEquals(2, $noinline$IntDivBy16(33)); + expectEquals(0x000f, $noinline$IntDivBy16(0x00ff)); + expectEquals(0x00ff, $noinline$IntDivBy16(0x0fff)); + expectEquals(0x000fff, $noinline$IntDivBy16(0x00ffff)); + expectEquals(0x07ffffff, $noinline$IntDivBy16(Integer.MAX_VALUE)); + expectEquals(0xf8000000, $noinline$IntDivBy16(Integer.MIN_VALUE)); + + expectEquals(0, $noinline$IntDivByMinus16(0)); + expectEquals(-1, $noinline$IntDivByMinus16(16)); + expectEquals(1, $noinline$IntDivByMinus16(-16)); + expectEquals(-2, $noinline$IntDivByMinus16(33)); + expectEquals(0xfffffff1, $noinline$IntDivByMinus16(0x00ff)); + expectEquals(0xffffff01, $noinline$IntDivByMinus16(0x0fff)); + expectEquals(0xfffff001, $noinline$IntDivByMinus16(0x00ffff)); + expectEquals(0xf8000001, $noinline$IntDivByMinus16(Integer.MAX_VALUE)); + expectEquals(0x08000000, $noinline$IntDivByMinus16(Integer.MIN_VALUE)); + + expectEquals(0, $noinline$IntDivByIntMin(0)); + expectEquals(0, $noinline$IntDivByIntMin(1)); + expectEquals(0, $noinline$IntDivByIntMin(-1)); + expectEquals(1, $noinline$IntDivByIntMin(Integer.MIN_VALUE)); + expectEquals(0, $noinline$IntDivByIntMin(Integer.MAX_VALUE)); + } + + /// CHECK-START-ARM64: java.lang.Integer DivTest.$noinline$IntDivBy2(int) disassembly (after) + /// CHECK: add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31 + /// CHECK: asr w{{\d+}}, w{{\d+}}, #1 + private static Integer $noinline$IntDivBy2(int v) { + int r = v / 2; + return r; + } + + /// CHECK-START-ARM64: java.lang.Integer DivTest.$noinline$IntDivByMinus2(int) disassembly (after) + /// CHECK: add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31 + /// CHECK: neg w{{\d+}}, w{{\d+}}, asr #1 + private static Integer $noinline$IntDivByMinus2(int v) { + int r = v / -2; + return r; + } + + /// CHECK-START-ARM64: java.lang.Integer DivTest.$noinline$IntDivBy16(int) disassembly (after) + /// CHECK: add w{{\d+}}, w{{\d+}}, #0xf + /// CHECK: cmp w{{\d+}}, #0x0 + /// CHECK: csel w{{\d+}}, w{{\d+}}, w{{\d+}}, lt + /// CHECK: asr w{{\d+}}, w{{\d+}}, #4 + private static Integer $noinline$IntDivBy16(int v) { + int r = v / 16; + return r; + } + + /// CHECK-START-ARM64: java.lang.Integer DivTest.$noinline$IntDivByMinus16(int) disassembly (after) + /// CHECK: add w{{\d+}}, w{{\d+}}, #0xf + /// CHECK: cmp w{{\d+}}, #0x0 + /// CHECK: csel w{{\d+}}, w{{\d+}}, w{{\d+}}, lt + /// CHECK: neg w{{\d+}}, w{{\d+}}, asr #4 + private static Integer $noinline$IntDivByMinus16(int v) { + int r = v / -16; + return r; + } + + /// CHECK-START-ARM64: java.lang.Integer DivTest.$noinline$IntDivByIntMin(int) disassembly (after) + /// CHECK: mov w{{\d+}}, #0x7fffffff + /// CHECK: add w{{\d+}}, w{{\d+}}, w{{\d+}} + /// CHECK: cmp w{{\d+}}, #0x0 + /// CHECK: csel w{{\d+}}, w{{\d+}}, w{{\d+}}, lt + /// CHECK: neg w{{\d+}}, w{{\d+}}, asr #31 + private static Integer $noinline$IntDivByIntMin(int v) { + int r = v / Integer.MIN_VALUE; + return r; + } + + private static void divLong() { + expectEquals(0L, $noinline$LongDivBy2(0L)); + expectEquals(0L, $noinline$LongDivBy2(1L)); + expectEquals(0L, $noinline$LongDivBy2(-1L)); + expectEquals(1L, $noinline$LongDivBy2(2L)); + expectEquals(-1L, $noinline$LongDivBy2(-2L)); + expectEquals(1L, $noinline$LongDivBy2(3L)); + expectEquals(-1L, $noinline$LongDivBy2(-3L)); + expectEquals(3L, $noinline$LongDivBy2(7L)); + expectEquals(-3L, $noinline$LongDivBy2(-7L)); + expectEquals(4L, $noinline$LongDivBy2(8L)); + expectEquals(-4L, $noinline$LongDivBy2(-8L)); + expectEquals(7L, $noinline$LongDivBy2(0x0fL)); + expectEquals(0x007fL, $noinline$LongDivBy2(0x00ffL)); + expectEquals(0x07ffL, $noinline$LongDivBy2(0x0fffL)); + expectEquals(0x007fffL, $noinline$LongDivBy2(0x00ffffL)); + expectEquals(0x3fffffffffffffffL, $noinline$LongDivBy2(Long.MAX_VALUE)); + expectEquals(0xc000000000000000L, $noinline$LongDivBy2(Long.MIN_VALUE)); + + expectEquals(0L, $noinline$LongDivByMinus2(0)); + expectEquals(0L, $noinline$LongDivByMinus2(1L)); + expectEquals(0L, $noinline$LongDivByMinus2(-1L)); + expectEquals(-1L, $noinline$LongDivByMinus2(2L)); + expectEquals(1L, $noinline$LongDivByMinus2(-2L)); + expectEquals(-1L, $noinline$LongDivByMinus2(3L)); + expectEquals(1L, $noinline$LongDivByMinus2(-3L)); + expectEquals(-3L, $noinline$LongDivByMinus2(7L)); + expectEquals(3L, $noinline$LongDivByMinus2(-7L)); + expectEquals(-4L, $noinline$LongDivByMinus2(8L)); + expectEquals(4L, $noinline$LongDivByMinus2(-8L)); + expectEquals(-7L, $noinline$LongDivByMinus2(0x0fL)); + expectEquals(0xffffffffffffff81L, $noinline$LongDivByMinus2(0x00ffL)); + expectEquals(0xfffffffffffff801L, $noinline$LongDivByMinus2(0x0fffL)); + expectEquals(0xffffffffffff8001L, $noinline$LongDivByMinus2(0x00ffffL)); + expectEquals(0xc000000000000001L, $noinline$LongDivByMinus2(Long.MAX_VALUE)); + expectEquals(0x4000000000000000L, $noinline$LongDivByMinus2(Long.MIN_VALUE)); + + expectEquals(0L, $noinline$LongDivBy16(0)); + expectEquals(1L, $noinline$LongDivBy16(16L)); + expectEquals(-1L, $noinline$LongDivBy16(-16L)); + expectEquals(2L, $noinline$LongDivBy16(33L)); + expectEquals(0x000fL, $noinline$LongDivBy16(0x00ffL)); + expectEquals(0x00ffL, $noinline$LongDivBy16(0x0fffL)); + expectEquals(0x000fffL, $noinline$LongDivBy16(0x00ffffL)); + expectEquals(0x07ffffffffffffffL, $noinline$LongDivBy16(Long.MAX_VALUE)); + expectEquals(0xf800000000000000L, $noinline$LongDivBy16(Long.MIN_VALUE)); + + expectEquals(0L, $noinline$LongDivByMinus16(0)); + expectEquals(-1L, $noinline$LongDivByMinus16(16L)); + expectEquals(1L, $noinline$LongDivByMinus16(-16L)); + expectEquals(-2L, $noinline$LongDivByMinus16(33L)); + expectEquals(0xfffffffffffffff1L, $noinline$LongDivByMinus16(0x00ffL)); + expectEquals(0xffffffffffffff01L, $noinline$LongDivByMinus16(0x0fffL)); + expectEquals(0xfffffffffffff001L, $noinline$LongDivByMinus16(0x00ffffL)); + expectEquals(0xf800000000000001L, $noinline$LongDivByMinus16(Long.MAX_VALUE)); + expectEquals(0x0800000000000000L, $noinline$LongDivByMinus16(Long.MIN_VALUE)); + + expectEquals(0L, $noinline$LongDivByLongMin(0)); + expectEquals(0L, $noinline$LongDivByLongMin(1)); + expectEquals(0L, $noinline$LongDivByLongMin(-1)); + expectEquals(1L, $noinline$LongDivByLongMin(Long.MIN_VALUE)); + expectEquals(0L, $noinline$LongDivByLongMin(Long.MAX_VALUE)); + } + + /// CHECK-START-ARM64: java.lang.Long DivTest.$noinline$LongDivBy2(long) disassembly (after) + /// CHECK: add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63 + /// CHECK: asr x{{\d+}}, x{{\d+}}, #1 + private static Long $noinline$LongDivBy2(long v) { + long r = v / 2; + return r; + } + + /// CHECK-START-ARM64: java.lang.Long DivTest.$noinline$LongDivByMinus2(long) disassembly (after) + /// CHECK: add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63 + /// CHECK: neg x{{\d+}}, x{{\d+}}, asr #1 + private static Long $noinline$LongDivByMinus2(long v) { + long r = v / -2; + return r; + } + + /// CHECK-START-ARM64: java.lang.Long DivTest.$noinline$LongDivBy16(long) disassembly (after) + /// CHECK: add x{{\d+}}, x{{\d+}}, #0xf + /// CHECK: cmp x{{\d+}}, #0x0 + /// CHECK: csel x{{\d+}}, x{{\d+}}, x{{\d+}}, lt + /// CHECK: asr x{{\d+}}, x{{\d+}}, #4 + private static Long $noinline$LongDivBy16(long v) { + long r = v / 16; + return r; + } + + /// CHECK-START-ARM64: java.lang.Long DivTest.$noinline$LongDivByMinus16(long) disassembly (after) + /// CHECK: add x{{\d+}}, x{{\d+}}, #0xf + /// CHECK: cmp x{{\d+}}, #0x0 + /// CHECK: csel x{{\d+}}, x{{\d+}}, x{{\d+}}, lt + /// CHECK: neg x{{\d+}}, x{{\d+}}, asr #4 + private static Long $noinline$LongDivByMinus16(long v) { + long r = v / -16; + return r; + } + + /// CHECK-START-ARM64: java.lang.Long DivTest.$noinline$LongDivByLongMin(long) disassembly (after) + /// CHECK: mov x{{\d+}}, #0x7fffffffffffffff + /// CHECK: add x{{\d+}}, x{{\d+}}, x{{\d+}} + /// CHECK: cmp x{{\d+}}, #0x0 + /// CHECK: csel x{{\d+}}, x{{\d+}}, x{{\d+}}, lt + /// CHECK: neg x{{\d+}}, x{{\d+}}, asr #63 + private static Long $noinline$LongDivByLongMin(long v) { + long r = v / Long.MIN_VALUE; + return r; + } +} diff --git a/test/411-checker-hdiv-hrem-pow2/src/Main.java b/test/411-checker-hdiv-hrem-pow2/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..4b34bf1af41bd212d7894861197eab6f506742b5 --- /dev/null +++ b/test/411-checker-hdiv-hrem-pow2/src/Main.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String args[]) { + DivTest.main(); + RemTest.main(); + } +} diff --git a/test/411-checker-hdiv-hrem-pow2/src/RemTest.java b/test/411-checker-hdiv-hrem-pow2/src/RemTest.java new file mode 100644 index 0000000000000000000000000000000000000000..72725c1cd4fba9396c3242ebe2d2c97fbad60a9c --- /dev/null +++ b/test/411-checker-hdiv-hrem-pow2/src/RemTest.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class RemTest { + + public static void expectEquals(T expected, T result) { + if (!expected.equals(result)) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void main() { + remInt(); + remLong(); + } + + private static void remInt() { + expectEquals(0, $noinline$IntMod2(0)); + expectEquals(1, $noinline$IntMod2(1)); + expectEquals(-1, $noinline$IntMod2(-1)); + expectEquals(0, $noinline$IntMod2(2)); + expectEquals(0, $noinline$IntMod2(-2)); + expectEquals(1, $noinline$IntMod2(3)); + expectEquals(-1, $noinline$IntMod2(-3)); + expectEquals(1, $noinline$IntMod2(0x0f)); + expectEquals(1, $noinline$IntMod2(0x00ff)); + expectEquals(1, $noinline$IntMod2(0x00ffff)); + expectEquals(1, $noinline$IntMod2(Integer.MAX_VALUE)); + expectEquals(0, $noinline$IntMod2(Integer.MIN_VALUE)); + + expectEquals(0, $noinline$IntModMinus2(0)); + expectEquals(1, $noinline$IntModMinus2(1)); + expectEquals(-1, $noinline$IntModMinus2(-1)); + expectEquals(0, $noinline$IntModMinus2(2)); + expectEquals(0, $noinline$IntModMinus2(-2)); + expectEquals(1, $noinline$IntModMinus2(3)); + expectEquals(-1, $noinline$IntModMinus2(-3)); + expectEquals(1, $noinline$IntModMinus2(0x0f)); + expectEquals(1, $noinline$IntModMinus2(0x00ff)); + expectEquals(1, $noinline$IntModMinus2(0x00ffff)); + expectEquals(1, $noinline$IntModMinus2(Integer.MAX_VALUE)); + expectEquals(0, $noinline$IntModMinus2(Integer.MIN_VALUE)); + + expectEquals(0, $noinline$IntMod16(0)); + expectEquals(1, $noinline$IntMod16(1)); + expectEquals(1, $noinline$IntMod16(17)); + expectEquals(-1, $noinline$IntMod16(-1)); + expectEquals(0, $noinline$IntMod16(32)); + expectEquals(0, $noinline$IntMod16(-32)); + expectEquals(0x0f, $noinline$IntMod16(0x0f)); + expectEquals(0x0f, $noinline$IntMod16(0x00ff)); + expectEquals(0x0f, $noinline$IntMod16(0x00ffff)); + expectEquals(15, $noinline$IntMod16(Integer.MAX_VALUE)); + expectEquals(0, $noinline$IntMod16(Integer.MIN_VALUE)); + + expectEquals(0, $noinline$IntModMinus16(0)); + expectEquals(1, $noinline$IntModMinus16(1)); + expectEquals(1, $noinline$IntModMinus16(17)); + expectEquals(-1, $noinline$IntModMinus16(-1)); + expectEquals(0, $noinline$IntModMinus16(32)); + expectEquals(0, $noinline$IntModMinus16(-32)); + expectEquals(0x0f, $noinline$IntModMinus16(0x0f)); + expectEquals(0x0f, $noinline$IntModMinus16(0x00ff)); + expectEquals(0x0f, $noinline$IntModMinus16(0x00ffff)); + expectEquals(15, $noinline$IntModMinus16(Integer.MAX_VALUE)); + expectEquals(0, $noinline$IntModMinus16(Integer.MIN_VALUE)); + + expectEquals(0, $noinline$IntModIntMin(0)); + expectEquals(1, $noinline$IntModIntMin(1)); + expectEquals(0, $noinline$IntModIntMin(Integer.MIN_VALUE)); + expectEquals(-1, $noinline$IntModIntMin(-1)); + expectEquals(0x0f, $noinline$IntModIntMin(0x0f)); + expectEquals(0x00ff, $noinline$IntModIntMin(0x00ff)); + expectEquals(0x00ffff, $noinline$IntModIntMin(0x00ffff)); + expectEquals(Integer.MAX_VALUE, $noinline$IntModIntMin(Integer.MAX_VALUE)); + } + + /// CHECK-START-ARM64: java.lang.Integer RemTest.$noinline$IntMod2(int) disassembly (after) + /// CHECK: cmp w{{\d+}}, #0x0 + /// CHECK: and w{{\d+}}, w{{\d+}}, #0x1 + /// CHECK: cneg w{{\d+}}, w{{\d+}}, lt + private static Integer $noinline$IntMod2(int v) { + int r = v % 2; + return r; + } + + /// CHECK-START-ARM64: java.lang.Integer RemTest.$noinline$IntModMinus2(int) disassembly (after) + /// CHECK: cmp w{{\d+}}, #0x0 + /// CHECK: and w{{\d+}}, w{{\d+}}, #0x1 + /// CHECK: cneg w{{\d+}}, w{{\d+}}, lt + private static Integer $noinline$IntModMinus2(int v) { + int r = v % -2; + return r; + } + + /// CHECK-START-ARM64: java.lang.Integer RemTest.$noinline$IntMod16(int) disassembly (after) + /// CHECK: negs w{{\d+}}, w{{\d+}} + /// CHECK: and w{{\d+}}, w{{\d+}}, #0xf + /// CHECK: and w{{\d+}}, w{{\d+}}, #0xf + /// CHECK: csneg w{{\d+}}, w{{\d+}}, mi + private static Integer $noinline$IntMod16(int v) { + int r = v % 16; + return r; + } + + /// CHECK-START-ARM64: java.lang.Integer RemTest.$noinline$IntModMinus16(int) disassembly (after) + /// CHECK: negs w{{\d+}}, w{{\d+}} + /// CHECK: and w{{\d+}}, w{{\d+}}, #0xf + /// CHECK: and w{{\d+}}, w{{\d+}}, #0xf + /// CHECK: csneg w{{\d+}}, w{{\d+}}, mi + private static Integer $noinline$IntModMinus16(int v) { + int r = v % -16; + return r; + } + + /// CHECK-START-ARM64: java.lang.Integer RemTest.$noinline$IntModIntMin(int) disassembly (after) + /// CHECK: negs w{{\d+}}, w{{\d+}} + /// CHECK: and w{{\d+}}, w{{\d+}}, #0x7fffffff + /// CHECK: and w{{\d+}}, w{{\d+}}, #0x7fffffff + /// CHECK: csneg w{{\d+}}, w{{\d+}}, mi + private static Integer $noinline$IntModIntMin(int v) { + int r = v % Integer.MIN_VALUE; + return r; + } + + private static void remLong() { + expectEquals(0L, $noinline$LongMod2(0)); + expectEquals(1L, $noinline$LongMod2(1)); + expectEquals(-1L, $noinline$LongMod2(-1)); + expectEquals(0L, $noinline$LongMod2(2)); + expectEquals(0L, $noinline$LongMod2(-2)); + expectEquals(1L, $noinline$LongMod2(3)); + expectEquals(-1L, $noinline$LongMod2(-3)); + expectEquals(1L, $noinline$LongMod2(0x0f)); + expectEquals(1L, $noinline$LongMod2(0x00ff)); + expectEquals(1L, $noinline$LongMod2(0x00ffff)); + expectEquals(1L, $noinline$LongMod2(0x00ffffff)); + expectEquals(1L, $noinline$LongMod2(0x00ffffffffL)); + expectEquals(1L, $noinline$LongMod2(Long.MAX_VALUE)); + expectEquals(0L, $noinline$LongMod2(Long.MIN_VALUE)); + + expectEquals(0L, $noinline$LongModMinus2(0)); + expectEquals(1L, $noinline$LongModMinus2(1)); + expectEquals(-1L, $noinline$LongModMinus2(-1)); + expectEquals(0L, $noinline$LongModMinus2(2)); + expectEquals(0L, $noinline$LongModMinus2(-2)); + expectEquals(1L, $noinline$LongModMinus2(3)); + expectEquals(-1L, $noinline$LongModMinus2(-3)); + expectEquals(1L, $noinline$LongModMinus2(0x0f)); + expectEquals(1L, $noinline$LongModMinus2(0x00ff)); + expectEquals(1L, $noinline$LongModMinus2(0x00ffff)); + expectEquals(1L, $noinline$LongModMinus2(0x00ffffff)); + expectEquals(1L, $noinline$LongModMinus2(0x00ffffffffL)); + expectEquals(1L, $noinline$LongModMinus2(Long.MAX_VALUE)); + expectEquals(0L, $noinline$LongModMinus2(Long.MIN_VALUE)); + + expectEquals(0L, $noinline$LongMod16(0)); + expectEquals(1L, $noinline$LongMod16(1)); + expectEquals(1L, $noinline$LongMod16(17)); + expectEquals(-1L, $noinline$LongMod16(-1)); + expectEquals(0L, $noinline$LongMod16(32)); + expectEquals(0L, $noinline$LongMod16(-32)); + expectEquals(0x0fL, $noinline$LongMod16(0x0f)); + expectEquals(0x0fL, $noinline$LongMod16(0x00ff)); + expectEquals(0x0fL, $noinline$LongMod16(0x00ffff)); + expectEquals(0x0fL, $noinline$LongMod16(0x00ffffff)); + expectEquals(0x0fL, $noinline$LongMod16(0x00ffffffffL)); + expectEquals(15L, $noinline$LongMod16(Long.MAX_VALUE)); + expectEquals(0L, $noinline$LongMod16(Long.MIN_VALUE)); + + expectEquals(0L, $noinline$LongModMinus16(0)); + expectEquals(1L, $noinline$LongModMinus16(1)); + expectEquals(1L, $noinline$LongModMinus16(17)); + expectEquals(-1L, $noinline$LongModMinus16(-1)); + expectEquals(0L, $noinline$LongModMinus16(32)); + expectEquals(0L, $noinline$LongModMinus16(-32)); + expectEquals(0x0fL, $noinline$LongModMinus16(0x0f)); + expectEquals(0x0fL, $noinline$LongModMinus16(0x00ff)); + expectEquals(0x0fL, $noinline$LongModMinus16(0x00ffff)); + expectEquals(0x0fL, $noinline$LongModMinus16(0x00ffffff)); + expectEquals(0x0fL, $noinline$LongModMinus16(0x00ffffffffL)); + expectEquals(15L, $noinline$LongModMinus16(Long.MAX_VALUE)); + expectEquals(0L, $noinline$LongModMinus16(Long.MIN_VALUE)); + + expectEquals(0L, $noinline$LongModLongMin(0)); + expectEquals(1L, $noinline$LongModLongMin(1)); + expectEquals(0L, $noinline$LongModLongMin(Long.MIN_VALUE)); + expectEquals(-1L, $noinline$LongModLongMin(-1)); + expectEquals(0x0fL, $noinline$LongModLongMin(0x0f)); + expectEquals(0x00ffL, $noinline$LongModLongMin(0x00ff)); + expectEquals(0x00ffffL, $noinline$LongModLongMin(0x00ffff)); + expectEquals(0x00ffffffL, $noinline$LongModLongMin(0x00ffffff)); + expectEquals(0x00ffffffffL, $noinline$LongModLongMin(0x00ffffffffL)); + expectEquals(Long.MAX_VALUE, $noinline$LongModLongMin(Long.MAX_VALUE)); + } + + /// CHECK-START-ARM64: java.lang.Long RemTest.$noinline$LongMod2(long) disassembly (after) + /// CHECK: cmp x{{\d+}}, #0x0 + /// CHECK: and x{{\d+}}, x{{\d+}}, #0x1 + /// CHECK: cneg x{{\d+}}, x{{\d+}}, lt + private static Long $noinline$LongMod2(long v) { + long r = v % 2; + return r; + } + + /// CHECK-START-ARM64: java.lang.Long RemTest.$noinline$LongModMinus2(long) disassembly (after) + /// CHECK: cmp x{{\d+}}, #0x0 + /// CHECK: and x{{\d+}}, x{{\d+}}, #0x1 + /// CHECK: cneg x{{\d+}}, x{{\d+}}, lt + private static Long $noinline$LongModMinus2(long v) { + long r = v % -2; + return r; + } + + /// CHECK-START-ARM64: java.lang.Long RemTest.$noinline$LongMod16(long) disassembly (after) + /// CHECK: negs x{{\d+}}, x{{\d+}} + /// CHECK: and x{{\d+}}, x{{\d+}}, #0xf + /// CHECK: and x{{\d+}}, x{{\d+}}, #0xf + /// CHECK: csneg x{{\d+}}, x{{\d+}}, mi + private static Long $noinline$LongMod16(long v) { + long r = v % 16; + return r; + } + + /// CHECK-START-ARM64: java.lang.Long RemTest.$noinline$LongModMinus16(long) disassembly (after) + /// CHECK: negs x{{\d+}}, x{{\d+}} + /// CHECK: and x{{\d+}}, x{{\d+}}, #0xf + /// CHECK: and x{{\d+}}, x{{\d+}}, #0xf + /// CHECK: csneg x{{\d+}}, x{{\d+}}, mi + private static Long $noinline$LongModMinus16(long v) { + long r = v % -16; + return r; + } + + /// CHECK-START-ARM64: java.lang.Long RemTest.$noinline$LongModLongMin(long) disassembly (after) + /// CHECK: negs x{{\d+}}, x{{\d+}} + /// CHECK: and x{{\d+}}, x{{\d+}}, #0x7fffffffffffffff + /// CHECK: and x{{\d+}}, x{{\d+}}, #0x7fffffffffffffff + /// CHECK: csneg x{{\d+}}, x{{\d+}}, mi + private static Long $noinline$LongModLongMin(long v) { + long r = v % Long.MIN_VALUE; + return r; + } +} diff --git a/test/411-optimizing-arith-mul/info.txt b/test/411-optimizing-arith-mul/info.txt deleted file mode 100644 index 10155512f09871412a5aedabeac95bbac4bd7674..0000000000000000000000000000000000000000 --- a/test/411-optimizing-arith-mul/info.txt +++ /dev/null @@ -1 +0,0 @@ -Tests for basic arithmethic operations. diff --git a/test/414-optimizing-arith-sub/expected.txt b/test/411-optimizing-arith/expected.txt similarity index 100% rename from test/414-optimizing-arith-sub/expected.txt rename to test/411-optimizing-arith/expected.txt diff --git a/test/411-optimizing-arith/info.txt b/test/411-optimizing-arith/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..42be5d564f9b5183c2c362ed96e1647d6282a2a7 --- /dev/null +++ b/test/411-optimizing-arith/info.txt @@ -0,0 +1,7 @@ +Tests for basic arithmethic operations: + - multiply, + - subtract, + - negate, + - division, + - modulo (rem), + - shifts. diff --git a/test/417-optimizing-arith-div/src/Main.java b/test/411-optimizing-arith/src/DivTest.java similarity index 98% rename from test/417-optimizing-arith-div/src/Main.java rename to test/411-optimizing-arith/src/DivTest.java index 68e89b3eb23b9c4d6a6d056c965de82edeb966ff..7696d0a806bf2af2a8f4622942d0749686335ff3 100644 --- a/test/417-optimizing-arith-div/src/Main.java +++ b/test/411-optimizing-arith/src/DivTest.java @@ -16,7 +16,7 @@ // Note that $opt$ is a marker for the optimizing compiler to test // it does compile the method. -public class Main { +public class DivTest { public static void expectEquals(int expected, int result) { if (expected != result) { @@ -98,11 +98,7 @@ public class Main { } } - public static void main(String[] args) { - div(); - } - - public static void div() { + public static void main() { divInt(); divLong(); divFloat(); diff --git a/test/411-optimizing-arith/src/Main.java b/test/411-optimizing-arith/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..e1a43d3b5761ba99a473aa0b748cf8168ddd0e57 --- /dev/null +++ b/test/411-optimizing-arith/src/Main.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String args[]) { + MulTest.main(); + SubTest.main(); + NegTest.main(); + DivTest.main(); + RemTest.main(); + ShiftsTest.main(); + } +} diff --git a/test/411-optimizing-arith-mul/src/Main.java b/test/411-optimizing-arith/src/MulTest.java similarity index 93% rename from test/411-optimizing-arith-mul/src/Main.java rename to test/411-optimizing-arith/src/MulTest.java index 60e418e1e534e55ca138bcd169c6472f0df13c2e..b9bffca0d1aaa926a01259ed4624c06de7cd398a 100644 --- a/test/411-optimizing-arith-mul/src/Main.java +++ b/test/411-optimizing-arith/src/MulTest.java @@ -16,7 +16,7 @@ // Note that $opt$ is a marker for the optimizing compiler to test // it does compile the method. -public class Main { +public class MulTest { public static void expectEquals(int expected, int result) { if (expected != result) { @@ -72,11 +72,7 @@ public class Main { } } - public static void main(String[] args) { - mul(); - } - - public static void mul() { + public static void main() { mulInt(); mulLong(); mulFloat(); @@ -129,9 +125,12 @@ public class Main { expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(-2F, 3.40282346638528860e+38F)); expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(2F, Float.NEGATIVE_INFINITY)); expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(-2F, Float.NEGATIVE_INFINITY)); - expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY)); - expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY)); - expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY)); + expectEquals(Float.NEGATIVE_INFINITY, + $opt$Mul(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY)); + expectEquals(Float.POSITIVE_INFINITY, + $opt$Mul(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY)); + expectEquals(Float.POSITIVE_INFINITY, + $opt$Mul(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY)); } private static void mulDouble() { diff --git a/test/415-optimizing-arith-neg/src/Main.java b/test/411-optimizing-arith/src/NegTest.java similarity index 92% rename from test/415-optimizing-arith-neg/src/Main.java rename to test/411-optimizing-arith/src/NegTest.java index c53b639d40ad892f3c03cd7c213839a13e2cecd6..83047269bb25307219e5959a6144ad61b63080d0 100644 --- a/test/415-optimizing-arith-neg/src/Main.java +++ b/test/411-optimizing-arith/src/NegTest.java @@ -17,7 +17,7 @@ // Note that $opt$ is a marker for the optimizing compiler to test // it does compile the method, and that $noinline$ is a marker to // test that it does not inline it. -public class Main { +public class NegTest { public static void assertEquals(int expected, int result) { if (expected != result) { @@ -67,7 +67,7 @@ public class Main { } } - public static void main(String[] args) { + public static void main() { negInt(); $opt$noinline$InplaceNegOneInt(1); @@ -169,55 +169,29 @@ public class Main { } - static boolean doThrow = false; - private static void $opt$noinline$InplaceNegOneInt(int a) { - if (doThrow) { - // Try defeating inlining. - throw new Error(); - } a = -a; assertEquals(-1, a); } private static void $opt$noinline$InplaceNegOneLong(long a) { - if (doThrow) { - // Try defeating inlining. - throw new Error(); - } a = -a; assertEquals(-1L, a); } private static int $opt$noinline$NegInt(int a){ - if (doThrow) { - // Try defeating inlining. - throw new Error(); - } return -a; } private static long $opt$noinline$NegLong(long a){ - if (doThrow) { - // Try defeating inlining. - throw new Error(); - } return -a; } private static float $opt$noinline$NegFloat(float a){ - if (doThrow) { - // Try defeating inlining. - throw new Error(); - } return -a; } private static double $opt$noinline$NegDouble(double a){ - if (doThrow) { - // Try defeating inlining. - throw new Error(); - } return -a; } } diff --git a/test/428-optimizing-arith-rem/src/Main.java b/test/411-optimizing-arith/src/RemTest.java similarity index 73% rename from test/428-optimizing-arith-rem/src/Main.java rename to test/411-optimizing-arith/src/RemTest.java index 3f77318e6ca4c3d7e85017a60909e5bd94e2bd2c..287f5d879976cfacefa9942be8854d0e152859b7 100644 --- a/test/428-optimizing-arith-rem/src/Main.java +++ b/test/411-optimizing-arith/src/RemTest.java @@ -14,9 +14,9 @@ * limitations under the License. */ -public class Main { +public class RemTest { - public static void main(String[] args) { + public static void main() { remInt(); remLong(); } @@ -89,6 +89,34 @@ public class Main { expectDivisionByZero(5L); expectDivisionByZero(Long.MAX_VALUE); expectDivisionByZero(Long.MIN_VALUE); + + expectEquals(0, $noinline$RemLoaded1(0)); + expectEquals(0, $noinline$RemLoaded1(1)); + expectEquals(0, $noinline$RemLoaded1(-1)); + expectEquals(0, $noinline$RemLoaded1(12345)); + expectEquals(0, $noinline$RemLoaded1(Integer.MAX_VALUE)); + expectEquals(0, $noinline$RemLoaded1(Integer.MIN_VALUE)); + + expectEquals(0, $noinline$RemLoadedN1(0)); + expectEquals(0, $noinline$RemLoadedN1(1)); + expectEquals(0, $noinline$RemLoadedN1(-1)); + expectEquals(0, $noinline$RemLoadedN1(12345)); + expectEquals(0, $noinline$RemLoadedN1(Integer.MAX_VALUE)); + expectEquals(0, $noinline$RemLoadedN1(Integer.MIN_VALUE)); + + expectEquals(0L, $noinline$RemLoaded1(0L)); + expectEquals(0L, $noinline$RemLoaded1(1L)); + expectEquals(0L, $noinline$RemLoaded1(-1L)); + expectEquals(0L, $noinline$RemLoaded1(12345L)); + expectEquals(0L, $noinline$RemLoaded1(Long.MAX_VALUE)); + expectEquals(0L, $noinline$RemLoaded1(Long.MIN_VALUE)); + + expectEquals(0L, $noinline$RemLoadedN1(0L)); + expectEquals(0L, $noinline$RemLoadedN1(1L)); + expectEquals(0L, $noinline$RemLoadedN1(-1L)); + expectEquals(0L, $noinline$RemLoadedN1(12345L)); + expectEquals(0L, $noinline$RemLoadedN1(Long.MAX_VALUE)); + expectEquals(0L, $noinline$RemLoadedN1(Long.MIN_VALUE)); } static int $opt$Rem(int a, int b) { @@ -99,6 +127,26 @@ public class Main { return a % 0; } + static int $noinline$RemLoaded1(int a) { + int[] v = {25, 1}; + return a % v[1]; + } + + static int $noinline$RemLoadedN1(int a) { + int [] v = {25, -1}; + return a % v[1]; + } + + static long $noinline$RemLoaded1(long a) { + long[] v = {25, 1}; + return a % v[1]; + } + + static long $noinline$RemLoadedN1(long a) { + long [] v = {25, -1}; + return a % v[1]; + } + // Modulo by literals != 0 should not generate checks. static int $opt$RemConst(int a) { return a % 4; diff --git a/test/431-optimizing-arith-shifts/src/Main.java b/test/411-optimizing-arith/src/ShiftsTest.java similarity index 99% rename from test/431-optimizing-arith-shifts/src/Main.java rename to test/411-optimizing-arith/src/ShiftsTest.java index b7a112f6a30e04794f4ac52b2be5e9617e4d0a2e..139ff70bf02be84735b3895f7c2d75720df11bef 100644 --- a/test/431-optimizing-arith-shifts/src/Main.java +++ b/test/411-optimizing-arith/src/ShiftsTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -public class Main { +public class ShiftsTest { public static void expectEquals(int expected, int result) { if (expected != result) { @@ -28,7 +28,7 @@ public class Main { } } - public static void main(String[] args) { + public static void main() { testShlInt(); testShlLong(); testShrInt(); diff --git a/test/414-optimizing-arith-sub/src/Main.java b/test/411-optimizing-arith/src/SubTest.java similarity index 99% rename from test/414-optimizing-arith-sub/src/Main.java rename to test/411-optimizing-arith/src/SubTest.java index b4531cdfd43e270f6af288a39a910a40f33648c0..9c9ea92f20a555aa33606680c031132f65559834 100644 --- a/test/414-optimizing-arith-sub/src/Main.java +++ b/test/411-optimizing-arith/src/SubTest.java @@ -16,7 +16,7 @@ // Note that $opt$ is a marker for the optimizing compiler to test // it does compile the method. -public class Main { +public class SubTest { public static void expectEquals(int expected, int result) { if (expected != result) { @@ -70,7 +70,7 @@ public class Main { } } - public static void main(String[] args) { + public static void main() { subInt(); subLong(); subFloat(); diff --git a/test/414-optimizing-arith-sub/info.txt b/test/414-optimizing-arith-sub/info.txt deleted file mode 100644 index 1eaa14887b7af709fbfcbd6c7b5e00ddd684f53e..0000000000000000000000000000000000000000 --- a/test/414-optimizing-arith-sub/info.txt +++ /dev/null @@ -1 +0,0 @@ -Subtraction tests. diff --git a/test/415-optimizing-arith-neg/expected.txt b/test/415-optimizing-arith-neg/expected.txt deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/test/415-optimizing-arith-neg/info.txt b/test/415-optimizing-arith-neg/info.txt deleted file mode 100644 index 8494aad938cbd4578ac797cb98022b6299d58c42..0000000000000000000000000000000000000000 --- a/test/415-optimizing-arith-neg/info.txt +++ /dev/null @@ -1 +0,0 @@ -Tests for arithmetic negation operations. diff --git a/test/417-optimizing-arith-div/expected.txt b/test/417-optimizing-arith-div/expected.txt deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/test/417-optimizing-arith-div/info.txt b/test/417-optimizing-arith-div/info.txt deleted file mode 100644 index 1374b0ffb337fb15819ffb5a841dfb4c828f93a5..0000000000000000000000000000000000000000 --- a/test/417-optimizing-arith-div/info.txt +++ /dev/null @@ -1 +0,0 @@ -Tests for division operation. diff --git a/test/428-optimizing-arith-rem/expected.txt b/test/428-optimizing-arith-rem/expected.txt deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/test/428-optimizing-arith-rem/info.txt b/test/428-optimizing-arith-rem/info.txt deleted file mode 100644 index 3e37ffeee8496bda004f5aeb29f0a5d704d462c5..0000000000000000000000000000000000000000 --- a/test/428-optimizing-arith-rem/info.txt +++ /dev/null @@ -1 +0,0 @@ -Tests for modulo (rem) operation. diff --git a/test/431-optimizing-arith-shifts/expected.txt b/test/431-optimizing-arith-shifts/expected.txt deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/test/431-optimizing-arith-shifts/info.txt b/test/431-optimizing-arith-shifts/info.txt deleted file mode 100644 index 14ff264662fd5b2da119e09fb7f2ad1a43209c8c..0000000000000000000000000000000000000000 --- a/test/431-optimizing-arith-shifts/info.txt +++ /dev/null @@ -1 +0,0 @@ -Tests for shift operations. diff --git a/test/441-checker-inliner/src/Main.java b/test/441-checker-inliner/src/Main.java index aff4a0717e6b53e3bc02da4468713f51ae9404b6..3ccfce4de1d144982b177c8e9215dc20fdbf8b95 100644 --- a/test/441-checker-inliner/src/Main.java +++ b/test/441-checker-inliner/src/Main.java @@ -152,10 +152,12 @@ public class Main { /// CHECK-DAG: <> InvokeStaticOrDirect /// CHECK-DAG: Return [<>] + // + // Intrinsic directly simplified into Abs and evaluated! + // /// CHECK-START: int Main.InlinedIntrinsicsAreStillIntrinsic() inliner (after) - /// CHECK-DAG: <> IntConstant -1 - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsInt - /// CHECK-DAG: Return [<>] + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: Return [<>] public static int InlinedIntrinsicsAreStillIntrinsic() { return returnAbs(-1); diff --git a/test/442-checker-constant-folding/build b/test/442-checker-constant-folding/build deleted file mode 100755 index 947ec9a560faa77e811f9ed8b6e619f0723c4938..0000000000000000000000000000000000000000 --- a/test/442-checker-constant-folding/build +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -# -# Copyright 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This checker test is incompatible with jack bytecode output, -# so force it to use javac/dx. -export USE_JACK=false -# Also disable desugar because it is missing in jack platform builds. -export DESUGAR=false - -# See b/65168732 -export DX=$ANDROID_HOST_OUT/bin/dx - -./default-build "$@" diff --git a/test/442-checker-constant-folding/src/Main.java b/test/442-checker-constant-folding/src/Main.java index 95c19eaabcd67d4f0adba2c83a587d05abe016d8..3d9294304d83f73c0a3464bc9cf10a92bff68bb5 100644 --- a/test/442-checker-constant-folding/src/Main.java +++ b/test/442-checker-constant-folding/src/Main.java @@ -991,15 +991,23 @@ public class Main { /// CHECK-START: int Main.StaticConditionNulls() constant_folding$after_inlining (before) /// CHECK-DAG: <> NullConstant /// CHECK-DAG: <> NotEqual [<>,<>] - /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<>] + /// CHECK-DAG: If [<>] /// CHECK-START: int Main.StaticConditionNulls() constant_folding$after_inlining (after) /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<>] + /// CHECK-DAG: If [<>] /// CHECK-START: int Main.StaticConditionNulls() constant_folding$after_inlining (after) /// CHECK-NOT: NotEqual + /// CHECK-START: int Main.StaticConditionNulls() dead_code_elimination$after_inlining (before) + /// CHECK-DAG: <> Phi + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.StaticConditionNulls() dead_code_elimination$after_inlining (after) + /// CHECK-DAG: <> IntConstant 5 + /// CHECK-DAG: Return [<>] + private static Object getNull() { return null; } @@ -1142,6 +1150,41 @@ public class Main { return arg % 1; } + /// CHECK-START: int Main.RemN1(int) constant_folding (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant -1 + /// CHECK-DAG: <> Rem [<>,<>] + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.RemN1(int) constant_folding (after) + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.RemN1(int) constant_folding (after) + /// CHECK-NOT: Rem + + public static int RemN1(int arg) { + return arg % -1; + } + + /// CHECK-START: long Main.Rem1(long) constant_folding (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> LongConstant 1 + /// CHECK-DAG: <> DivZeroCheck [<>] + /// CHECK-DAG: <> Rem [<>,<>] + /// CHECK-DAG: Return [<>] + + /// CHECK-START: long Main.Rem1(long) constant_folding (after) + /// CHECK-DAG: <> LongConstant 0 + /// CHECK-DAG: Return [<>] + + /// CHECK-START: long Main.Rem1(long) constant_folding (after) + /// CHECK-NOT: Rem + + public static long Rem1(long arg) { + return arg % 1; + } + /// CHECK-START: long Main.RemN1(long) constant_folding (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> LongConstant -1 @@ -1589,7 +1632,26 @@ public class Main { assertIntEquals(-1, OrAllOnes(arbitrary)); assertLongEquals(0, Rem0(arbitrary)); assertIntEquals(0, Rem1(arbitrary)); + assertIntEquals(0, Rem1(0)); + assertIntEquals(0, Rem1(-1)); + assertIntEquals(0, Rem1(Integer.MAX_VALUE)); + assertIntEquals(0, Rem1(Integer.MIN_VALUE)); + assertIntEquals(0, RemN1(arbitrary)); + assertIntEquals(0, RemN1(0)); + assertIntEquals(0, RemN1(-1)); + assertIntEquals(0, RemN1(Integer.MAX_VALUE)); + assertIntEquals(0, RemN1(Integer.MIN_VALUE)); + assertIntEquals(0, RemN1(arbitrary)); + assertLongEquals(0, Rem1((long)arbitrary)); + assertLongEquals(0, Rem1(0L)); + assertLongEquals(0, Rem1(-1L)); + assertLongEquals(0, Rem1(Long.MAX_VALUE)); + assertLongEquals(0, Rem1(Long.MIN_VALUE)); assertLongEquals(0, RemN1(arbitrary)); + assertLongEquals(0, RemN1(0L)); + assertLongEquals(0, RemN1(-1L)); + assertLongEquals(0, RemN1(Long.MAX_VALUE)); + assertLongEquals(0, RemN1(Long.MIN_VALUE)); assertIntEquals(0, Shl0(arbitrary)); assertLongEquals(0, ShlLong0WithInt(arbitrary)); assertLongEquals(0, Shr0(arbitrary)); diff --git a/test/445-checker-licm/src/Main.java b/test/445-checker-licm/src/Main.java index 9635e7027875571e0cf684bce87010895568e882..517aacd9f20d475c657c9df74593cc9251f03508 100644 --- a/test/445-checker-licm/src/Main.java +++ b/test/445-checker-licm/src/Main.java @@ -153,14 +153,17 @@ public class Main { return result; } - /// CHECK-START: int Main.invariantBoundIntrinsic(int) licm (before) + /// CHECK-START: int Main.invariantBoundIntrinsic(int) instruction_simplifier (before) /// CHECK-DAG: InvokeStaticOrDirect loop:{{B\d+}} + // + /// CHECK-START: int Main.invariantBoundIntrinsic(int) licm (before) + /// CHECK-DAG: Abs loop:{{B\d+}} /// CHECK-START: int Main.invariantBoundIntrinsic(int) licm (after) - /// CHECK-NOT: InvokeStaticOrDirect loop:{{B\d+}} + /// CHECK-NOT: Abs loop:{{B\d+}} /// CHECK-START: int Main.invariantBoundIntrinsic(int) licm (after) - /// CHECK-DAG: InvokeStaticOrDirect loop:none + /// CHECK-DAG: Abs loop:none public static int invariantBoundIntrinsic(int x) { int result = 0; @@ -172,14 +175,17 @@ public class Main { return result; } - /// CHECK-START: int Main.invariantBodyIntrinsic(int, int) licm (before) + /// CHECK-START: int Main.invariantBodyIntrinsic(int, int) instruction_simplifier (before) /// CHECK-DAG: InvokeStaticOrDirect loop:{{B\d+}} + /// CHECK-START: int Main.invariantBodyIntrinsic(int, int) licm (before) + /// CHECK-DAG: Max loop:{{B\d+}} + /// CHECK-START: int Main.invariantBodyIntrinsic(int, int) licm (after) - /// CHECK-NOT: InvokeStaticOrDirect loop:{{B\d+}} + /// CHECK-NOT: Max loop:{{B\d+}} /// CHECK-START: int Main.invariantBodyIntrinsic(int, int) licm (after) - /// CHECK-DAG: InvokeStaticOrDirect loop:none + /// CHECK-DAG: Max loop:none public static int invariantBodyIntrinsic(int x, int y) { int result = 0; diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java index 4868355b90561513d551cff66ef999cb0da6e899..1144366dfb8c6fbac81fc973a887033b56df6cb6 100644 --- a/test/449-checker-bce/src/Main.java +++ b/test/449-checker-bce/src/Main.java @@ -1151,8 +1151,7 @@ public class Main { /// CHECK-DAG: <> NullCheck [<>] loop:<> /// CHECK-DAG: <> ArrayLength [<>] loop:<> /// CHECK-DAG: <> BoundsCheck [<>,<>] loop:<> - // Note: The ArtMethod* (typed as int or long) is optional after sharpening. - /// CHECK-DAG: InvokeStaticOrDirect [<>{{(,[ij]\d+)?}}] loop:<> + /// CHECK-DAG: Abs [<>] loop:<> /// CHECK-DAG: <> Phi loop:<> /// CHECK-DAG: <> Phi loop:<> /// CHECK-DAG: <> StaticFieldGet loop:none @@ -1165,8 +1164,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> // Array reference ..[j] still in inner loop, with a direct index. /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> - // Note: The ArtMethod* (typed as int or long) is optional after sharpening. - /// CHECK-DAG: InvokeStaticOrDirect [<>{{(,[ij]\d+)?}}] loop:<> + /// CHECK-DAG: Abs [<>] loop:<> /// CHECK-DAG: <> Phi loop:<> /// CHECK-DAG: <> Phi loop:<> // Synthetic phi. diff --git a/test/450-checker-types/build b/test/450-checker-types/build deleted file mode 100755 index 372195567057d9a4f8b4a5aec1198cd11e9c338e..0000000000000000000000000000000000000000 --- a/test/450-checker-types/build +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -# -# Copyright 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This checker test is incompatible with jack bytecode output, -# so force it to use javac/dx. -export USE_JACK=false -# Also disable desugar because it is missing in jack platform builds. -export DESUGAR=false - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/450-checker-types/smali/Main2.smali b/test/450-checker-types/smali/Main2.smali new file mode 100644 index 0000000000000000000000000000000000000000..5f11be3d29337cf27d8b47cf0df12469baa21d87 --- /dev/null +++ b/test/450-checker-types/smali/Main2.smali @@ -0,0 +1,55 @@ +# Copyright 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class LMain2; +.super Ljava/lang/Object; +.source "Main2.java" + + +# direct methods +.method constructor ()V + .registers 1 + + .prologue + .line 17 + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method + +## CHECK-START: void Main2.testArraySimpleRemove() instruction_simplifier (before) +## CHECK: CheckCast + +## CHECK-START: void Main2.testArraySimpleRemove() instruction_simplifier (after) +## CHECK-NOT: CheckCast + +.method static testArraySimpleRemove()V + .registers 3 + + .prologue + .line 19 + const/16 v2, 0xa + + new-array v0, v2, [LSubclassA; + + .local v0, "b":[LSuper; + move-object v1, v0 + + .line 20 + check-cast v1, [LSubclassA; + + .line 21 + .local v1, "c":[LSubclassA; + return-void +.end method diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java index ae0fdbe576216c45a9532267aab81bfa50443dc0..2351c2333b2632d4744438a9d03ad98bbe142b76 100644 --- a/test/450-checker-types/src/Main.java +++ b/test/450-checker-types/src/Main.java @@ -463,16 +463,6 @@ public class Main { public SubclassA $noinline$getSubclass() { throw new RuntimeException(); } - /// CHECK-START: void Main.testArraySimpleRemove() instruction_simplifier (before) - /// CHECK: CheckCast - - /// CHECK-START: void Main.testArraySimpleRemove() instruction_simplifier (after) - /// CHECK-NOT: CheckCast - public void testArraySimpleRemove() { - Super[] b = new SubclassA[10]; - SubclassA[] c = (SubclassA[])b; - } - /// CHECK-START: void Main.testInvokeSimpleRemove() instruction_simplifier (before) /// CHECK: CheckCast @@ -709,7 +699,7 @@ public class Main { /// CHECK-DAG: <> NullCheck [<>] klass:SubclassA /// CHECK-DAG: InvokeVirtual [<>] method_name:java.lang.Object.hashCode - public void testExplicitArgumentMoreSpecific(SubclassA obj) { + public static void testExplicitArgumentMoreSpecific(SubclassA obj) { // Inlining a method will build it with reference types from its signature, // here the callee graph is built with Super as the type of its only argument. // Running RTP after its ParameterValue instructions are replaced with actual diff --git a/test/455-checker-gvn/src/Main.java b/test/455-checker-gvn/src/Main.java index 6df57fdeefdb488a4e8505e074ed771a753a397f..74b183980880d8f3e918885083abde3940da51ee 100644 --- a/test/455-checker-gvn/src/Main.java +++ b/test/455-checker-gvn/src/Main.java @@ -40,7 +40,7 @@ public class Main { /// CHECK: StaticFieldGet /// CHECK: StaticFieldGet /// CHECK: Mul - /// CHECK: InvokeStaticOrDirect + /// CHECK: Abs /// CHECK: StaticFieldGet /// CHECK: StaticFieldGet /// CHECK: Mul @@ -50,7 +50,7 @@ public class Main { /// CHECK: StaticFieldGet /// CHECK: StaticFieldGet /// CHECK: Mul - /// CHECK: InvokeStaticOrDirect + /// CHECK: Abs /// CHECK-NOT: StaticFieldGet /// CHECK-NOT: StaticFieldGet /// CHECK-NOT: Mul @@ -66,13 +66,13 @@ public class Main { } /// CHECK-START: int Main.directIntrinsic(int) GVN (before) - /// CHECK: InvokeStaticOrDirect - /// CHECK: InvokeStaticOrDirect + /// CHECK: Abs + /// CHECK: Abs /// CHECK: Add /// CHECK-START: int Main.directIntrinsic(int) GVN (after) - /// CHECK: InvokeStaticOrDirect - /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK: Abs + /// CHECK-NOT: Abs /// CHECK: Add public static int directIntrinsic(int x) { diff --git a/test/458-checker-instruct-simplification/build b/test/458-checker-instruct-simplification/build deleted file mode 100755 index 372195567057d9a4f8b4a5aec1198cd11e9c338e..0000000000000000000000000000000000000000 --- a/test/458-checker-instruct-simplification/build +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -# -# Copyright 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This checker test is incompatible with jack bytecode output, -# so force it to use javac/dx. -export USE_JACK=false -# Also disable desugar because it is missing in jack platform builds. -export DESUGAR=false - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/458-checker-instruct-simplification/smali/SmaliTests2.smali b/test/458-checker-instruct-simplification/smali/SmaliTests2.smali new file mode 100644 index 0000000000000000000000000000000000000000..99fb0495103cf9a6ae6a42cd9df7c3536b6bb516 --- /dev/null +++ b/test/458-checker-instruct-simplification/smali/SmaliTests2.smali @@ -0,0 +1,305 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class public LSmaliTests2; +.super Ljava/lang/Object; + +## CHECK-START: int SmaliTests2.$noinline$XorAllOnes(int) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant -1 +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int SmaliTests2.$noinline$XorAllOnes(int) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> Not [<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int SmaliTests2.$noinline$XorAllOnes(int) instruction_simplifier (after) +## CHECK-NOT: Xor + +# Original java source: +# +# return arg ^ -1; +# +.method public static $noinline$XorAllOnes(I)I + .registers 2 + .param p0, "arg" # I + + .prologue + .line 658 + xor-int/lit8 v0, p0, -0x1 + + return v0 +.end method + +# Test simplification of the `~~var` pattern. +# The transformation tested is implemented in `InstructionSimplifierVisitor::VisitNot`. + +## CHECK-START: long SmaliTests2.$noinline$NotNot1(long) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> LongConstant -1 +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: long SmaliTests2.$noinline$NotNot1(long) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: Return [<>] + +## CHECK-START: long SmaliTests2.$noinline$NotNot1(long) instruction_simplifier (after) +## CHECK-NOT: Xor + +# Original java source: +# +# return ~~arg; +.method public static $noinline$NotNot1(J)J + .registers 6 + .param p0, "arg" # J + + .prologue + const-wide/16 v2, -0x1 + + .line 1001 + xor-long v0, p0, v2 + + xor-long/2addr v0, v2 + + return-wide v0 +.end method + +## CHECK-START: int SmaliTests2.$noinline$NotNot2(int) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant -1 +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: <> Add [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int SmaliTests2.$noinline$NotNot2(int) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> Not [<>] +## CHECK-DAG: <> Add [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int SmaliTests2.$noinline$NotNot2(int) instruction_simplifier (after) +## CHECK: Not +## CHECK-NOT: Not + +## CHECK-START: int SmaliTests2.$noinline$NotNot2(int) instruction_simplifier (after) +## CHECK-NOT: Xor + +# Original java source: +# +# int temp = ~arg; +# return temp + ~temp; +# +.method public static $noinline$NotNot2(I)I + .registers 3 + .param p0, "arg" # I + + .prologue + .line 1026 + xor-int/lit8 v0, p0, -0x1 + + .line 1027 + .local v0, "temp":I + xor-int/lit8 v1, v0, -0x1 + + add-int/2addr v1, v0 + + return v1 +.end method + +# Original java source: +# +# return !arg; +# +.method public static NegateValue(Z)Z + .registers 2 + .param p0, "arg" # Z + + .prologue + .line 1216 + if-nez p0, :cond_4 + + const/4 v0, 0x1 + + :goto_3 + return v0 + + :cond_4 + const/4 v0, 0x0 + + goto :goto_3 +.end method + +# Test simplification of double Boolean negation. Note that sometimes +# both negations can be removed but we only expect the simplifier to +# remove the second. + +## CHECK-START: boolean SmaliTests2.$noinline$NotNotBool(boolean) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> InvokeStaticOrDirect method_name:SmaliTests2.NegateValue +## CHECK-DAG: <> NotEqual [<>,<>] +## CHECK-DAG: If [<>] + +## CHECK-START: boolean SmaliTests2.$noinline$NotNotBool(boolean) instruction_simplifier (after) +## CHECK-NOT: NotEqual + +## CHECK-START: boolean SmaliTests2.$noinline$NotNotBool(boolean) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> InvokeStaticOrDirect method_name:SmaliTests2.NegateValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> Phi [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: boolean SmaliTests2.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: If [<>] +## CHECK-DAG: <> Phi [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: boolean SmaliTests2.$noinline$NotNotBool(boolean) instruction_simplifier$after_gvn (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: Return [<>] + +# Original java source: +# +# return !(NegateValue(arg)); +# +.method public static $noinline$NotNotBool(Z)Z + .registers 2 + .param p0, "arg" # Z + + .prologue + .line 1220 + invoke-static {p0}, LSmaliTests2;->NegateValue(Z)Z + + move-result v0 + + if-nez v0, :cond_8 + + const/4 v0, 0x1 + + :goto_7 + return v0 + + :cond_8 + const/4 v0, 0x0 + + goto :goto_7 +.end method + +## CHECK-START: int SmaliTests2.$noinline$bug68142795Short(short) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 65535 +## CHECK-DAG: <> And [<>,<>] +## CHECK-DAG: <> And [<>,<>] +## CHECK-DAG: <> TypeConversion [<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int SmaliTests2.$noinline$bug68142795Short(short) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: Return [<>] + +# Original java source +# +# return (short)(0xffff & (s & 0xffff)); +# +.method public static $noinline$bug68142795Short(S)I + .registers 3 + .param p0, "s" # S + + .prologue + const v1, 0xffff + + .line 2562 + and-int v0, p0, v1 + + and-int/2addr v0, v1 + + int-to-short v0, v0 + + return v0 +.end method + +# Original java source +# +# return 255; +# +.method private static $inline$get255()I + .registers 1 + + .prologue + .line 2849 + const/16 v0, 0xff + + return v0 +.end method + +## CHECK-START: int SmaliTests2.$noinline$bug68142795Boolean(boolean) instruction_simplifier$after_inlining (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> IntConstant 255 +## CHECK-DAG: If [<>] +## CHECK-DAG: <> Phi [<>,<>] +## CHECK-DAG: <> And [<>,<>] +## CHECK-DAG: <> TypeConversion [<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int SmaliTests2.$noinline$bug68142795Boolean(boolean) instruction_simplifier$after_gvn (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: Return [<>] + +# Original java source +# +# int v = b ? 1 : 0; // Should be simplified to "b" after inlining. +# return (byte)($inline$get255() & v); +# +.method public static $noinline$bug68142795Boolean(Z)I + .registers 3 + .param p0, "b" # Z + + .prologue + .line 2580 + if-eqz p0, :cond_a + + const/4 v0, 0x1 + + .line 2581 + .local v0, "v":I + :goto_3 + invoke-static {}, LSmaliTests2;->$inline$get255()I + + move-result v1 + + and-int/2addr v1, v0 + + int-to-byte v1, v1 + + return v1 + + .line 2580 + .end local v0 # "v":I + :cond_a + const/4 v0, 0x0 + + goto :goto_3 +.end method diff --git a/test/458-checker-instruct-simplification/src/Main.java b/test/458-checker-instruct-simplification/src/Main.java index 7797f31867365d1bf15d40b2119a5ffb3687a371..9e714f5111e780c6ebd4ae6875c6e1d87fb1c3d0 100644 --- a/test/458-checker-instruct-simplification/src/Main.java +++ b/test/458-checker-instruct-simplification/src/Main.java @@ -640,24 +640,6 @@ public class Main { return arg ^ 0; } - /// CHECK-START: int Main.$noinline$XorAllOnes(int) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant -1 - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$noinline$XorAllOnes(int) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> Not [<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$noinline$XorAllOnes(int) instruction_simplifier (after) - /// CHECK-NOT: Xor - - public static int $noinline$XorAllOnes(int arg) { - return arg ^ -1; - } - /** * Test that addition or subtraction operation with both inputs negated are * optimized to use a single negation after the operation. @@ -882,7 +864,7 @@ public class Main { /// CHECK-NOT: Neg /// CHECK-NOT: Add - /// CHECK-START: int Main.$noinline$NegNeg2(int) constant_folding$after_inlining (after) + /// CHECK-START: int Main.$noinline$NegNeg2(int) constant_folding$after_gvn (after) /// CHECK: <> IntConstant 0 /// CHECK-NOT: Neg /// CHECK-NOT: Add @@ -978,56 +960,7 @@ public class Main { return -temp | -temp; } - /** - * Test simplification of the `~~var` pattern. - * The transformation tested is implemented in `InstructionSimplifierVisitor::VisitNot`. - */ - - /// CHECK-START: long Main.$noinline$NotNot1(long) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> LongConstant -1 - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: long Main.$noinline$NotNot1(long) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: Return [<>] - - /// CHECK-START: long Main.$noinline$NotNot1(long) instruction_simplifier (after) - /// CHECK-NOT: Xor - - public static long $noinline$NotNot1(long arg) { - return ~~arg; - } - - /// CHECK-START: int Main.$noinline$NotNot2(int) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant -1 - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> Add [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$noinline$NotNot2(int) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> Not [<>] - /// CHECK-DAG: <> Add [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$noinline$NotNot2(int) instruction_simplifier (after) - /// CHECK: Not - /// CHECK-NOT: Not - - /// CHECK-START: int Main.$noinline$NotNot2(int) instruction_simplifier (after) - /// CHECK-NOT: Xor - - public static int $noinline$NotNot2(int arg) { - int temp = ~arg; - return temp + ~temp; - } - - /** + /** * Test the simplification of a subtraction with a negated argument. * The transformation tested is implemented in `InstructionSimplifierVisitor::VisitSub`. */ @@ -1128,17 +1061,19 @@ public class Main { return res; } - /// CHECK-START: boolean Main.$noinline$EqualBoolVsIntConst(boolean) instruction_simplifier$after_inlining (before) + /// CHECK-START: boolean Main.$noinline$EqualBoolVsIntConst(boolean) dead_code_elimination$after_inlining (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 2 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> Equal [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: boolean Main.$noinline$EqualBoolVsIntConst(boolean) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: <> Equal [<>,<>] + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: Return [<>] + + /// CHECK-START: boolean Main.$noinline$EqualBoolVsIntConst(boolean) dead_code_elimination$after_inlining (after) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: Return [<>] @@ -1157,12 +1092,14 @@ public class Main { /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 2 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> NotEqual [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: boolean Main.$noinline$NotEqualBoolVsIntConst(boolean) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: <> NotEqual [<>,<>] + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: Return [<>] + + /// CHECK-START: boolean Main.$noinline$NotEqualBoolVsIntConst(boolean) dead_code_elimination$after_inlining (after) /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: Return [<>] @@ -1172,64 +1109,6 @@ public class Main { return (arg ? $inline$ReturnArg(0) : $inline$ReturnArg(1)) == 2; } - /* - * Test simplification of double Boolean negation. Note that sometimes - * both negations can be removed but we only expect the simplifier to - * remove the second. - */ - - /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> InvokeStaticOrDirect method_name:Main.NegateValue - /// CHECK-DAG: <> NotEqual [<>,<>] - /// CHECK-DAG: If [<>] - - /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier (after) - /// CHECK-NOT: NotEqual - - /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> InvokeStaticOrDirect method_name:Main.NegateValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> Phi [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-NOT: BooleanNot [<>] - /// CHECK-NOT: Phi - - /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK: BooleanNot [<>] - /// CHECK-NEXT: Goto - - /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (after) - /// CHECK-NOT: Select - - /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) dead_code_elimination$final (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-NOT: BooleanNot [<>] - /// CHECK-DAG: Return [<>] - - public static boolean NegateValue(boolean arg) { - return !arg; - } - - public static boolean $noinline$NotNotBool(boolean arg) { - return !(NegateValue(arg)); - } - /// CHECK-START: float Main.$noinline$Div2(float) instruction_simplifier (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> FloatConstant 2 @@ -1348,10 +1227,11 @@ public class Main { /// CHECK-DAG: <> IntConstant 54 /// CHECK-DAG: <> StaticFieldGet /// CHECK-DAG: <> NotEqual [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] + /// CHECK-DAG: <> Equal [<>,<>] + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.$noinline$booleanFieldEqualZero() instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.$noinline$booleanFieldEqualZero() select_generator (after) /// CHECK-DAG: <> StaticFieldGet /// CHECK-DAG: <> IntConstant 13 /// CHECK-DAG: <> IntConstant 54 @@ -1390,18 +1271,20 @@ public class Main { /// CHECK-DAG: <> IntConstant 42 /// CHECK-DAG: <> IntConstant 54 /// CHECK-DAG: <> LessThanOrEqual [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> NotEqual [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$noinline$intConditionNotEqualOne(int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: <> NotEqual [<>,<>] + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.$noinline$intConditionNotEqualOne(int) select_generator (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 13 /// CHECK-DAG: <> IntConstant 42 /// CHECK-DAG: <> IntConstant 54 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> LessThanOrEqual [<>,<>] + /// CHECK-DAG: <> LessThanOrEqual [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // Note that we match `LE` from Select because there are two identical // LessThanOrEqual instructions. @@ -1418,18 +1301,20 @@ public class Main { /// CHECK-DAG: <> IntConstant 42 /// CHECK-DAG: <> IntConstant 54 /// CHECK-DAG: <> LessThanOrEqual [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> Equal [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$noinline$intConditionEqualZero(int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: <> Equal [<>,<>] + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.$noinline$intConditionEqualZero(int) select_generator (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 13 /// CHECK-DAG: <> IntConstant 42 /// CHECK-DAG: <> IntConstant 54 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> LessThanOrEqual [<>,<>] + /// CHECK-DAG: <> LessThanOrEqual [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // Note that we match `LE` from Select because there are two identical // LessThanOrEqual instructions. @@ -1915,9 +1800,9 @@ public class Main { } } - public static boolean $noinline$runSmaliTestBoolean(String name, boolean input) { + public static boolean $noinline$runSmaliTest2Boolean(String name, boolean input) { try { - Class c = Class.forName("SmaliTests"); + Class c = Class.forName("SmaliTests2"); Method m = c.getMethod(name, boolean.class); return (Boolean) m.invoke(null, input); } catch (Exception ex) { @@ -1925,9 +1810,9 @@ public class Main { } } - public static int $noinline$runSmaliTestInt(String name, int arg) { + public static int $noinline$runSmaliTestInt(String postfix, String name, int arg) { try { - Class c = Class.forName("SmaliTests"); + Class c = Class.forName("SmaliTests" + postfix); Method m = c.getMethod(name, int.class); return (Integer) m.invoke(null, arg); } catch (Exception ex) { @@ -1935,9 +1820,13 @@ public class Main { } } - public static long $noinline$runSmaliTestLong(String name, long arg) { + public static int $noinline$runSmaliTestInt(String name, int arg) { + return $noinline$runSmaliTestInt("", name, arg); + } + + public static long $noinline$runSmaliTest2Long(String name, long arg) { try { - Class c = Class.forName("SmaliTests"); + Class c = Class.forName("SmaliTests2"); Method m = c.getMethod(name, long.class); return (Long) m.invoke(null, arg); } catch (Exception ex) { @@ -2551,39 +2440,6 @@ public class Main { return (byte)(0xff & (b & 0xff)); } - /// CHECK-START: int Main.$noinline$bug68142795Short(short) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 65535 - /// CHECK-DAG: <> And [<>,<>] - /// CHECK-DAG: <> And [<>,<>] - /// CHECK-DAG: <> TypeConversion [<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$noinline$bug68142795Short(short) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: Return [<>] - public static int $noinline$bug68142795Short(short s) { - return (short)(0xffff & (s & 0xffff)); - } - - /// CHECK-START: int Main.$noinline$bug68142795Boolean(boolean) instruction_simplifier$after_inlining (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> IntConstant 255 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> And [<>,<>] + public static int $noinline$singleCharStringIndexOf(int ch) { + return "x".indexOf(ch); + } + + /// CHECK-START: int Main.$noinline$singleCharStringIndexOfAfter(int, int) instruction_simplifier (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> LoadString + /// CHECK-DAG: <> InvokeVirtual [<>,<>,<>] intrinsic:StringIndexOfAfter + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.$noinline$singleCharStringIndexOfAfter(int, int) instruction_simplifier (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> LoadString + /// CHECK-DAG: <> InvokeVirtual [<>,<>,<>] intrinsic:StringIndexOfAfter + /// CHECK-DAG: Return [<>] + public static int $noinline$singleCharStringIndexOfAfter(int ch, int fromIndex) { + return "x".indexOf(ch, fromIndex); // Not simplified. + } + + public static void main(String[] args) throws Exception { + Class smaliTests2 = Class.forName("SmaliTests2"); + Method $noinline$XorAllOnes = smaliTests2.getMethod("$noinline$XorAllOnes", int.class); + Method $noinline$NotNot1 = smaliTests2.getMethod("$noinline$NotNot1", long.class); + Method $noinline$NotNot2 = smaliTests2.getMethod("$noinline$NotNot2", int.class); + Method $noinline$NotNotBool = smaliTests2.getMethod("$noinline$NotNotBool", boolean.class); + Method $noinline$bug68142795Short = smaliTests2.getMethod("$noinline$bug68142795Short", short.class); + Method $noinline$bug68142795Boolean = smaliTests2.getMethod("$noinline$bug68142795Boolean", boolean.class); + int arg = 123456; float floatArg = 123456.125f; @@ -2627,7 +2562,7 @@ public class Main { assertLongEquals(3, $noinline$SubSubConst(4)); assertLongEquals(arg, $noinline$UShr0(arg)); assertIntEquals(arg, $noinline$Xor0(arg)); - assertIntEquals(~arg, $noinline$XorAllOnes(arg)); + assertIntEquals(~arg, (int)$noinline$XorAllOnes.invoke(null, arg)); assertIntEquals(-(arg + arg + 1), $noinline$AddNegs1(arg, arg + 1)); assertIntEquals(-(arg + arg + 1), $noinline$AddNegs2(arg, arg + 1)); assertLongEquals(-(2 * arg + 1), $noinline$AddNegs3(arg, arg + 1)); @@ -2638,10 +2573,10 @@ public class Main { assertLongEquals(arg, $noinline$NegNeg3(arg)); assertIntEquals(1, $noinline$NegSub1(arg, arg + 1)); assertIntEquals(1, $noinline$NegSub2(arg, arg + 1)); - assertLongEquals(arg, $noinline$NotNot1(arg)); - assertLongEquals(arg, $noinline$runSmaliTestLong("$noinline$NotNot1", arg)); - assertIntEquals(-1, $noinline$NotNot2(arg)); - assertIntEquals(-1, $noinline$runSmaliTestInt("$noinline$NotNot2", arg)); + assertLongEquals(arg, (long)$noinline$NotNot1.invoke(null, arg)); + assertLongEquals(arg, $noinline$runSmaliTest2Long("$noinline$NotNot1", arg)); + assertIntEquals(-1, (int)$noinline$NotNot2.invoke(null, arg)); + assertIntEquals(-1, $noinline$runSmaliTestInt("2", "$noinline$NotNot2", arg)); assertIntEquals(-(arg + arg + 1), $noinline$SubNeg1(arg, arg + 1)); assertIntEquals(-(arg + arg + 1), $noinline$SubNeg2(arg, arg + 1)); assertLongEquals(-(2 * arg + 1), $noinline$SubNeg3(arg, arg + 1)); @@ -2649,10 +2584,10 @@ public class Main { assertBooleanEquals(true, $noinline$EqualBoolVsIntConst(true)); assertBooleanEquals(false, $noinline$NotEqualBoolVsIntConst(false)); assertBooleanEquals(false, $noinline$NotEqualBoolVsIntConst(false)); - assertBooleanEquals(true, $noinline$NotNotBool(true)); - assertBooleanEquals(true, $noinline$runSmaliTestBoolean("$noinline$NotNotBool", true)); - assertBooleanEquals(false, $noinline$NotNotBool(false)); - assertBooleanEquals(false, $noinline$runSmaliTestBoolean("$noinline$NotNotBool", false)); + assertBooleanEquals(true, (boolean)$noinline$NotNotBool.invoke(null, true)); + assertBooleanEquals(true, $noinline$runSmaliTest2Boolean("$noinline$NotNotBool", true)); + assertBooleanEquals(false, (boolean)$noinline$NotNotBool.invoke(null, false)); + assertBooleanEquals(false, $noinline$runSmaliTest2Boolean("$noinline$NotNotBool", false)); assertFloatEquals(50.0f, $noinline$Div2(100.0f)); assertDoubleEquals(75.0, $noinline$Div2(150.0)); assertFloatEquals(-400.0f, $noinline$DivMP25(100.0f)); @@ -2839,17 +2774,29 @@ public class Main { assertIntEquals(0x7f, $noinline$bug68142795Byte((byte) 0x7f)); assertIntEquals((byte) 0x80, $noinline$bug68142795Byte((byte) 0x80)); - assertIntEquals(0x7fff, $noinline$bug68142795Short((short) 0x7fff)); - assertIntEquals((short) 0x8000, $noinline$bug68142795Short((short) 0x8000)); - assertIntEquals(0, $noinline$bug68142795Boolean(false)); - assertIntEquals(1, $noinline$bug68142795Boolean(true)); + assertIntEquals(0x7fff, (int)$noinline$bug68142795Short.invoke(null, (short) 0x7fff)); + assertIntEquals((short) 0x8000, (int)$noinline$bug68142795Short.invoke(null, (short) 0x8000)); + assertIntEquals(0, (int)$noinline$bug68142795Boolean.invoke(null, false)); + assertIntEquals(1, (int)$noinline$bug68142795Boolean.invoke(null, true)); assertIntEquals(0x7f, $noinline$bug68142795Elaborate((byte) 0x7f)); assertIntEquals((byte) 0x80, $noinline$bug68142795Elaborate((byte) 0x80)); + + assertIntEquals(-1, $noinline$emptyStringIndexOf('a')); + assertIntEquals(-1, $noinline$emptyStringIndexOf('Z')); + assertIntEquals(-1, $noinline$emptyStringIndexOfAfter('a', 0)); + assertIntEquals(-1, $noinline$emptyStringIndexOfAfter('Z', -1)); + + assertIntEquals(-1, $noinline$singleCharStringIndexOf('a')); + assertIntEquals(0, $noinline$singleCharStringIndexOf('x')); + assertIntEquals(-1, $noinline$singleCharStringIndexOf('Z')); + assertIntEquals(-1, $noinline$singleCharStringIndexOfAfter('a', 0)); + assertIntEquals(0, $noinline$singleCharStringIndexOfAfter('x', -1)); + assertIntEquals(-1, $noinline$singleCharStringIndexOfAfter('x', 1)); + assertIntEquals(-1, $noinline$singleCharStringIndexOfAfter('Z', -1)); } private static boolean $inline$true() { return true; } private static boolean $inline$false() { return false; } - private static int $inline$get255() { return 255; } public static boolean booleanField; diff --git a/test/461-get-reference-vreg/get_reference_vreg_jni.cc b/test/461-get-reference-vreg/get_reference_vreg_jni.cc index b2cad67829936603dd33c2451d2e2e955c1dd4da..7eb3fe5ae6a80fcf06591666bc7feb906eeacc57 100644 --- a/test/461-get-reference-vreg/get_reference_vreg_jni.cc +++ b/test/461-get-reference-vreg/get_reference_vreg_jni.cc @@ -37,21 +37,21 @@ class TestVisitor : public StackVisitor { ArtMethod* m = GetMethod(); std::string m_name(m->GetName()); - if (m_name.compare("testThisWithInstanceCall") == 0) { + if (m_name.compare("$noinline$testThisWithInstanceCall") == 0) { found_method_index_ = 1; uint32_t value = 0; CHECK(GetVReg(m, 1, kReferenceVReg, &value)); CHECK_EQ(reinterpret_cast(value), this_value_); CHECK_EQ(GetThisObject(), this_value_); - } else if (m_name.compare("testThisWithStaticCall") == 0) { + } else if (m_name.compare("$noinline$testThisWithStaticCall") == 0) { found_method_index_ = 2; uint32_t value = 0; CHECK(GetVReg(m, 1, kReferenceVReg, &value)); - } else if (m_name.compare("testParameter") == 0) { + } else if (m_name.compare("$noinline$testParameter") == 0) { found_method_index_ = 3; uint32_t value = 0; CHECK(GetVReg(m, 1, kReferenceVReg, &value)); - } else if (m_name.compare("testObjectInScope") == 0) { + } else if (m_name.compare("$noinline$testObjectInScope") == 0) { found_method_index_ = 4; uint32_t value = 0; CHECK(GetVReg(m, 0, kReferenceVReg, &value)); diff --git a/test/461-get-reference-vreg/src/Main.java b/test/461-get-reference-vreg/src/Main.java index f7d43568d5c643e9d4f9eba116b746ad35c3a132..a14b0e96ede8e72a2d5ad2405fb9cc12a736020f 100644 --- a/test/461-get-reference-vreg/src/Main.java +++ b/test/461-get-reference-vreg/src/Main.java @@ -18,19 +18,19 @@ public class Main { public Main() { } - int testThisWithInstanceCall() { + int $noinline$testThisWithInstanceCall() { return doNativeCallRef(); } - int testThisWithStaticCall() { + int $noinline$testThisWithStaticCall() { return doStaticNativeCallRef(); } - static int testParameter(Object a) { + static int $noinline$testParameter(Object a) { return doStaticNativeCallRef(); } - static int testObjectInScope() { + static int $noinline$testObjectInScope() { Object a = array[0]; return doStaticNativeCallRef(); } @@ -41,19 +41,19 @@ public class Main { public static void main(String[] args) { System.loadLibrary(args[0]); Main rm = new Main(); - if (rm.testThisWithInstanceCall() != 1) { + if (rm.$noinline$testThisWithInstanceCall() != 1) { throw new Error("Expected 1"); } - if (rm.testThisWithStaticCall() != 2) { + if (rm.$noinline$testThisWithStaticCall() != 2) { throw new Error("Expected 2"); } - if (testParameter(new Object()) != 3) { + if ($noinline$testParameter(new Object()) != 3) { throw new Error("Expected 3"); } - if (testObjectInScope() != 4) { + if ($noinline$testObjectInScope() != 4) { throw new Error("Expected 4"); } } diff --git a/test/463-checker-boolean-simplifier/build b/test/463-checker-boolean-simplifier/build deleted file mode 100755 index 372195567057d9a4f8b4a5aec1198cd11e9c338e..0000000000000000000000000000000000000000 --- a/test/463-checker-boolean-simplifier/build +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -# -# Copyright 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This checker test is incompatible with jack bytecode output, -# so force it to use javac/dx. -export USE_JACK=false -# Also disable desugar because it is missing in jack platform builds. -export DESUGAR=false - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/463-checker-boolean-simplifier/smali/Main2.smali b/test/463-checker-boolean-simplifier/smali/Main2.smali new file mode 100644 index 0000000000000000000000000000000000000000..5fc553ea36d8412f9c8f7adb600ccc2216d7d9c9 --- /dev/null +++ b/test/463-checker-boolean-simplifier/smali/Main2.smali @@ -0,0 +1,308 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class public LMain2; +.super Ljava/lang/Object; +.source "Main2.java" + + +# direct methods +.method constructor ()V + .registers 1 + + .prologue + .line 17 + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method + +# Elementary test negating a boolean. Verifies that blocks are merged and +# empty branches removed. + +## CHECK-START: boolean Main2.BooleanNot(boolean) select_generator (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: If [<>] +## CHECK-DAG: <> Phi [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: boolean Main2.BooleanNot(boolean) select_generator (before) +## CHECK: Goto +## CHECK: Goto +## CHECK: Goto +## CHECK-NOT: Goto + +## CHECK-START: boolean Main2.BooleanNot(boolean) select_generator (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> Select [<>,<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: boolean Main2.BooleanNot(boolean) select_generator (after) +## CHECK-NOT: If +## CHECK-NOT: Phi + +## CHECK-START: boolean Main2.BooleanNot(boolean) select_generator (after) +## CHECK: Goto +## CHECK-NOT: Goto + +# The original java source of this method: +# +# return !x; +# +.method public static BooleanNot(Z)Z + .registers 2 + .param p0, "x" # Z + + .prologue + .line 70 + if-nez p0, :cond_4 + + const/4 v0, 0x1 + + :goto_3 + return v0 + + :cond_4 + const/4 v0, 0x0 + + goto :goto_3 +.end method + +# Program which further uses negated conditions. +# Note that Phis are discovered retrospectively. + +## CHECK-START: boolean Main2.ValuesOrdered(int, int, int) select_generator (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> GreaterThan [<>,<>] +## CHECK-DAG: If [<>] +## CHECK-DAG: <> GreaterThan [<>,<>] +## CHECK-DAG: If [<>] +## CHECK-DAG: <> NotEqual [<>,<>] +## CHECK-DAG: If [<>] +## CHECK-DAG: Return [<>] +## CHECK-DAG: <> Phi [<>,<>] +## CHECK-DAG: <> Phi [<>,<>] +## CHECK-DAG: <> Phi [<>,<>] + +## CHECK-START: boolean Main2.ValuesOrdered(int, int, int) select_generator (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> GreaterThan [<>,<>] +## CHECK-DAG: <> Select [<>,<>,<>] +## CHECK-DAG: <> GreaterThan [<>,<>] +## CHECK-DAG: <> Select [<>,<>,<>] +## CHECK-DAG: <> NotEqual [<>,<>] +## CHECK-DAG: <> Select [<>,<>,<>] +## CHECK-DAG: Return [<>] + +# The original java source of this method: +# +# return (x <= y) == (y <= z); +# +.method public static ValuesOrdered(III)Z + .registers 7 + .param p0, "x" # I + .param p1, "y" # I + .param p2, "z" # I + + .prologue + const/4 v0, 0x1 + + const/4 v1, 0x0 + + .line 166 + if-gt p0, p1, :cond_b + + move v3, v0 + + :goto_5 + if-gt p1, p2, :cond_d + + move v2, v0 + + :goto_8 + if-ne v3, v2, :cond_f + + :goto_a + return v0 + + :cond_b + move v3, v1 + + goto :goto_5 + + :cond_d + move v2, v1 + + goto :goto_8 + + :cond_f + move v0, v1 + + goto :goto_a +.end method + +## CHECK-START: int Main2.NegatedCondition(boolean) select_generator (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 42 +## CHECK-DAG: <> IntConstant 43 +## CHECK-DAG: If [<>] +## CHECK-DAG: <> Phi [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int Main2.NegatedCondition(boolean) select_generator (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 42 +## CHECK-DAG: <> IntConstant 43 +## CHECK-DAG: <> Select [<>,<>,<>] +## CHECK-DAG: Return [<> Select [<>,<>,<>] +## CHECK-DAG: Return [<>] + +# The original java source of this method: +# +# int x = 0; +# int y = 1; +# while (y++ < 10) { +# if (y > 1) { +# x = 13; +# } else { +# x = 42; +# } +# } +# return x; +# +.method public static MultiplePhis()I + .registers 4 + + .prologue + .line 290 + const/4 v0, 0x0 + + .line 291 + .local v0, "x":I + const/4 v1, 0x1 + + .local v1, "y":I + move v2, v1 + + .line 292 + .end local v1 # "y":I + .local v2, "y":I + :goto_3 + add-int/lit8 v1, v2, 0x1 + + .end local v2 # "y":I + .restart local v1 # "y":I + const/16 v3, 0xa + + if-ge v2, v3, :cond_14 + + .line 293 + const/4 v3, 0x1 + + if-le v1, v3, :cond_10 + + .line 294 + const/16 v0, 0xd + + move v2, v1 + + .end local v1 # "y":I + .restart local v2 # "y":I + goto :goto_3 + + .line 296 + .end local v2 # "y":I + .restart local v1 # "y":I + :cond_10 + const/16 v0, 0x2a + + move v2, v1 + + .end local v1 # "y":I + .restart local v2 # "y":I + goto :goto_3 + + .line 299 + .end local v2 # "y":I + .restart local v1 # "y":I + :cond_14 + return v0 +.end method diff --git a/test/463-checker-boolean-simplifier/src-art/Main.java b/test/463-checker-boolean-simplifier/src-art/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..2c759ed6f9ed560528f443760642167f387fcebb --- /dev/null +++ b/test/463-checker-boolean-simplifier/src-art/Main.java @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Method; + +public class Main { + + // Note #1: `javac` flips the conditions of If statements. + // Note #2: In the optimizing compiler, the first input of Phi is always + // the fall-through path, i.e. the false branch. + + public static void assertBoolEquals(boolean expected, boolean result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void assertIntEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + /* + * Program which only delegates the condition, i.e. returns 1 when True + * and 0 when False. + */ + + /// CHECK-START: boolean Main.GreaterThan(int, int) select_generator (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> GreaterThan [<>,<>] + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: Return [<>] + + /// CHECK-START: boolean Main.GreaterThan(int, int) select_generator (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> GreaterThan [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + + public static boolean LessThan(int x, int y) { + return (x < y) ? true : false; + } + + /// CHECK-START: int Main.SimpleTrueBlock(boolean, int) select_generator (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 42 + /// CHECK-DAG: <> IntConstant 43 + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.SimpleFalseBlock(boolean, int) select_generator (after) + /// CHECK-NOT: If + + public static int SimpleFalseBlock(boolean x, int y) { + return x ? 42 : y + 43; + } + + /// CHECK-START: int Main.SimpleBothBlocks(boolean, int, int) select_generator (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 42 + /// CHECK-DAG: <> IntConstant 43 + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] - - public static boolean GreaterThan(int x, int y) { - return (x <= y) ? false : true; - } - - /* - * Program which negates a condition, i.e. returns 0 when True - * and 1 when False. - */ - - /// CHECK-START: boolean Main.LessThan(int, int) select_generator (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] - /// CHECK-DAG: If [<>] - /// CHECK-DAG: <> Phi [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: boolean Main.LessThan(int, int) select_generator (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.NegatedCondition(boolean) select_generator (after) - /// CHECK-NOT: BooleanNot - - public static int NegatedCondition(boolean x) { - if (x != false) { - return 42; - } else { - return 43; + public static void main(String[] args) { + // This file is just for running on the RI as the test is ART specific. As successful + // execution for this test produces no output, there's nothing to do here. } - } - - /// CHECK-START: int Main.SimpleTrueBlock(boolean, int) select_generator (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 42 - /// CHECK-DAG: <> IntConstant 43 - /// CHECK-DAG: <> Add [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.SimpleFalseBlock(boolean, int) select_generator (after) - /// CHECK-NOT: If - - public static int SimpleFalseBlock(boolean x, int y) { - return x ? 42 : y + 43; - } - - /// CHECK-START: int Main.SimpleBothBlocks(boolean, int, int) select_generator (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 42 - /// CHECK-DAG: <> IntConstant 43 - /// CHECK-DAG: <> Add [<>,<>] - /// CHECK-DAG: <> Add [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - public static int MultiplePhis() { - int x = 0; - int y = 1; - while (y++ < 10) { - if (y > 1) { - x = 13; - } else { - x = 42; - } - } - return x; - } - - /// CHECK-START: int Main.TrueBlockWithTooManyInstructions(boolean) select_generator (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 2 - /// CHECK-DAG: <> IntConstant 43 - /// CHECK-DAG: If [<>] - /// CHECK-DAG: <> InstanceFieldGet [<>] - /// CHECK-DAG: <> Add [<>,<>] - /// CHECK-DAG: Phi [<>,<>] - - /// CHECK-START: int Main.TrueBlockWithTooManyInstructions(boolean) select_generator (after) - /// CHECK-NOT: Select - - public int TrueBlockWithTooManyInstructions(boolean x) { - return x ? (read_field + 2) : 43; - } - - /// CHECK-START: int Main.FalseBlockWithTooManyInstructions(boolean) select_generator (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 3 - /// CHECK-DAG: <> IntConstant 42 - /// CHECK-DAG: If [<>] - /// CHECK-DAG: <> InstanceFieldGet [<>] - /// CHECK-DAG: <> Add [<>,<>] - /// CHECK-DAG: Phi [<>,<>] - - /// CHECK-START: int Main.FalseBlockWithTooManyInstructions(boolean) select_generator (after) - /// CHECK-NOT: Select - - public int FalseBlockWithTooManyInstructions(boolean x) { - return x ? 42 : (read_field + 3); - } - - /// CHECK-START: int Main.TrueBlockWithSideEffects(boolean) select_generator (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 42 - /// CHECK-DAG: <> IntConstant 43 - /// CHECK-DAG: If [<>] - /// CHECK-DAG: InstanceFieldSet [<>,<>] - /// CHECK-DAG: Phi [<>,<>] - - /// CHECK-START: int Main.TrueBlockWithSideEffects(boolean) select_generator (after) - /// CHECK-NOT: Select - - public int TrueBlockWithSideEffects(boolean x) { - return x ? (write_field = 42) : 43; - } - - /// CHECK-START: int Main.FalseBlockWithSideEffects(boolean) select_generator (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 42 - /// CHECK-DAG: <> IntConstant 43 - /// CHECK-DAG: If [<>] - /// CHECK-DAG: InstanceFieldSet [<>,<>] - /// CHECK-DAG: Phi [<>,<>] - - /// CHECK-START: int Main.FalseBlockWithSideEffects(boolean) select_generator (after) - /// CHECK-NOT: Select - - public int FalseBlockWithSideEffects(boolean x) { - return x ? 42 : (write_field = 43); - } - - public static void main(String[] args) throws Exception { - assertBoolEquals(false, BooleanNot(true)); - assertBoolEquals(true, BooleanNot(false)); - assertBoolEquals(true, GreaterThan(10, 5)); - assertBoolEquals(false, GreaterThan(10, 10)); - assertBoolEquals(false, GreaterThan(5, 10)); - assertBoolEquals(true, LessThan(5, 10)); - assertBoolEquals(false, LessThan(10, 10)); - assertBoolEquals(false, LessThan(10, 5)); - assertBoolEquals(true, ValuesOrdered(1, 3, 5)); - assertBoolEquals(true, ValuesOrdered(5, 3, 1)); - assertBoolEquals(false, ValuesOrdered(1, 3, 2)); - assertBoolEquals(false, ValuesOrdered(2, 3, 1)); - assertBoolEquals(true, ValuesOrdered(3, 3, 3)); - assertBoolEquals(true, ValuesOrdered(3, 3, 5)); - assertBoolEquals(false, ValuesOrdered(5, 5, 3)); - assertIntEquals(42, NegatedCondition(true)); - assertIntEquals(43, NegatedCondition(false)); - assertIntEquals(46, SimpleTrueBlock(true, 4)); - assertIntEquals(43, SimpleTrueBlock(false, 4)); - assertIntEquals(42, SimpleFalseBlock(true, 7)); - assertIntEquals(50, SimpleFalseBlock(false, 7)); - assertIntEquals(48, SimpleBothBlocks(true, 6, 2)); - assertIntEquals(45, SimpleBothBlocks(false, 6, 2)); - assertIntEquals(1, ThreeBlocks(true, true)); - assertIntEquals(1, ThreeBlocks(true, false)); - assertIntEquals(2, ThreeBlocks(false, true)); - assertIntEquals(3, ThreeBlocks(false, false)); - assertIntEquals(13, MultiplePhis()); - - Main m = new Main(); - assertIntEquals(42, m.TrueBlockWithTooManyInstructions(true)); - assertIntEquals(43, m.TrueBlockWithTooManyInstructions(false)); - assertIntEquals(42, m.FalseBlockWithTooManyInstructions(true)); - assertIntEquals(43, m.FalseBlockWithTooManyInstructions(false)); - assertIntEquals(42, m.TrueBlockWithSideEffects(true)); - assertIntEquals(43, m.TrueBlockWithSideEffects(false)); - assertIntEquals(42, m.FalseBlockWithSideEffects(true)); - assertIntEquals(43, m.FalseBlockWithSideEffects(false)); - } - - // These need to be instance fields so as to not generate a LoadClass for iget/iput. - public int read_field = 40; - public int write_field = 42; } diff --git a/test/466-get-live-vreg/get_live_vreg_jni.cc b/test/466-get-live-vreg/get_live_vreg_jni.cc index 44ea0c987756aa8b70949d43146937d64b614479..58ffe04fee472b6f880b05ca610115ccd490d6a9 100644 --- a/test/466-get-live-vreg/get_live_vreg_jni.cc +++ b/test/466-get-live-vreg/get_live_vreg_jni.cc @@ -36,32 +36,46 @@ class TestVisitor : public StackVisitor { ArtMethod* m = GetMethod(); std::string m_name(m->GetName()); - if (m_name.compare("testLiveArgument") == 0) { + if (m_name.compare("$noinline$testLiveArgument") == 0) { + found_method_ = true; + CHECK_EQ(CodeItemDataAccessor(m->DexInstructionData()).RegistersSize(), 3u); + CheckOptimizedOutRegLiveness(m, 1, kIntVReg, true, 42); + + uint32_t value; + CHECK(GetVReg(m, 2, kReferenceVReg, &value)); + } else if (m_name.compare("$noinline$testIntervalHole") == 0) { found_method_ = true; - uint32_t value = 0; - CHECK(GetVReg(m, 0, kIntVReg, &value)); - CHECK_EQ(value, 42u); - } else if (m_name.compare("$opt$noinline$testIntervalHole") == 0) { uint32_t number_of_dex_registers = CodeItemDataAccessor(m->DexInstructionData()).RegistersSize(); uint32_t dex_register_of_first_parameter = number_of_dex_registers - 2; + CheckOptimizedOutRegLiveness(m, dex_register_of_first_parameter, kIntVReg, true, 1); + } else if (m_name.compare("$noinline$testCodeSinking") == 0) { found_method_ = true; - uint32_t value = 0; - if (GetCurrentQuickFrame() != nullptr && - GetCurrentOatQuickMethodHeader()->IsOptimized() && - !Runtime::Current()->IsJavaDebuggable()) { - CHECK_EQ(GetVReg(m, dex_register_of_first_parameter, kIntVReg, &value), false); - } else { - CHECK(GetVReg(m, dex_register_of_first_parameter, kIntVReg, &value)); - CHECK_EQ(value, 1u); - } + CheckOptimizedOutRegLiveness(m, 0, kReferenceVReg); } return true; } - // Value returned to Java to ensure the methods testSimpleVReg and testPairVReg - // have been found and tested. + void CheckOptimizedOutRegLiveness(ArtMethod* m, + uint32_t dex_reg, + VRegKind vreg_kind, + bool check_val = false, + uint32_t expected = 0) REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t value = 0; + if (GetCurrentQuickFrame() != nullptr && + GetCurrentOatQuickMethodHeader()->IsOptimized() && + !Runtime::Current()->IsJavaDebuggable()) { + CHECK_EQ(GetVReg(m, dex_reg, vreg_kind, &value), false); + } else { + CHECK(GetVReg(m, dex_reg, vreg_kind, &value)); + if (check_val) { + CHECK_EQ(value, expected); + } + } + } + + // Value returned to Java to ensure the required methods have been found and tested. bool found_method_ = false; }; diff --git a/test/466-get-live-vreg/src/Main.java b/test/466-get-live-vreg/src/Main.java index 19032601fa6e2db048dfcdf8108521098ec995d0..29a6901a701ceea4b50b04a337a2a90ecb5ff6f4 100644 --- a/test/466-get-live-vreg/src/Main.java +++ b/test/466-get-live-vreg/src/Main.java @@ -18,9 +18,9 @@ public class Main { public Main() { } - static int testLiveArgument(int arg) { + static int $noinline$testLiveArgument(int arg1, Integer arg2) { doStaticNativeCallLiveVreg(); - return arg; + return arg1 + arg2.intValue(); } static void moveArgToCalleeSave() { @@ -31,7 +31,7 @@ public class Main { } } - static void $opt$noinline$testIntervalHole(int arg, boolean test) { + static void $noinline$testIntervalHole(int arg, boolean test) { // Move the argument to callee save to ensure it is in // a readable register. moveArgToCalleeSave(); @@ -53,16 +53,18 @@ public class Main { public static void main(String[] args) { System.loadLibrary(args[0]); - if (testLiveArgument(staticField3) != staticField3) { - throw new Error("Expected " + staticField3); + if ($noinline$testLiveArgument(staticField3, Integer.valueOf(1)) != staticField3 + 1) { + throw new Error("Expected " + staticField3 + 1); } - if (testLiveArgument(staticField3) != staticField3) { - throw new Error("Expected " + staticField3); + if ($noinline$testLiveArgument(staticField3,Integer.valueOf(1)) != staticField3 + 1) { + throw new Error("Expected " + staticField3 + 1); } testWrapperIntervalHole(1, true); testWrapperIntervalHole(1, false); + + $noinline$testCodeSinking(1); } // Wrapper method to avoid inlining, which affects liveness @@ -70,12 +72,25 @@ public class Main { static void testWrapperIntervalHole(int arg, boolean test) { try { Thread.sleep(0); - $opt$noinline$testIntervalHole(arg, test); + $noinline$testIntervalHole(arg, test); } catch (Exception e) { throw new Error(e); } } + // The value of dex register which originally holded "Object[] o = new Object[1];" will not be + // live at the call to doStaticNativeCallLiveVreg after code sinking optimizizaion. + static void $noinline$testCodeSinking(int x) { + Object[] o = new Object[1]; + o[0] = o; + doStaticNativeCallLiveVreg(); + if (doThrow) { + throw new Error(o.toString()); + } + } + + static boolean doThrow; + static int staticField1; static int staticField2; static int staticField3 = 42; diff --git a/test/477-checker-bound-type/src/Main.java b/test/477-checker-bound-type/src/Main.java index 2504ab2839e8e27a6d738f0e448d1d0b22246b77..237e4dafb6bbce7f9a1784747f133c066a583dc8 100644 --- a/test/477-checker-bound-type/src/Main.java +++ b/test/477-checker-bound-type/src/Main.java @@ -57,5 +57,79 @@ public class Main { } } - public static void main(String[] args) { } + /// CHECK-START: void Main.boundTypeInLoop(int[]) licm (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: <> BoundType [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayLength [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + + /// CHECK-START: void Main.boundTypeInLoop(int[]) licm (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: <> BoundType [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayLength [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + // + /// CHECK-NOT: BoundType + + /// CHECK-START: void Main.boundTypeInLoop(int[]) loop_optimization (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> BoundType [<>] loop:none + /// CHECK-DAG: ArrayLength [<>] loop:none + /// CHECK-DAG: ArrayGet loop:none + /// CHECK-DAG: ArraySet loop:none + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: <> BoundType [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayLength [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + + /// CHECK-START: void Main.boundTypeInLoop(int[]) GVN$after_arch (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> BoundType [<>] loop:none + /// CHECK-DAG: ArrayLength [<>] loop:none + /// CHECK-DAG: ArrayGet loop:none + /// CHECK-DAG: ArraySet loop:none + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + // + /// CHECK-NOT: BoundType + /// CHECK-NOT: ArrayLength + private static void boundTypeInLoop(int[] a) { + for (int i = 0; a != null && i < a.length; i++) { + a[i] += 1; + } + } + + // BoundType must not be hoisted by LICM, in this example it leads to ArrayLength being + // hoisted as well which is invalid. + // + /// CHECK-START: void Main.BoundTypeNoLICM(java.lang.Object) licm (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: SuspendCheck loop:<> outer_loop:none + /// CHECK-DAG: <> BoundType [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundType [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayLength [<>] loop:<> outer_loop:none + // + /// CHECK-START: void Main.BoundTypeNoLICM(java.lang.Object) licm (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: SuspendCheck loop:<> outer_loop:none + /// CHECK-DAG: <> BoundType [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundType [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayLength [<>] loop:<> outer_loop:none + // + /// CHECK-NOT: BoundType loop:none + private static void BoundTypeNoLICM(Object obj) { + int i = 0; + while (obj instanceof int[]) { + int[] a = (int[])obj; + a[0] = 1; + } + } + + public static void main(String[] args) { } } diff --git a/test/478-checker-clinit-check-pruning/src/Main.java b/test/478-checker-clinit-check-pruning/src/Main.java index 752e761f1f7a617c96a933dc8ecac1b5e6ebe256..ca92e7a86a362e09bf4a9fe1ab39b3c60a0fe21f 100644 --- a/test/478-checker-clinit-check-pruning/src/Main.java +++ b/test/478-checker-clinit-check-pruning/src/Main.java @@ -185,21 +185,20 @@ public class Main { } /* - * Ensure an inlined call to a static method whose declaring class - * is a super class of the caller's class does not require an - * explicit clinit check. + * We used to remove clinit check for calls to static methods in a superclass. However, this + * is not a valid optimization when instances of erroneous classes can escape. b/62478025 */ /// CHECK-START: void Main$SubClassOfClassWithClinit5.invokeStaticInlined() builder (after) /// CHECK-DAG: InvokeStaticOrDirect /// CHECK-START: void Main$SubClassOfClassWithClinit5.invokeStaticInlined() builder (after) - /// CHECK-NOT: LoadClass - /// CHECK-NOT: ClinitCheck + /// CHECK: LoadClass + /// CHECK: ClinitCheck /// CHECK-START: void Main$SubClassOfClassWithClinit5.invokeStaticInlined() inliner (after) - /// CHECK-NOT: LoadClass - /// CHECK-NOT: ClinitCheck + /// CHECK: LoadClass + /// CHECK: ClinitCheck /// CHECK-NOT: InvokeStaticOrDirect static class ClassWithClinit5 { @@ -218,25 +217,22 @@ public class Main { } /* - * Ensure an non-inlined call to a static method whose declaring - * class is a super class of the caller's class does not require an - * explicit clinit check. + * We used to remove clinit check for calls to static methods in a superclass. However, this + * is not a valid optimization when instances of erroneous classes can escape. b/62478025 */ /// CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() builder (after) /// CHECK-DAG: InvokeStaticOrDirect /// CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() builder (after) - /// CHECK-NOT: LoadClass - /// CHECK-NOT: ClinitCheck + /// CHECK: LoadClass + /// CHECK: ClinitCheck /// CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() inliner (after) + /// CHECK-DAG: LoadClass + /// CHECK-DAG: ClinitCheck /// CHECK-DAG: InvokeStaticOrDirect - /// CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() inliner (after) - /// CHECK-NOT: LoadClass - /// CHECK-NOT: ClinitCheck - static class ClassWithClinit6 { static boolean doThrow = false; diff --git a/test/482-checker-loop-back-edge-use/src/Main.java b/test/482-checker-loop-back-edge-use/src/Main.java index 47823409a3e92b7994b4e923859a01170cf93605..8311d8cc4f35bf76d18040b35da22fec693bf124 100644 --- a/test/482-checker-loop-back-edge-use/src/Main.java +++ b/test/482-checker-loop-back-edge-use/src/Main.java @@ -18,26 +18,28 @@ public class Main { /// CHECK-START: void Main.loop1(boolean) liveness (after) - /// CHECK: <> ParameterValue liveness:<> ranges:{[<>,<>)} uses:[<>,<>] - /// CHECK: If [<>] liveness:<> - /// CHECK: Goto liveness:<> - /// CHECK: Exit + /// CHECK-DAG: <> ParameterValue liveness:<> ranges:{[<>,<>)} uses:[<>] + /// CHECK-DAG: If [<>] liveness:<> loop:none + /// CHECK-DAG: Goto loop:B{{\d+}} + /// CHECK-DAG: Exit /// CHECK-EVAL: <> + 1 == <> - /// CHECK-EVAL: <> + 2 == <> + // + // Loop invariant exit check is hoisted from the loop by peeling. public static void loop1(boolean incoming) { while (incoming) {} } /// CHECK-START: void Main.loop2(boolean) liveness (after) - /// CHECK: <> ParameterValue liveness:<> ranges:{[<>,<>)} uses:[<>,<>,<>] - /// CHECK: If [<>] liveness:<> - /// CHECK: Goto liveness:<> - /// CHECK: Goto liveness:<> + /// CHECK-DAG: <> ParameterValue liveness:<> ranges:{[<>,<>)} uses:[<>,<>] + /// CHECK-DAG: If [<>] liveness:<> loop:<> + /// CHECK-DAG: Goto liveness:<> loop:<> + /// CHECK-DAG: Goto liveness:<> loop:<> /// CHECK-EVAL: <> + 1 == <> /// CHECK-EVAL: <> < <> - /// CHECK-EVAL: <> + 2 == <> - /// CHECK-EVAL: <> + 2 == <> + /// CHECK-EVAL: <> + 2 == <> + // + // Loop invariant exit check is hoisted from the loop by peeling. public static void loop2(boolean incoming) { // Add some code at entry to avoid having the entry block be a pre header. @@ -122,17 +124,18 @@ public class Main { } /// CHECK-START: void Main.loop7(boolean) liveness (after) - /// CHECK: <> ParameterValue liveness:<> ranges:{[<>,<>)} uses:[<>,<>,<>,<>] - /// CHECK: InvokeVirtual [{{l\d+}},<>] method_name:java.io.PrintStream.println liveness:<> - /// CHECK: If [<>] liveness:<> - /// CHECK: Goto liveness:<> - /// CHECK: Goto liveness:<> - /// CHECK: Exit + /// CHECK-DAG: <> ParameterValue liveness:<> ranges:{[<>,<>)} uses:[<>,<>,<>] + /// CHECK-DAG: InvokeVirtual [{{l\d+}},<>] method_name:java.io.PrintStream.println liveness:<> + /// CHECK-DAG: If [<>] liveness:<> loop:<> + /// CHECK-DAG: Goto liveness:<> loop:<> + /// CHECK-DAG: Goto liveness:<> loop:<> + /// CHECK-DAG: Exit /// CHECK-EVAL: <> == <> /// CHECK-EVAL: <> + 1 == <> /// CHECK-EVAL: <> < <> - /// CHECK-EVAL: <> + 2 == <> - /// CHECK-EVAL: <> + 2 == <> + /// CHECK-EVAL: <> + 2 == <> + // + // Loop invariant exit check is hoisted from the loop by peeling. public static void loop7(boolean incoming) { // 'incoming' must have a use at both back edges. @@ -144,15 +147,16 @@ public class Main { } /// CHECK-START: void Main.loop8() liveness (after) - /// CHECK: <> StaticFieldGet liveness:<> ranges:{[<>,<>)} uses:[<>,<>,<>] - /// CHECK: If [<>] liveness:<> - /// CHECK: Goto liveness:<> - /// CHECK: Goto liveness:<> - /// CHECK: Exit + /// CHECK-DAG: <> StaticFieldGet liveness:<> ranges:{[<>,<>)} uses:[<>,<>] + /// CHECK-DAG: If [<>] liveness:<> loop:<> + /// CHECK-DAG: Goto liveness:<> loop:<> + /// CHECK-DAG: Goto liveness:<> loop:<> + /// CHECK-DAG: Exit /// CHECK-EVAL: <> + 1 == <> /// CHECK-EVAL: <> < <> - /// CHECK-EVAL: <> + 2 == <> - /// CHECK-EVAL: <> + 2 == <> + /// CHECK-EVAL: <> + 2 == <> + // + // Loop invariant exit check is hoisted from the loop by peeling. public static void loop8() { // 'incoming' must have a use at both back edges. @@ -171,14 +175,15 @@ public class Main { } /// CHECK-START: void Main.loop9() liveness (after) - /// CHECK: <> StaticFieldGet liveness:<> ranges:{[<>,<>)} uses:[<>,<>] - /// CHECK: If [<>] liveness:<> - /// CHECK: Goto liveness:<> - /// CHECK-DAG: Goto liveness:<> + /// CHECK-DAG: <> StaticFieldGet liveness:<> ranges:{[<>,<>)} uses:[<>] + /// CHECK-DAG: If [<>] liveness:<> loop:<> + /// CHECK-DAG: Goto liveness:<> loop:<> + /// CHECK-DAG: Goto liveness:<> loop:<> /// CHECK-DAG: Exit /// CHECK-EVAL: <> + 1 == <> /// CHECK-EVAL: <> < <> - /// CHECK-EVAL: <> + 2 == <> + // + // Loop invariant exit check is hoisted from the loop by peeling. public static void loop9() { // Add some code at entry to avoid having the entry block be a pre header. diff --git a/test/485-checker-dce-loop-update/smali/TestCase.smali b/test/485-checker-dce-loop-update/smali/TestCase.smali index cda6f7386199c662e0f2c370c773e4ebb3eacdeb..5290bad3ed2ef6f11787041abc21deeff810faa5 100644 --- a/test/485-checker-dce-loop-update/smali/TestCase.smali +++ b/test/485-checker-dce-loop-update/smali/TestCase.smali @@ -140,11 +140,11 @@ ## CHECK-DAG: <> Phi [<>,<>,<>] loop:<> ## CHECK-DAG: If [<>] loop:<> ## CHECK-DAG: <> Mul [<>,<>] loop:<> -## CHECK-DAG: <> Select [<>,<>,<>] loop:<> +## CHECK-DAG: <> Phi [<>,<>] loop:<> ## CHECK-DAG: If [<>] loop:<> -## CHECK-DAG: <> Add [<>,<>] loop:<> +## CHECK-DAG: <> Add [<>,<>] loop:<> ## CHECK-DAG: <> Add [<>,<>] loop:<> -## CHECK-DAG: Return [<>] loop:none +## CHECK-DAG: Return [<>] loop:none ## CHECK-START: int TestCase.testExitPredecessors(int, boolean, boolean) dead_code_elimination$after_inlining (after) ## CHECK-DAG: <> ParameterValue @@ -156,6 +156,22 @@ ## CHECK-DAG: If [<>] loop:<> ## CHECK-DAG: <> Add [<>,<>] loop:<> ## CHECK-DAG: <> Mul [<>,<>] loop:none +## CHECK-DAG: <> Phi [<>,<>] loop:none +## CHECK-DAG: Return [<>] loop:none + +## CHECK-START: int TestCase.testExitPredecessors(int, boolean, boolean) dead_code_elimination$after_inlining (after) +## CHECK-NOT: IntConstant 5 + +## CHECK-START: int TestCase.testExitPredecessors(int, boolean, boolean) select_generator (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 7 +## CHECK-DAG: <> IntConstant 11 +## CHECK-DAG: <> Phi [<>,<>] loop:<> +## CHECK-DAG: If [<>] loop:<> +## CHECK-DAG: <> Add [<>,<>] loop:<> +## CHECK-DAG: <> Mul [<>,<>] loop:none ## CHECK-DAG: <> Select [<>,<>,<>] loop:none ## CHECK-DAG: Return [<>] loop:none diff --git a/test/497-inlining-and-class-loader/clear_dex_cache.cc b/test/497-inlining-and-class-loader/clear_dex_cache.cc index c113042c9cbeff7aeccf3cc4ce9759afe90330f8..c6fd56f20d086a1cc129e47af876596bd703ad37 100644 --- a/test/497-inlining-and-class-loader/clear_dex_cache.cc +++ b/test/497-inlining-and-class-loader/clear_dex_cache.cc @@ -52,11 +52,11 @@ extern "C" JNIEXPORT jobject JNICALL Java_Main_cloneResolvedMethods(JNIEnv* env, uint32_t index = pair.index; ArtMethod* method = pair.object; if (sizeof(void*) == 4) { - ObjPtr int_array = down_cast(decoded_array.Ptr()); + ObjPtr int_array = ObjPtr::DownCast(decoded_array); int_array->Set(2u * i, index); int_array->Set(2u * i + 1u, static_cast(reinterpret_cast(method))); } else { - ObjPtr long_array = down_cast(decoded_array.Ptr()); + ObjPtr long_array = ObjPtr::DownCast(decoded_array); long_array->Set(2u * i, index); long_array->Set(2u * i + 1u, reinterpret_cast64(method)); } diff --git a/test/527-checker-array-access-split/src/Main.java b/test/527-checker-array-access-split/src/Main.java index a5caa7bce0ede996d5856c323ce262780e2bd4e6..935b37858d3af74c221fd818411424e270d3bb9d 100644 --- a/test/527-checker-array-access-split/src/Main.java +++ b/test/527-checker-array-access-split/src/Main.java @@ -400,7 +400,7 @@ public class Main { /// CHECK: ArraySet [<
>,<>,<
>] public static int canMergeAfterBCE1() { - int[] array = {0, 7, 14, 21}; + int[] array = {0, 7, 14, 21, 28, 35, 42}; for (int i = 0; i < array.length; i++) { array[i] = array[i] / 7; } @@ -513,7 +513,7 @@ public class Main { /// CHECK-NOT: IntermediateAddress public static int canMergeAfterBCE2() { - int[] array = {64, 8, 4, 2 }; + int[] array = {128, 64, 32, 8, 4, 2 }; for (int i = 0; i < array.length - 1; i++) { array[i + 1] = array[i] << array[i + 1]; } @@ -571,8 +571,8 @@ public class Main { accrossGC(array, 0); assertIntEquals(125, array[0]); - assertIntEquals(3, canMergeAfterBCE1()); - assertIntEquals(1048576, canMergeAfterBCE2()); + assertIntEquals(6, canMergeAfterBCE1()); + assertIntEquals(2097152, canMergeAfterBCE2()); assertIntEquals(18, checkLongFloatDouble()); } diff --git a/test/530-checker-lse/smali/Main.smali b/test/530-checker-lse/smali/Main.smali new file mode 100644 index 0000000000000000000000000000000000000000..4c18266c0105a2c9eb84a2f42477296efc18e490 --- /dev/null +++ b/test/530-checker-lse/smali/Main.smali @@ -0,0 +1,292 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class public LMain2; +.super Ljava/lang/Object; +.source "Main.java" + +# direct methods + +## CHECK-START: int Main2.test4(TestClass, boolean) load_store_elimination (before) +## CHECK: InstanceFieldSet +## CHECK: InstanceFieldGet +## CHECK: Return +## CHECK: InstanceFieldSet + +## CHECK-START: int Main2.test4(TestClass, boolean) load_store_elimination (after) +## CHECK: InstanceFieldSet +## CHECK-NOT: NullCheck +## CHECK-NOT: InstanceFieldGet +## CHECK: Return +## CHECK: InstanceFieldSet + +# Set and merge the same value in two branches. + +# Original java source: +# +# static int test4(TestClass obj, boolean b) { +# if (b) { +# obj.i = 1; +# } else { +# obj.i = 1; +# } +# return obj.i; +# } + +.method public static test4(LTestClass;Z)I + .registers 3 + .param p0, "obj" # LTestClass; + .param p1, "b" # Z + + .prologue + const/4 v0, 0x1 + + .line 185 + if-eqz p1, :cond_8 + + .line 186 + iput v0, p0, LTestClass;->i:I + + .line 190 + :goto_5 + iget v0, p0, LTestClass;->i:I + + return v0 + + .line 188 + :cond_8 + iput v0, p0, LTestClass;->i:I + + goto :goto_5 +.end method + +## CHECK-START: int Main2.test5(TestClass, boolean) load_store_elimination (before) +## CHECK: InstanceFieldSet +## CHECK: InstanceFieldGet +## CHECK: Return +## CHECK: InstanceFieldSet + +## CHECK-START: int Main2.test5(TestClass, boolean) load_store_elimination (after) +## CHECK: InstanceFieldSet +## CHECK: InstanceFieldGet +## CHECK: Return +## CHECK: InstanceFieldSet + +# Set and merge different values in two branches. +# Original java source: +# +# static int test5(TestClass obj, boolean b) { +# if (b) { +# obj.i = 1; +# } else { +# obj.i = 2; +# } +# return obj.i; +# } + +.method public static test5(LTestClass;Z)I + .registers 3 + .param p0, "obj" # LTestClass; + .param p1, "b" # Z + + .prologue + .line 207 + if-eqz p1, :cond_8 + + .line 208 + const/4 v0, 0x1 + + iput v0, p0, LTestClass;->i:I + + .line 212 + :goto_5 + iget v0, p0, LTestClass;->i:I + + return v0 + + .line 210 + :cond_8 + const/4 v0, 0x2 + + iput v0, p0, LTestClass;->i:I + + goto :goto_5 +.end method + +## CHECK-START: int Main2.test10(TestClass) load_store_elimination (before) +## CHECK: StaticFieldGet +## CHECK: InstanceFieldGet +## CHECK: StaticFieldSet +## CHECK: InstanceFieldGet + +## CHECK-START: int Main2.test10(TestClass) load_store_elimination (after) +## CHECK: StaticFieldGet +## CHECK: InstanceFieldGet +## CHECK: StaticFieldSet +## CHECK-NOT: NullCheck +## CHECK-NOT: InstanceFieldGet + +# Original java source: +# +# // Static fields shouldn't alias with instance fields. +# static int test10(TestClass obj) { +# TestClass.si += obj.i; +# return obj.i; +# } + +.method public static test10(LTestClass;)I + .registers 3 + .param p0, "obj" # LTestClass; + sget v0, LTestClass;->si:I + iget v1, p0, LTestClass;->i:I + add-int/2addr v0, v1 + sput v0, LTestClass;->si:I + iget p0, p0, LTestClass;->i:I + return p0 +.end method + +## CHECK-START: int Main2.test23(boolean) load_store_elimination (before) +## CHECK: NewInstance +## CHECK: InstanceFieldSet +## CHECK: InstanceFieldGet +## CHECK: InstanceFieldSet +## CHECK: InstanceFieldGet +## CHECK: Return +## CHECK: InstanceFieldGet +## CHECK: InstanceFieldSet + +## CHECK-START: int Main2.test23(boolean) load_store_elimination (after) +## CHECK: NewInstance +## CHECK-NOT: InstanceFieldSet +## CHECK-NOT: InstanceFieldGet +## CHECK: InstanceFieldSet +## CHECK: InstanceFieldGet +## CHECK: Return +## CHECK-NOT: InstanceFieldGet +## CHECK: InstanceFieldSet + +# Test store elimination on merging. + +# Original java source: +# +# static int test23(boolean b) { +# TestClass obj = new TestClass(); +# obj.i = 3; // This store can be eliminated since the value flows into each branch. +# if (b) { +# obj.i += 1; // This store cannot be eliminated due to the merge later. +# } else { +# obj.i += 2; // This store cannot be eliminated due to the merge later. +# } +# return obj.i; +# } + +.method public static test23(Z)I + .registers 3 + .param p0, "b" # Z + + .prologue + .line 582 + new-instance v0, LTestClass; + + invoke-direct {v0}, LTestClass;->()V + + .line 583 + .local v0, "obj":LTestClass; + const/4 v1, 0x3 + + iput v1, v0, LTestClass;->i:I + + .line 584 + if-eqz p0, :cond_13 + + .line 585 + iget v1, v0, LTestClass;->i:I + + add-int/lit8 v1, v1, 0x1 + + iput v1, v0, LTestClass;->i:I + + .line 589 + :goto_10 + iget v1, v0, LTestClass;->i:I + + return v1 + + .line 587 + :cond_13 + iget v1, v0, LTestClass;->i:I + + add-int/lit8 v1, v1, 0x2 + + iput v1, v0, LTestClass;->i:I + + goto :goto_10 +.end method + +## CHECK-START: float Main2.test24() load_store_elimination (before) +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> FloatConstant 8 +## CHECK-DAG: <> FloatConstant 42 +## CHECK-DAG: <> NewInstance +## CHECK-DAG: InstanceFieldSet [<>,<>] +## CHECK-DAG: InstanceFieldSet [<>,<>] +## CHECK-DAG: <> InstanceFieldGet [<>] +## CHECK-DAG: <> InstanceFieldGet [<>] +## CHECK-DAG: <> Select [<>,<>,<>] +## CHECK-DAG: Return [<>] + +# Original java source: +# +# static float test24() { +# float a = 42.0f; +# TestClass3 obj = new TestClass3(); +# if (obj.test1) { +# a = obj.floatField; +# } +# return a; +# } + +.method public static test24()F + .registers 3 + + .prologue + .line 612 + const/high16 v0, 0x42280000 # 42.0f + + .line 613 + .local v0, "a":F + new-instance v1, LTestClass3; + + invoke-direct {v1}, LTestClass3;->()V + + .line 614 + .local v1, "obj":LTestClass3; + iget-boolean v2, v1, LTestClass3;->test1:Z + + if-eqz v2, :cond_d + + .line 615 + iget v0, v1, LTestClass3;->floatField:F + + .line 617 + :cond_d + return v0 +.end method diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java index ebde3bf84573cc02d470f68395922f796d0d71f1..22bff0aaf5cfc9b0e638ca45fb26fa19d78668f6 100644 --- a/test/530-checker-lse/src/Main.java +++ b/test/530-checker-lse/src/Main.java @@ -14,6 +14,8 @@ * limitations under the License. */ +import java.lang.reflect.Method; + class Circle { Circle(double radius) { this.radius = radius; @@ -167,51 +169,6 @@ public class Main { return obj.i + obj1.j + obj2.i + obj2.j; } - /// CHECK-START: int Main.test4(TestClass, boolean) load_store_elimination (before) - /// CHECK: InstanceFieldSet - /// CHECK: InstanceFieldGet - /// CHECK: Return - /// CHECK: InstanceFieldSet - - /// CHECK-START: int Main.test4(TestClass, boolean) load_store_elimination (after) - /// CHECK: InstanceFieldSet - /// CHECK-NOT: NullCheck - /// CHECK-NOT: InstanceFieldGet - /// CHECK: Return - /// CHECK: InstanceFieldSet - - // Set and merge the same value in two branches. - static int test4(TestClass obj, boolean b) { - if (b) { - obj.i = 1; - } else { - obj.i = 1; - } - return obj.i; - } - - /// CHECK-START: int Main.test5(TestClass, boolean) load_store_elimination (before) - /// CHECK: InstanceFieldSet - /// CHECK: InstanceFieldGet - /// CHECK: Return - /// CHECK: InstanceFieldSet - - /// CHECK-START: int Main.test5(TestClass, boolean) load_store_elimination (after) - /// CHECK: InstanceFieldSet - /// CHECK: InstanceFieldGet - /// CHECK: Return - /// CHECK: InstanceFieldSet - - // Set and merge different values in two branches. - static int test5(TestClass obj, boolean b) { - if (b) { - obj.i = 1; - } else { - obj.i = 2; - } - return obj.i; - } - /// CHECK-START: int Main.test6(TestClass, TestClass, boolean) load_store_elimination (before) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet @@ -294,25 +251,6 @@ public class Main { return obj2.i; } - /// CHECK-START: int Main.test10(TestClass) load_store_elimination (before) - /// CHECK: StaticFieldGet - /// CHECK: InstanceFieldGet - /// CHECK: StaticFieldSet - /// CHECK: InstanceFieldGet - - /// CHECK-START: int Main.test10(TestClass) load_store_elimination (after) - /// CHECK: StaticFieldGet - /// CHECK: InstanceFieldGet - /// CHECK: StaticFieldSet - /// CHECK-NOT: NullCheck - /// CHECK-NOT: InstanceFieldGet - - // Static fields shouldn't alias with instance fields. - static int test10(TestClass obj) { - TestClass.si += obj.i; - return obj.i; - } - /// CHECK-START: int Main.test11(TestClass) load_store_elimination (before) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet @@ -557,66 +495,6 @@ public class Main { return sum; } - /// CHECK-START: int Main.test23(boolean) load_store_elimination (before) - /// CHECK: NewInstance - /// CHECK: InstanceFieldSet - /// CHECK: InstanceFieldGet - /// CHECK: InstanceFieldSet - /// CHECK: InstanceFieldGet - /// CHECK: Return - /// CHECK: InstanceFieldGet - /// CHECK: InstanceFieldSet - - /// CHECK-START: int Main.test23(boolean) load_store_elimination (after) - /// CHECK: NewInstance - /// CHECK-NOT: InstanceFieldSet - /// CHECK-NOT: InstanceFieldGet - /// CHECK: InstanceFieldSet - /// CHECK: InstanceFieldGet - /// CHECK: Return - /// CHECK-NOT: InstanceFieldGet - /// CHECK: InstanceFieldSet - - // Test store elimination on merging. - static int test23(boolean b) { - TestClass obj = new TestClass(); - obj.i = 3; // This store can be eliminated since the value flows into each branch. - if (b) { - obj.i += 1; // This store cannot be eliminated due to the merge later. - } else { - obj.i += 2; // This store cannot be eliminated due to the merge later. - } - return obj.i; - } - - /// CHECK-START: float Main.test24() load_store_elimination (before) - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> FloatConstant 8 - /// CHECK-DAG: <> FloatConstant 42 - /// CHECK-DAG: <> NewInstance - /// CHECK-DAG: InstanceFieldSet [<>,<>] - /// CHECK-DAG: InstanceFieldSet [<>,<>] - /// CHECK-DAG: <> InstanceFieldGet [<>] - /// CHECK-DAG: <> InstanceFieldGet [<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - static float test24() { - float a = 42.0f; - TestClass3 obj = new TestClass3(); - if (obj.test1) { - a = obj.floatField; - } - return a; - } - /// CHECK-START: void Main.testFinalizable() load_store_elimination (before) /// CHECK: NewInstance /// CHECK: InstanceFieldSet @@ -1137,6 +1015,126 @@ public class Main { static Object[] sArray; + /// CHECK-START: int Main.testLocalArrayMerge1(boolean) load_store_elimination (before) + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> NewArray + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: <> ArrayGet [<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.testLocalArrayMerge1(boolean) load_store_elimination (after) + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.testLocalArrayMerge1(boolean) load_store_elimination (after) + /// CHECK-NOT: NewArray + /// CHECK-NOT: ArraySet + /// CHECK-NOT: ArrayGet + private static int testLocalArrayMerge1(boolean x) { + // The explicit store can be removed right away + // since it is equivalent to the default. + int[] a = { 0 }; + // The diamond pattern stores/load can be replaced + // by the direct value. + if (x) { + a[0] = 1; + } else { + a[0] = 1; + } + return a[0]; + } + + /// CHECK-START: int Main.testLocalArrayMerge2(boolean) load_store_elimination (before) + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> IntConstant 2 + /// CHECK-DAG: <> IntConstant 3 + /// CHECK-DAG: <> NewArray + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: <> ArrayGet [<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.testLocalArrayMerge2(boolean) load_store_elimination (after) + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> NewArray + /// CHECK-DAG: <> ArrayGet [<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.testLocalArrayMerge2(boolean) load_store_elimination (after) + /// CHECK-DAG: ArraySet + /// CHECK-DAG: ArraySet + /// CHECK-NOT: ArraySet + private static int testLocalArrayMerge2(boolean x) { + // The explicit store can be removed eventually even + // though it is not equivalent to the default. + int[] a = { 1 }; + // The diamond pattern stores/load remain. + if (x) { + a[0] = 2; + } else { + a[0] = 3; + } + return a[0]; + } + + /// CHECK-START: int Main.testLocalArrayMerge3(boolean) load_store_elimination (after) + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> IntConstant 2 + /// CHECK-DAG: <> NewArray + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: <> ArrayGet [<>,<>] + /// CHECK-DAG: Return [<>] + private static int testLocalArrayMerge3(boolean x) { + // All stores/load remain. + int[] a = { 1 }; + if (x) { + a[0] = 2; + } + return a[0]; + } + + /// CHECK-START: int Main.testLocalArrayMerge4(boolean) load_store_elimination (before) + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> NewArray + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: <> ArrayGet [<>,<>] + /// CHECK-DAG: <> ArrayGet [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.testLocalArrayMerge4(boolean) load_store_elimination (after) + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> TypeConversion [<>] + /// CHECK-DAG: <> TypeConversion [<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.testLocalArrayMerge4(boolean) load_store_elimination (after) + /// CHECK-NOT: NewArray + /// CHECK-NOT: ArraySet + /// CHECK-NOT: ArrayGet + private static int testLocalArrayMerge4(boolean x) { + byte[] a = { 0 }; + if (x) { + a[0] = 1; + } else { + a[0] = 1; + } + // Differently typed (signed vs unsigned), + // but same reference. + return a[0] + (a[0] & 0xff); + } + static void assertIntEquals(int result, int expected) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); @@ -1155,7 +1153,15 @@ public class Main { } } - public static void main(String[] args) { + public static void main(String[] args) throws Exception { + + Class main2 = Class.forName("Main2"); + Method test4 = main2.getMethod("test4", TestClass.class, boolean.class); + Method test5 = main2.getMethod("test5", TestClass.class, boolean.class); + Method test10 = main2.getMethod("test10", TestClass.class); + Method test23 = main2.getMethod("test23", boolean.class); + Method test24 = main2.getMethod("test24"); + assertDoubleEquals(Math.PI * Math.PI * Math.PI, calcCircleArea(Math.PI)); assertIntEquals(test1(new TestClass(), new TestClass()), 3); assertIntEquals(test2(new TestClass()), 1); @@ -1163,10 +1169,10 @@ public class Main { TestClass obj2 = new TestClass(); obj1.next = obj2; assertIntEquals(test3(obj1), 10); - assertIntEquals(test4(new TestClass(), true), 1); - assertIntEquals(test4(new TestClass(), false), 1); - assertIntEquals(test5(new TestClass(), true), 1); - assertIntEquals(test5(new TestClass(), false), 2); + assertIntEquals((int)test4.invoke(null, new TestClass(), true), 1); + assertIntEquals((int)test4.invoke(null, new TestClass(), false), 1); + assertIntEquals((int)test5.invoke(null, new TestClass(), true), 1); + assertIntEquals((int)test5.invoke(null, new TestClass(), false), 2); assertIntEquals(test6(new TestClass(), new TestClass(), true), 4); assertIntEquals(test6(new TestClass(), new TestClass(), false), 2); assertIntEquals(test7(new TestClass()), 1); @@ -1175,7 +1181,7 @@ public class Main { obj2 = new TestClass(); obj1.next = obj2; assertIntEquals(test9(new TestClass()), 1); - assertIntEquals(test10(new TestClass(3, 4)), 3); + assertIntEquals((int)test10.invoke(null, new TestClass(3, 4)), 3); assertIntEquals(TestClass.si, 3); assertIntEquals(test11(new TestClass()), 10); assertIntEquals(test12(new TestClass(), new TestClass()), 10); @@ -1192,9 +1198,9 @@ public class Main { assertFloatEquals(test20().i, 0); test21(new TestClass()); assertIntEquals(test22(), 13); - assertIntEquals(test23(true), 4); - assertIntEquals(test23(false), 5); - assertFloatEquals(test24(), 8.0f); + assertIntEquals((int)test23.invoke(null, true), 4); + assertIntEquals((int)test23.invoke(null, false), 5); + assertFloatEquals((float)test24.invoke(null), 8.0f); testFinalizableByForcingGc(); assertIntEquals($noinline$testHSelect(true), 0xdead); int[] array = {2, 5, 9, -1, -3, 10, 8, 4}; @@ -1271,6 +1277,15 @@ public class Main { assertIntEquals(testclass2.i, 55); assertIntEquals(testStoreStoreWithDeoptimize(new int[4]), 4); + + assertIntEquals(testLocalArrayMerge1(true), 1); + assertIntEquals(testLocalArrayMerge1(false), 1); + assertIntEquals(testLocalArrayMerge2(true), 2); + assertIntEquals(testLocalArrayMerge2(false), 3); + assertIntEquals(testLocalArrayMerge3(true), 2); + assertIntEquals(testLocalArrayMerge3(false), 1); + assertIntEquals(testLocalArrayMerge4(true), 2); + assertIntEquals(testLocalArrayMerge4(false), 2); } static boolean sFlag; diff --git a/test/651-checker-double-simd-minmax/expected.txt b/test/530-checker-peel-unroll/expected.txt similarity index 100% rename from test/651-checker-double-simd-minmax/expected.txt rename to test/530-checker-peel-unroll/expected.txt diff --git a/test/530-checker-peel-unroll/info.txt b/test/530-checker-peel-unroll/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..0e10b364427df9d29f9e407bd838484922a52214 --- /dev/null +++ b/test/530-checker-peel-unroll/info.txt @@ -0,0 +1 @@ +Test on loop optimizations, peeling and unrolling. diff --git a/test/530-checker-peel-unroll/src/Main.java b/test/530-checker-peel-unroll/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..4d814407a3e8783d38ea833128d0b51313d21d2c --- /dev/null +++ b/test/530-checker-peel-unroll/src/Main.java @@ -0,0 +1,1199 @@ +/* + * 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. + */ + +// +// Test loop optimizations, in particular scalar loop peeling and unrolling. +public class Main { + + static final int LENGTH = 4 * 1024; + int[] a = new int[LENGTH]; + int[] b = new int[LENGTH]; + + private static final int LENGTH_A = LENGTH; + private static final int LENGTH_B = 16; + private static final int RESULT_POS = 4; + + double[][] mA; + double[][] mB; + double[][] mC; + + public Main() { + mA = new double[LENGTH_A][]; + mB = new double[LENGTH_B][]; + mC = new double[LENGTH_B][]; + for (int i = 0; i < LENGTH_A; i++) { + mA[i] = new double[LENGTH_B]; + } + + for (int i = 0; i < LENGTH_B; i++) { + mB[i] = new double[LENGTH_A]; + mC[i] = new double[LENGTH_B]; + } + } + + private static final void initMatrix(double[][] m) { + for (int i = 0; i < m.length; i++) { + for (int j = 0; j < m[i].length; j++) { + m[i][j] = (double) (i * LENGTH / (j + 1)); + } + } + } + + private static final void initIntArray(int[] a) { + for (int i = 0; i < a.length; i++) { + a[i] = i % 4; + } + } + + private static final void initDoubleArray(double[] a) { + for (int i = 0; i < a.length; i++) { + a[i] = (double)(i % 4); + } + } + + /// CHECK-START: void Main.unrollingLoadStoreElimination(int[]) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<> outer_loop:none + /// CHECK-NOT: ArraySet loop:<> outer_loop:none + + /// CHECK-START: void Main.unrollingLoadStoreElimination(int[]) loop_optimization (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<> outer_loop:none + /// CHECK-NOT: ArraySet loop:<> outer_loop:none + private static final void unrollingLoadStoreElimination(int[] a) { + for (int i = 0; i < LENGTH - 2; i++) { + a[i] += a[i + 1]; + } + } + + /// CHECK-START: void Main.unrollingWhile(int[]) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> IntConstant 128 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Rem [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NotEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Phi [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<> outer_loop:none + /// CHECK-NOT: ArraySet loop:<> outer_loop:none + + /// CHECK-START: void Main.unrollingWhile(int[]) loop_optimization (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> IntConstant 128 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Rem [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NotEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Rem [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NotEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: Phi [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<> outer_loop:none + /// CHECK-NOT: ArraySet loop:<> outer_loop:none + private static final void unrollingWhile(int[] a) { + int i = 0; + int s = 128; + while (i++ < LENGTH - 2) { + if (i % 2 == 0) { + a[i] = s++; + } + } + } + + // Simple check that loop unrolling has happened. + // + /// CHECK-START: void Main.unrollingSwitch(int[]) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4096 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<> outer_loop:none + /// CHECK-NOT: ArraySet loop:<> outer_loop:none + + /// CHECK-START: void Main.unrollingSwitch(int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4096 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<> outer_loop:none + /// CHECK-NOT: ArraySet loop:<> outer_loop:none + private static final void unrollingSwitch(int[] a) { + for (int i = 0; i < LENGTH; i++) { + switch (i % 3) { + case 2: + a[i]++; + break; + default: + break; + } + } + } + + // Simple check that loop unrolling has happened. + // + /// CHECK-START: void Main.unrollingSwapElements(int[]) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<> outer_loop:none + /// CHECK-NOT: ArraySet loop:<> outer_loop:none + + /// CHECK-START: void Main.unrollingSwapElements(int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<> outer_loop:none + /// CHECK-NOT: ArraySet loop:<> outer_loop:none + private static final void unrollingSwapElements(int[] array) { + for (int i = 0; i < LENGTH - 2; i++) { + if (array[i] > array[i + 1]) { + int temp = array[i + 1]; + array[i + 1] = array[i]; + array[i] = temp; + } + } + } + + // Simple check that loop unrolling has happened. + // + /// CHECK-START: void Main.unrollingRInnerproduct(double[][], double[][], double[][], int, int) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 16 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<> outer_loop:none + /// CHECK-NOT: ArraySet loop:<> outer_loop:none + + /// CHECK-START: void Main.unrollingRInnerproduct(double[][], double[][], double[][], int, int) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 16 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<> outer_loop:none + /// CHECK-NOT: ArraySet loop:<> outer_loop:none + private static final void unrollingRInnerproduct(double[][] result, + double[][] a, + double[][] b, + int row, + int column) { + // computes the inner product of A[row,*] and B[*,column] + int i; + result[row][column] = 0.0f; + for (i = 0; i < LENGTH_B; i++) { + result[row][column] = result[row][column] + a[row][i] * b[i][column]; + } + } + + // nested loop + // [[[]]] + + /// CHECK-START: void Main.unrollingInTheNest(int[], int[], int) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 128 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:<> + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + /// CHECK-NOT: If + + /// CHECK-START: void Main.unrollingInTheNest(int[], int[], int) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 128 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + // + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + /// CHECK-NOT: If + private static final void unrollingInTheNest(int[] a, int[] b, int x) { + for (int k = 0; k < 16; k++) { + for (int j = 0; j < 16; j++) { + for (int i = 0; i < 128; i++) { + b[x]++; + a[i] = a[i] + 1; + } + } + } + } + + // nested loop: + // [ + // if [] else [] + // ] + + /// CHECK-START: void Main.unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 128 loop:none + /// CHECK-DAG: <> IntConstant 100 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + // + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + // + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + /// CHECK-NOT: If + + /// CHECK-START: void Main.unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 128 loop:none + /// CHECK-DAG: <> IntConstant 100 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + // + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:<> + // + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:<> + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + /// CHECK-NOT: If + private static final void unrollingTwoLoopsInTheNest(int[] a, int[] b, int x) { + for (int k = 0; k < 128; k++) { + if (x > 100) { + for (int j = 0; j < 128; j++) { + a[x]++; + } + } else { + for (int i = 0; i < 128; i++) { + b[x]++; + } + } + } + } + + /// CHECK-START: int Main.unrollingSimpleLiveOuts(int[]) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Mul [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: <> Add [<>,<>] loop:none + /// CHECK-DAG: <> DivZeroCheck [<>] env:[[<>,<>,<>,<>,_,<>]] loop:none + /// CHECK-DAG: <> Div [<>,<>] loop:none + /// CHECK-DAG: Return [<
>] loop:none + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + + /// CHECK-START: int Main.unrollingSimpleLiveOuts(int[]) loop_optimization (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Mul [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Mul [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: <> Phi [<>,<>] loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:none + /// CHECK-DAG: <> DivZeroCheck [<>] env:[[<>,<>,<>,<>,_,<>]] loop:none + /// CHECK-DAG: <> Div [<>,<>] loop:none + /// CHECK-DAG: Return [<
>] loop:none + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + private static final int unrollingSimpleLiveOuts(int[] a) { + int s = 1; + int t = 2; + for (int i = 0; i < LENGTH - 2; i++) { + int temp = a[i + 1]; + s += temp; + t *= temp; + a[i] += s; + } + + return 1 / (s + t); + } + + /// CHECK-START: int Main.unrollingWhileLiveOuts(int[]) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> IntConstant 128 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Rem [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NotEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Phi [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + + /// CHECK-START: int Main.unrollingWhileLiveOuts(int[]) loop_optimization (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> IntConstant 128 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Rem [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NotEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Rem [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NotEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: Phi [<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: <> Phi [<>,<>] loop:none + /// CHECK-DAG: Return [<>] loop:none + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + private static final int unrollingWhileLiveOuts(int[] a) { + int i = 0; + int s = 128; + while (i++ < LENGTH - 2) { + if (i % 2 == 0) { + a[i] = s++; + } + } + return s; + } + + /// CHECK-START: int Main.unrollingLiveOutsNested(int[]) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + // + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + // + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Mul [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:<> + // + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + + /// CHECK-START: int Main.unrollingLiveOutsNested(int[]) loop_optimization (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + // + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + // + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Mul [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:<> + // + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Mul [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:<> + // + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: <> Add [<>,<>] loop:none + /// CHECK-DAG: Return [<>] loop:none + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + private static final int unrollingLiveOutsNested(int[] a) { + int s = 1; + int t = 2; + for (int j = 0; j < 16; j++) { + for (int i = 0; i < LENGTH - 2; i++) { + int temp = a[i + 1]; + s += temp; + t *= temp; + a[i] += s; + } + } + return s + t; + } + + /// CHECK-START: void Main.unrollingInstanceOf(int[], java.lang.Object[]) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: InstanceOf loop:<> outer_loop:none + // + /// CHECK-NOT: InstanceOf + + /// CHECK-START: void Main.unrollingInstanceOf(int[], java.lang.Object[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: InstanceOf loop:<> outer_loop:none + /// CHECK-DAG: InstanceOf loop:<> outer_loop:none + // + /// CHECK-NOT: InstanceOf + public void unrollingInstanceOf(int[] a, Object[] obj_array) { + for (int i = 0; i < LENGTH_B; i++) { + if (obj_array[i] instanceof Integer) { + a[i] += 1; + } + } + } + + /// CHECK-START: void Main.unrollingDivZeroCheck(int[], int) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: DivZeroCheck loop:<> outer_loop:none + // + /// CHECK-NOT: DivZeroCheck + + /// CHECK-START: void Main.unrollingDivZeroCheck(int[], int) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: DivZeroCheck loop:<> outer_loop:none + /// CHECK-DAG: DivZeroCheck loop:<> outer_loop:none + // + /// CHECK-NOT: DivZeroCheck + public void unrollingDivZeroCheck(int[] a, int r) { + for (int i = 0; i < LENGTH_B; i++) { + a[i] += a[i] / r; + } + } + + /// CHECK-START: void Main.unrollingTypeConversion(int[], double[]) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: TypeConversion loop:<> outer_loop:none + // + /// CHECK-NOT: TypeConversion + + /// CHECK-START: void Main.unrollingTypeConversion(int[], double[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: TypeConversion loop:<> outer_loop:none + /// CHECK-DAG: TypeConversion loop:<> outer_loop:none + // + /// CHECK-NOT: TypeConversion + public void unrollingTypeConversion(int[] a, double[] b) { + for (int i = 0; i < LENGTH_B; i++) { + a[i] = (int) b[i]; + } + } + + interface Itf { + } + + class SubMain extends Main implements Itf { + } + + /// CHECK-START: void Main.unrollingCheckCast(int[], java.lang.Object) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: CheckCast loop:<> outer_loop:none + // + /// CHECK-NOT: CheckCast + + /// CHECK-START: void Main.unrollingCheckCast(int[], java.lang.Object) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: CheckCast loop:<> outer_loop:none + /// CHECK-DAG: CheckCast loop:<> outer_loop:none + // + /// CHECK-NOT: CheckCast + public void unrollingCheckCast(int[] a, Object o) { + for (int i = 0; i < LENGTH_B; i++) { + if (((SubMain)o) == o) { + a[i] = i; + } + } + } + + /// CHECK-START: void Main.noUnrollingOddTripCount(int[]) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4095 loop:none + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: Phi + /// CHECK-NOT: If + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.noUnrollingOddTripCount(int[]) loop_optimization (after) + /// CHECK-DAG: Phi loop:<> outer_loop:none + /// CHECK-DAG: If loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + // + /// CHECK-NOT: Phi + /// CHECK-NOT: If + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + private static final void noUnrollingOddTripCount(int[] a) { + for (int i = 0; i < LENGTH - 1; i++) { + a[i] += a[i + 1]; + } + } + + /// CHECK-START: void Main.noUnrollingNotKnownTripCount(int[], int) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: Phi + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.noUnrollingNotKnownTripCount(int[], int) loop_optimization (after) + /// CHECK-DAG: Phi loop:<> outer_loop:none + /// CHECK-DAG: If loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + // + /// CHECK-NOT: Phi + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + private static final void noUnrollingNotKnownTripCount(int[] a, int n) { + for (int i = 0; i < n; i++) { + a[i] += a[i + 1]; + } + } + + /// CHECK-START: void Main.peelingSimple(int[], boolean) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4096 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.peelingSimple(int[], boolean) loop_optimization (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4096 loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:none + /// CHECK-DAG: If [<>] loop:none + /// CHECK-DAG: If [<>] loop:none + /// CHECK-DAG: ArrayGet loop:none + /// CHECK-DAG: ArraySet loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$final (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4096 loop:none + /// CHECK-DAG: If [<>] loop:none + /// CHECK-DAG: ArrayGet loop:none + /// CHECK-DAG: ArraySet loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: GreaterThanOrEqual + /// CHECK-NOT: If + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + private static final void peelingSimple(int[] a, boolean f) { + for (int i = 0; i < LENGTH; i++) { + if (f) { + break; + } + a[i] += 1; + } + } + + // Often used idiom that, when not hoisted, prevents BCE and vectorization. + // + /// CHECK-START: void Main.peelingAddInts(int[]) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> NullConstant loop:none + /// CHECK-DAG: <> Equal [<>,<>] loop:none + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + + /// CHECK-START: void Main.peelingAddInts(int[]) dead_code_elimination$final (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> NullConstant loop:none + /// CHECK-DAG: <> Equal [<>,<>] loop:none + /// CHECK-DAG: If [<>] loop:none + /// CHECK-DAG: ArraySet loop:none + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + // + /// CHECK-NOT: If [<>] loop:<> outer_loop:none + private static final void peelingAddInts(int[] a) { + for (int i = 0; a != null && i < a.length; i++) { + a[i] += 1; + } + } + + /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4096 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) dead_code_elimination$final (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4096 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + private static final void peelingBreakFromNest(int[] a, boolean f) { + outer: + for (int i = 1; i < 32; i++) { + for (int j = 0; j < LENGTH; j++) { + if (f) { + break outer; + } + a[j] += 1; + } + } + } + + /// CHECK-START: int Main.peelingHoistOneControl(int) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> NotEqual [<>,<>] loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: If + + /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$final (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> NotEqual [<>,<>] loop:none + /// CHECK-DAG: If [<>] loop:none + /// CHECK-DAG: SuspendCheck loop:<> outer_loop:none + /// CHECK-DAG: Goto loop:<> outer_loop:none + // + // Check that the loop has no instruction except SuspendCheck and Goto (indefinite loop). + /// CHECK-NOT: loop:<> outer_loop:none + /// CHECK-NOT: If + /// CHECK-NOT: Phi + /// CHECK-NOT: Add + private static final int peelingHoistOneControl(int x) { + int i = 0; + while (true) { + if (x == 0) + return 1; + i++; + } + } + + /// CHECK-START: int Main.peelingHoistOneControl(int, int) loop_optimization (before) + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: If loop:<> outer_loop:none + /// CHECK-DAG: If loop:<> outer_loop:none + // + /// CHECK-START: int Main.peelingHoistOneControl(int, int) dead_code_elimination$final (after) + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: If loop:<> outer_loop:none + /// CHECK-NOT: If loop:<> outer_loop:none + private static final int peelingHoistOneControl(int x, int y) { + while (true) { + if (x == 0) + return 1; + if (y == 0) // no longer invariant + return 2; + y--; + } + } + + /// CHECK-START: int Main.peelingHoistTwoControl(int, int, int) loop_optimization (before) + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: If loop:<> outer_loop:none + /// CHECK-DAG: If loop:<> outer_loop:none + /// CHECK-DAG: If loop:<> outer_loop:none + // + /// CHECK-START: int Main.peelingHoistTwoControl(int, int, int) dead_code_elimination$final (after) + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: If loop:<> outer_loop:none + /// CHECK-NOT: If loop:<> outer_loop:none + private static final int peelingHoistTwoControl(int x, int y, int z) { + while (true) { + if (x == 0) + return 1; + if (y == 0) + return 2; + if (z == 0) // no longer invariant + return 3; + z--; + } + } + + /// CHECK-START: void Main.unrollingFull(int[]) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.unrollingFull(int[]) loop_optimization (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + // Two peeled iterations + /// CHECK-DAG: ArrayGet loop:none + /// CHECK-DAG: ArrayGet loop:none + /// CHECK-DAG: ArraySet loop:none + /// CHECK-DAG: ArrayGet loop:none + /// CHECK-DAG: ArrayGet loop:none + /// CHECK-DAG: ArraySet loop:none + // Loop + /// CHECK-DAG: <> Phi [{{i\d+}},{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + private static final void unrollingFull(int[] a) { + for (int i = 0; i < 2; i++) { + a[i] += a[i + 1]; + } + } + + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public void verifyUnrolling() { + initIntArray(a); + initIntArray(b); + + initMatrix(mA); + initMatrix(mB); + initMatrix(mC); + + int expected = 174291515; + int found = 0; + + double[] doubleArray = new double[LENGTH_B]; + initDoubleArray(doubleArray); + + unrollingInstanceOf(a, new Integer[LENGTH_B]); + unrollingDivZeroCheck(a, 15); + unrollingTypeConversion(a, doubleArray); + unrollingCheckCast(a, new SubMain()); + + unrollingWhile(a); + unrollingLoadStoreElimination(a); + unrollingSwitch(a); + unrollingSwapElements(a); + unrollingRInnerproduct(mC, mA, mB, RESULT_POS, RESULT_POS); + unrollingInTheNest(a, b, RESULT_POS); + unrollingTwoLoopsInTheNest(a, b, RESULT_POS); + + noUnrollingOddTripCount(b); + noUnrollingNotKnownTripCount(b, 128); + + for (int i = 0; i < LENGTH; i++) { + found += a[i]; + found += b[i]; + } + found += (int)mC[RESULT_POS][RESULT_POS]; + + expectEquals(expected, found); + } + + public void verifyPeeling() { + expectEquals(1, peelingHoistOneControl(0)); // anything else loops + expectEquals(1, peelingHoistOneControl(0, 0)); + expectEquals(1, peelingHoistOneControl(0, 1)); + expectEquals(2, peelingHoistOneControl(1, 0)); + expectEquals(2, peelingHoistOneControl(1, 1)); + expectEquals(1, peelingHoistTwoControl(0, 0, 0)); + expectEquals(1, peelingHoistTwoControl(0, 0, 1)); + expectEquals(1, peelingHoistTwoControl(0, 1, 0)); + expectEquals(1, peelingHoistTwoControl(0, 1, 1)); + expectEquals(2, peelingHoistTwoControl(1, 0, 0)); + expectEquals(2, peelingHoistTwoControl(1, 0, 1)); + expectEquals(3, peelingHoistTwoControl(1, 1, 0)); + expectEquals(3, peelingHoistTwoControl(1, 1, 1)); + + initIntArray(a); + peelingSimple(a, false); + peelingSimple(a, true); + peelingAddInts(a); + peelingAddInts(null); // okay + peelingBreakFromNest(a, false); + peelingBreakFromNest(a, true); + + unrollingSimpleLiveOuts(a); + unrollingWhileLiveOuts(a); + unrollingLiveOutsNested(a); + + int expected = 51565978; + int found = 0; + for (int i = 0; i < a.length; i++) { + found += a[i]; + } + + expectEquals(expected, found); + } + + public static void main(String[] args) { + Main obj = new Main(); + + obj.verifyUnrolling(); + obj.verifyPeeling(); + + System.out.println("passed"); + } +} diff --git a/test/536-checker-intrinsic-optimization/build b/test/536-checker-intrinsic-optimization/build deleted file mode 100755 index 49292c9ac1d27679e32ff18a77487f6742de7a31..0000000000000000000000000000000000000000 --- a/test/536-checker-intrinsic-optimization/build +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -# -# Copyright 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This checker test is incompatible with jack bytecode output, -# so force it to use javac/dx. -export USE_JACK=false -# Also disable desugar because it is missing in jack platform builds. -export DESUGAR=false - -./default-build "$@" diff --git a/test/537-checker-inline-and-unverified/build b/test/537-checker-inline-and-unverified/build deleted file mode 100755 index 49292c9ac1d27679e32ff18a77487f6742de7a31..0000000000000000000000000000000000000000 --- a/test/537-checker-inline-and-unverified/build +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -# -# Copyright 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This checker test is incompatible with jack bytecode output, -# so force it to use javac/dx. -export USE_JACK=false -# Also disable desugar because it is missing in jack platform builds. -export DESUGAR=false - -./default-build "$@" diff --git a/test/543-checker-dce-trycatch/smali/TestCase.smali b/test/543-checker-dce-trycatch/smali/TestCase.smali index f50e01e51b0730de4e03b4ce005753e0c0571790..7ad9ba8e647a534cff19cc754015abe435d47b85 100644 --- a/test/543-checker-dce-trycatch/smali/TestCase.smali +++ b/test/543-checker-dce-trycatch/smali/TestCase.smali @@ -215,10 +215,10 @@ ## CHECK-DAG: <> IntConstant 16 ## CHECK-DAG: <> IntConstant 17 ## CHECK-DAG: <> Add [<>,<>] -## CHECK-DAG: <> Select [<>,<>,{{z\d+}}] +## CHECK-DAG: <> Phi [<>,<>] reg:3 is_catch_phi:false ## CHECK-DAG: Phi [<>,<>,<>] reg:1 is_catch_phi:true ## CHECK-DAG: Phi [<>,<>,<>] reg:2 is_catch_phi:true -## CHECK-DAG: Phi [<>] + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: Return [<>] - /// CHECK-START: boolean Main.classEquality1() instruction_simplifier$after_inlining (after) + /// CHECK-START: boolean Main.classEquality1() dead_code_elimination$after_inlining (after) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: Return [<>] public static boolean classEquality1() { @@ -46,11 +49,14 @@ public class Main { } /// CHECK-START: boolean Main.classEquality2() instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> {{Equal|NotEqual}} - /// CHECK-DAG: <> Select [{{i\d+}},{{i\d+}},<>] - /// CHECK-DAG: Return [<>] + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: Return [<>] - /// CHECK-START: boolean Main.classEquality3() instruction_simplifier$after_inlining (after) + /// CHECK-START: boolean Main.classEquality3() dead_code_elimination$after_inlining (after) /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: Return [<>] public static boolean classEquality3() { @@ -71,11 +80,14 @@ public class Main { } /// CHECK-START: boolean Main.classEquality4() instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> {{Equal|NotEqual}} - /// CHECK-DAG: <> Select [{{i\d+}},{{i\d+}},<>] - /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.sad1(byte, byte) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsInt + /// CHECK-START: int Main.sad1(byte, byte) instruction_simplifier$after_gvn (after) + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad1(byte x, byte y) { return x >= y ? x - y : y - x; } - /// CHECK-START: int Main.sad2(byte, byte) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.sad2(byte, byte) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.sad3(byte, byte) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsInt + /// CHECK-START: int Main.sad3(byte, byte) instruction_simplifier$after_gvn (after) + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad3(byte x, byte y) { int diff = x - y; return diff >= 0 ? diff : -diff; } - /// CHECK-START: int Main.sad3Alt(byte, byte) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.sad3Alt(byte, byte) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.sadL1(byte, byte) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsLong + /// CHECK-START: long Main.sadL1(byte, byte) instruction_simplifier$after_gvn (after) + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL1(byte x, byte y) { long xl = x; @@ -80,12 +80,12 @@ public class Main { return xl >= yl ? xl - yl : yl - xl; } - /// CHECK-START: long Main.sadL2(byte, byte) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.sadL2(byte, byte) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.sadL3(byte, byte) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsLong + /// CHECK-START: long Main.sadL3(byte, byte) instruction_simplifier$after_gvn (after) + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL3(byte x, byte y) { long diff = x - y; return diff >= 0L ? diff : -diff; } - /// CHECK-START: long Main.sadL3Alt(byte, byte) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.sadL3Alt(byte, byte) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.sad1(char, char) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsInt + /// CHECK-START: int Main.sad1(char, char) instruction_simplifier$after_gvn (after) + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad1(char x, char y) { return x >= y ? x - y : y - x; } - /// CHECK-START: int Main.sad2(char, char) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.sad2(char, char) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.sad3(char, char) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsInt + /// CHECK-START: int Main.sad3(char, char) instruction_simplifier$after_gvn (after) + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad3(char x, char y) { int diff = x - y; return diff >= 0 ? diff : -diff; } - /// CHECK-START: int Main.sad3Alt(char, char) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.sad3Alt(char, char) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.sadL1(char, char) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsLong + /// CHECK-START: long Main.sadL1(char, char) instruction_simplifier$after_gvn (after) + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL1(char x, char y) { long xl = x; @@ -80,12 +80,12 @@ public class Main { return xl >= yl ? xl - yl : yl - xl; } - /// CHECK-START: long Main.sadL2(char, char) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.sadL2(char, char) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.sadL3(char, char) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsLong + /// CHECK-START: long Main.sadL3(char, char) instruction_simplifier$after_gvn (after) + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL3(char x, char y) { long diff = x - y; return diff >= 0L ? diff : -diff; } - /// CHECK-START: long Main.sadL3Alt(char, char) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.sadL3Alt(char, char) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.sad1(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.sad1(int, int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.sad2(int, int) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsInt + /// CHECK-START: int Main.sad2(int, int) instruction_simplifier$after_gvn (after) + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad2(int x, int y) { int diff = x - y; @@ -48,36 +48,36 @@ public class Main { return diff; } - /// CHECK-START: int Main.sad3(int, int) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.sad3(int, int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.sad3Alt(int, int) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsInt + /// CHECK-START: int Main.sad3Alt(int, int) instruction_simplifier$after_gvn (after) + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad3Alt(int x, int y) { int diff = x - y; return 0 <= diff ? diff : -diff; } - /// CHECK-START: long Main.sadL1(int, int) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.sadL1(int, int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.sadL2(int, int) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsLong + /// CHECK-START: long Main.sadL2(int, int) instruction_simplifier$after_gvn (after) + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL2(int x, int y) { long diff = x - y; @@ -98,24 +98,24 @@ public class Main { return diff; } - /// CHECK-START: long Main.sadL3(int, int) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.sadL3(int, int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.sadL3Alt(int, int) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsLong + /// CHECK-START: long Main.sadL3Alt(int, int) instruction_simplifier$after_gvn (after) + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL3Alt(int x, int y) { long diff = x - y; diff --git a/test/660-checker-sad-long/src/Main.java b/test/660-checker-sad-long/src/Main.java index d2e32acffc44307d4b84d12bed8a9245ef0ea84a..b9eeb5ff8136dd25f22406ee0226ba9ea7eaa11d 100644 --- a/test/660-checker-sad-long/src/Main.java +++ b/test/660-checker-sad-long/src/Main.java @@ -19,28 +19,28 @@ */ public class Main { - /// CHECK-START: long Main.sad1(long, long) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.sad1(long, long) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.sad1(long, long) instruction_simplifier$after_inlining (after) - /// CHECK-NOT: InvokeStaticOrDirect intrinsic:MathAbsLong + /// CHECK-START: long Main.sad1(long, long) instruction_simplifier$after_gvn (after) + /// CHECK-NOT: Abs // // NOTE: for direct 64-bit operands, this is not an ABS. static long sad1(long x, long y) { return x >= y ? x - y : y - x; } - /// CHECK-START: long Main.sad2(long, long) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.sad2(long, long) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.sad3(long, long) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsLong + /// CHECK-START: long Main.sad3(long, long) instruction_simplifier$after_gvn (after) + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sad3(long x, long y) { long diff = x - y; return diff >= 0 ? diff : -diff; } - /// CHECK-START: long Main.sad3Alt(long, long) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.sad3Alt(long, long) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.sad1(short, short) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsInt + /// CHECK-START: int Main.sad1(short, short) instruction_simplifier$after_gvn (after) + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad1(short x, short y) { return x >= y ? x - y : y - x; } - /// CHECK-START: int Main.sad2(short, short) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.sad2(short, short) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.sad3(short, short) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsInt + /// CHECK-START: int Main.sad3(short, short) instruction_simplifier$after_gvn (after) + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad3(short x, short y) { int diff = x - y; return diff >= 0 ? diff : -diff; } - /// CHECK-START: int Main.sad3Alt(short, short) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.sad3Alt(short, short) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.sadL1(short, short) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsLong + /// CHECK-START: long Main.sadL1(short, short) instruction_simplifier$after_gvn (after) + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL1(short x, short y) { long xl = x; @@ -80,12 +80,12 @@ public class Main { return xl >= yl ? xl - yl : yl - xl; } - /// CHECK-START: long Main.sadL2(short, short) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.sadL2(short, short) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.sadL3(short, short) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsLong + /// CHECK-START: long Main.sadL3(short, short) instruction_simplifier$after_gvn (after) + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL3(short x, short y) { long diff = x - y; return diff >= 0L ? diff : -diff; } - /// CHECK-START: long Main.sadL3Alt(short, short) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.sadL3Alt(short, short) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<