diff options
205 files changed, 8588 insertions, 3692 deletions
diff --git a/Android.mk b/Android.mk index 593ee0422e..7d313821ef 100644 --- a/Android.mk +++ b/Android.mk @@ -17,13 +17,13 @@ LOCAL_PATH := $(call my-dir) art_path := $(LOCAL_PATH) -art_build_path := $(art_path)/build -include $(art_build_path)/Android.common.mk ######################################################################## -# clean-oat targets +# clean-oat rules # +include $(art_path)/build/Android.common_path.mk + # following the example of build's dont_bother for clean targets ifneq (,$(filter clean-oat,$(MAKECMDGOALS))) art_dont_bother := true @@ -45,6 +45,11 @@ clean-oat-host: rm -f $(HOST_CORE_IMG_OUT) rm -f $(HOST_CORE_OAT_OUT) rm -f $(HOST_OUT_JAVA_LIBRARIES)/$(ART_HOST_ARCH)/*.odex +ifneq ($(HOST_PREFER_32_BIT),true) + rm -f $(2ND_HOST_CORE_IMG_OUT) + rm -f $(2ND_HOST_CORE_OAT_OUT) + rm -f $(HOST_OUT_JAVA_LIBRARIES)/$(2ND_ART_HOST_ARCH)/*.odex +endif rm -f $(TARGET_CORE_IMG_OUT) rm -f $(TARGET_CORE_OAT_OUT) ifdef TARGET_2ND_ARCH @@ -67,9 +72,9 @@ endif .PHONY: clean-oat-target clean-oat-target: adb remount - adb shell rm -rf $(ART_NATIVETEST_DIR) - adb shell rm -rf $(ART_TEST_DIR) - adb shell rm -rf $(ART_DALVIK_CACHE_DIR)/* + adb shell rm -rf $(ART_TARGET_NATIVETEST_DIR) + adb shell rm -rf $(ART_TARGET_TEST_DIR) + adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*/* adb shell rm -rf $(DEXPREOPT_BOOT_JAR_DIR)/$(DEX2OAT_TARGET_ARCH) adb shell rm -rf system/app/$(DEX2OAT_TARGET_ARCH) ifdef TARGET_2ND_ARCH @@ -81,7 +86,13 @@ endif ifneq ($(art_dont_bother),true) ######################################################################## -# product targets +# cpplint rules to style check art source files + +include $(art_path)/build/Android.cpplint.mk + +######################################################################## +# product rules + include $(art_path)/runtime/Android.mk include $(art_path)/compiler/Android.mk include $(art_path)/dex2oat/Android.mk @@ -89,251 +100,207 @@ include $(art_path)/disassembler/Android.mk include $(art_path)/oatdump/Android.mk include $(art_path)/dalvikvm/Android.mk include $(art_path)/tools/Android.mk -include $(art_build_path)/Android.oat.mk +include $(art_path)/build/Android.oat.mk include $(art_path)/sigchainlib/Android.mk - - # ART_HOST_DEPENDENCIES depends on Android.executable.mk above for ART_HOST_EXECUTABLES -ART_HOST_DEPENDENCIES := $(ART_HOST_EXECUTABLES) $(HOST_OUT_JAVA_LIBRARIES)/core-libart-hostdex.jar -ART_HOST_DEPENDENCIES += $(HOST_LIBRARY_PATH)/libjavacore$(ART_HOST_SHLIB_EXTENSION) -ART_TARGET_DEPENDENCIES := $(ART_TARGET_EXECUTABLES) $(TARGET_OUT_JAVA_LIBRARIES)/core-libart.jar $(TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so +ART_HOST_DEPENDENCIES := \ + $(ART_HOST_EXECUTABLES) \ + $(HOST_OUT_JAVA_LIBRARIES)/core-libart-hostdex.jar \ + $(HOST_LIBRARY_PATH)/libjavacore$(ART_HOST_SHLIB_EXTENSION) +ART_TARGET_DEPENDENCIES := \ + $(ART_TARGET_EXECUTABLES) \ + $(TARGET_OUT_JAVA_LIBRARIES)/core-libart.jar \ + $(TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so ifdef TARGET_2ND_ARCH ART_TARGET_DEPENDENCIES += $(2ND_TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so endif ######################################################################## -# test targets - -include $(art_path)/test/Android.mk -include $(art_build_path)/Android.gtest.mk +# test rules -$(eval $(call combine-art-multi-target-var,ART_TARGET_GTEST_TARGETS)) -$(eval $(call combine-art-multi-target-var,ART_TARGET_GTEST_EXECUTABLES)) +# All the dependencies that must be built ahead of sync-ing them onto the target device. +TEST_ART_TARGET_SYNC_DEPS := -# The ART_*_TEST_DEPENDENCIES definitions: -# - depend on Android.oattest.mk above for ART_TEST_*_DEX_FILES -# - depend on Android.gtest.mk above for ART_*_GTEST_EXECUTABLES -ART_HOST_TEST_DEPENDENCIES := $(ART_HOST_DEPENDENCIES) $(ART_HOST_GTEST_EXECUTABLES) $(ART_TEST_HOST_DEX_FILES) $(HOST_CORE_IMG_OUT) +include $(art_path)/build/Android.common_test.mk +include $(art_path)/build/Android.gtest.mk +include $(art_path)/test/Android.oat.mk +include $(art_path)/test/Android.run-test.mk -define declare-art-target-test-dependencies-var -ART_TARGET_TEST_DEPENDENCIES$(1) := $(ART_TARGET_DEPENDENCIES) $(ART_TARGET_GTEST_EXECUTABLES$(1)) $(ART_TEST_TARGET_DEX_FILES$(1)) $(TARGET_CORE_IMG_OUT$(1)) -endef -$(eval $(call call-art-multi-target-var,declare-art-target-test-dependencies-var,ART_TARGET_TEST_DEPENDENCIES)) +# Sync test files to the target, depends upon all things that must be pushed to the target. +.PHONY: test-art-target-sync +test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS) + adb remount + adb sync + adb shell mkdir -p $(ART_TARGET_TEST_DIR) -include $(art_build_path)/Android.libarttest.mk +# Undefine variable now its served its purpose. +TEST_ART_TARGET_SYNC_DEPS := # "mm test-art" to build and run all tests on host and device .PHONY: test-art test-art: test-art-host test-art-target - @echo test-art PASSED + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) .PHONY: test-art-gtest test-art-gtest: test-art-host-gtest test-art-target-gtest - @echo test-art-gtest PASSED - -.PHONY: test-art-oat -test-art-oat: test-art-host-oat test-art-target-oat - @echo test-art-oat PASSED + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) .PHONY: test-art-run-test test-art-run-test: test-art-host-run-test test-art-target-run-test - @echo test-art-run-test PASSED + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) ######################################################################## -# host test targets +# host test rules -.PHONY: test-art-host-vixl VIXL_TEST_DEPENDENCY := # We can only run the vixl tests on 64-bit hosts (vixl testing issue) when its a # top-level build (to declare the vixl test rule). -ifneq ($(HOST_IS_64_BIT),) +ifneq ($(HOST_PREFER_32_BIT),true) ifeq ($(ONE_SHOT_MAKEFILE),) VIXL_TEST_DEPENDENCY := run-vixl-tests endif endif +.PHONY: test-art-host-vixl test-art-host-vixl: $(VIXL_TEST_DEPENDENCY) -# "mm test-art-host" to build and run all host tests +# "mm test-art-host" to build and run all host tests. .PHONY: test-art-host test-art-host: test-art-host-gtest test-art-host-oat test-art-host-run-test test-art-host-vixl - @echo test-art-host PASSED - -.PHONY: test-art-host-interpreter -test-art-host-interpreter: test-art-host-oat-interpreter test-art-host-run-test-interpreter - @echo test-art-host-interpreter PASSED - -.PHONY: test-art-host-dependencies -test-art-host-dependencies: $(ART_HOST_TEST_DEPENDENCIES) $(HOST_LIBRARY_PATH)/libarttest$(ART_HOST_SHLIB_EXTENSION) $(HOST_CORE_DEX_LOCATIONS) - -.PHONY: test-art-host-gtest -test-art-host-gtest: $(ART_HOST_GTEST_TARGETS) - @echo test-art-host-gtest PASSED - -# "mm valgrind-test-art-host-gtest" to build and run the host gtests under valgrind. -.PHONY: valgrind-test-art-host-gtest -valgrind-test-art-host-gtest: $(ART_HOST_VALGRIND_GTEST_TARGETS) - @echo valgrind-test-art-host-gtest PASSED - -.PHONY: test-art-host-oat-default -test-art-host-oat-default: $(ART_TEST_HOST_OAT_DEFAULT_TARGETS) - @echo test-art-host-oat-default PASSED - -.PHONY: test-art-host-oat-interpreter -test-art-host-oat-interpreter: $(ART_TEST_HOST_OAT_INTERPRETER_TARGETS) - @echo test-art-host-oat-interpreter PASSED + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) -.PHONY: test-art-host-oat -test-art-host-oat: test-art-host-oat-default test-art-host-oat-interpreter - @echo test-art-host-oat PASSED +# All host tests that run solely with the default compiler. +.PHONY: test-art-host-default +test-art-host-default: test-art-host-oat-default test-art-host-run-test-default + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) -FAILING_OPTIMIZING_MESSAGE := failed with the optimizing compiler. If the test passes \ - with Quick and interpreter, it is probably just a bug in the optimizing compiler. Please \ - add the test name to the FAILING_OPTIMIZING_TESTS Makefile variable in art/Android.mk, \ - and file a bug. +# All host tests that run solely with the optimizing compiler. +.PHONY: test-art-host-optimizing +test-art-host-optimizing: test-art-host-oat-optimizing test-art-host-run-test-optimizing + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) -# Placeholder for failing tests on the optimizing compiler. - -define declare-test-art-host-run-test -.PHONY: test-art-host-run-test-default-$(1) -test-art-host-run-test-default-$(1): test-art-host-dependencies $(DX) $(HOST_OUT_EXECUTABLES)/jasmin - DX=$(abspath $(DX)) JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) art/test/run-test $(addprefix --runtime-option ,$(DALVIKVM_FLAGS)) --host $(1) - @echo test-art-host-run-test-default-$(1) PASSED - -TEST_ART_HOST_RUN_TEST_DEFAULT_TARGETS += test-art-host-run-test-default-$(1) - -.PHONY: test-art-host-run-test-optimizing-$(1) -test-art-host-run-test-optimizing-$(1): test-art-host-dependencies $(DX) $(HOST_OUT_EXECUTABLES)/jasmin - DX=$(abspath $(DX)) JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) art/test/run-test -Xcompiler-option --compiler-backend=Optimizing $(addprefix --runtime-option ,$(DALVIKVM_FLAGS)) --host $(1) \ - || (echo -e "\x1b[31;01mTest $(1) $(FAILING_OPTIMIZING_MESSAGE)\x1b[0m" && exit 1) - @echo test-art-host-run-test-optimizing-$(1) PASSED - -TEST_ART_HOST_RUN_TEST_OPTIMIZING_TARGETS += test-art-host-run-test-optimizing-$(1) - -.PHONY: test-art-host-run-test-interpreter-$(1) -test-art-host-run-test-interpreter-$(1): test-art-host-dependencies $(DX) $(HOST_OUT_EXECUTABLES)/jasmin - DX=$(abspath $(DX)) JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) art/test/run-test $(addprefix --runtime-option ,$(DALVIKVM_FLAGS)) --host --interpreter $(1) - @echo test-art-host-run-test-interpreter-$(1) PASSED - -TEST_ART_HOST_RUN_TEST_INTERPRETER_TARGETS += test-art-host-run-test-interpreter-$(1) - -.PHONY: test-art-host-run-test-$(1) -test-art-host-run-test-$(1): test-art-host-run-test-default-$(1) test-art-host-run-test-interpreter-$(1) test-art-host-run-test-optimizing-$(1) - -endef - -$(foreach test, $(TEST_ART_RUN_TESTS), $(eval $(call declare-test-art-host-run-test,$(test)))) - -.PHONY: test-art-host-run-test-default -test-art-host-run-test-default: $(TEST_ART_HOST_RUN_TEST_DEFAULT_TARGETS) - @echo test-art-host-run-test-default PASSED - -FAILING_OPTIMIZING_TESTS := -$(foreach test, $(FAILING_OPTIMIZING_TESTS), \ - $(eval TEST_ART_HOST_RUN_TEST_OPTIMIZING_TARGETS := $(filter-out test-art-host-run-test-optimizing-$(test), $(TEST_ART_HOST_RUN_TEST_OPTIMIZING_TARGETS)))) - -.PHONY: test-art-host-run-test-optimizing -test-art-host-run-test-optimizing: $(TEST_ART_HOST_RUN_TEST_OPTIMIZING_TARGETS) - $(foreach test, $(FAILING_OPTIMIZING_TESTS), $(info Optimizing compiler has skipped $(test))) - @echo test-art-host-run-test-optimizing PASSED - -.PHONY: test-art-host-run-test-interpreter -test-art-host-run-test-interpreter: $(TEST_ART_HOST_RUN_TEST_INTERPRETER_TARGETS) - @echo test-art-host-run-test-interpreter PASSED - -.PHONY: test-art-host-run-test -test-art-host-run-test: test-art-host-run-test-default test-art-host-run-test-interpreter test-art-host-run-test-optimizing - @echo test-art-host-run-test PASSED +# All host tests that run solely on the interpreter. +.PHONY: test-art-host-interpreter +test-art-host-interpreter: test-art-host-oat-interpreter test-art-host-run-test-interpreter + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +# Primary host architecture variants: +.PHONY: test-art-host$(ART_PHONY_TEST_HOST_SUFFIX) +test-art-host$(ART_PHONY_TEST_HOST_SUFFIX): test-art-host-gtest$(ART_PHONY_TEST_HOST_SUFFIX) \ + test-art-host-oat$(ART_PHONY_TEST_HOST_SUFFIX) test-art-host-run-test$(ART_PHONY_TEST_HOST_SUFFIX) + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +.PHONY: test-art-host-default$(ART_PHONY_TEST_HOST_SUFFIX) +test-art-host-default$(ART_PHONY_TEST_HOST_SUFFIX): test-art-host-oat-default$(ART_PHONY_TEST_HOST_SUFFIX) \ + test-art-host-run-test-default$(ART_PHONY_TEST_HOST_SUFFIX) + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +.PHONY: test-art-host-optimizing$(ART_PHONY_TEST_HOST_SUFFIX) +test-art-host-optimizing$(ART_PHONY_TEST_HOST_SUFFIX): test-art-host-oat-optimizing$(ART_PHONY_TEST_HOST_SUFFIX) \ + test-art-host-run-test-optimizing$(ART_PHONY_TEST_HOST_SUFFIX) + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +.PHONY: test-art-host-interpreter$(ART_PHONY_TEST_HOST_SUFFIX) +test-art-host-interpreter$(ART_PHONY_TEST_HOST_SUFFIX): test-art-host-oat-interpreter$(ART_PHONY_TEST_HOST_SUFFIX) \ + test-art-host-run-test-interpreter$(ART_PHONY_TEST_HOST_SUFFIX) + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +# Secondary host architecture variants: +ifneq ($(HOST_PREFER_32_BIT),true) +.PHONY: test-art-host$(2ND_ART_PHONY_TEST_HOST_SUFFIX) +test-art-host$(2ND_ART_PHONY_TEST_HOST_SUFFIX): test-art-host-gtest$(2ND_ART_PHONY_TEST_HOST_SUFFIX) \ + test-art-host-oat$(2ND_ART_PHONY_TEST_HOST_SUFFIX) test-art-host-run-test$(2ND_ART_PHONY_TEST_HOST_SUFFIX) + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +.PHONY: test-art-host-default$(2ND_ART_PHONY_TEST_HOST_SUFFIX) +test-art-host-default$(2ND_ART_PHONY_TEST_HOST_SUFFIX): test-art-host-oat-default$(2ND_ART_PHONY_TEST_HOST_SUFFIX) \ + test-art-host-run-test-default$(2ND_ART_PHONY_TEST_HOST_SUFFIX) + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +.PHONY: test-art-host-optimizing$(2ND_ART_PHONY_TEST_HOST_SUFFIX) +test-art-host-optimizing$(2ND_ART_PHONY_TEST_HOST_SUFFIX): test-art-host-oat-optimizing$(2ND_ART_PHONY_TEST_HOST_SUFFIX) \ + test-art-host-run-test-optimizing$(2ND_ART_PHONY_TEST_HOST_SUFFIX) + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +.PHONY: test-art-host-interpreter$(2ND_ART_PHONY_TEST_HOST_SUFFIX) +test-art-host-interpreter$(2ND_ART_PHONY_TEST_HOST_SUFFIX): test-art-host-oat-interpreter$(2ND_ART_PHONY_TEST_HOST_SUFFIX) \ + test-art-host-run-test-interpreter$(2ND_ART_PHONY_TEST_HOST_SUFFIX) + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) +endif ######################################################################## -# target test targets - -# "mm test-art-target" to build and run all target tests -define declare-test-art-target -.PHONY: test-art-target$(1) -test-art-target$(1): test-art-target-gtest$(1) test-art-target-oat$(1) test-art-target-run-test$(1) - @echo test-art-target$(1) PASSED -endef -$(eval $(call call-art-multi-target-rule,declare-test-art-target,test-art-target)) - -define declare-test-art-target-dependencies -.PHONY: test-art-target-dependencies$(1) -test-art-target-dependencies$(1): $(ART_TARGET_TEST_DEPENDENCIES$(1)) $(ART_TARGET_LIBARTTEST_$(1)) -endef -$(eval $(call call-art-multi-target-rule,declare-test-art-target-dependencies,test-art-target-dependencies)) - - -.PHONY: test-art-target-sync -test-art-target-sync: test-art-target-dependencies$(ART_PHONY_TEST_TARGET_SUFFIX) test-art-target-dependencies$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) - adb remount - adb sync - adb shell mkdir -p $(ART_TEST_DIR) - - -define declare-test-art-target-gtest -.PHONY: test-art-target-gtest$(1) -test-art-target-gtest$(1): $(ART_TARGET_GTEST_TARGETS$(1)) - @echo test-art-target-gtest$(1) PASSED -endef -$(eval $(call call-art-multi-target-rule,declare-test-art-target-gtest,test-art-target-gtest)) - - -define declare-test-art-target-oat -.PHONY: test-art-target-oat$(1) -test-art-target-oat$(1): $(ART_TEST_TARGET_OAT_TARGETS$(1)) - @echo test-art-target-oat$(1) PASSED -endef -$(eval $(call call-art-multi-target-rule,declare-test-art-target-oat,test-art-target-oat)) - - -define declare-test-art-target-run-test-impl -$(2)run_test_$(1) := -ifeq ($($(2)ART_PHONY_TEST_TARGET_SUFFIX),64) - $(2)run_test_$(1) := --64 +# target test rules + +# "mm test-art-target" to build and run all target tests. +.PHONY: test-art-target +test-art-target: test-art-target-gtest test-art-target-oat test-art-target-run-test + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +# All target tests that run solely with the default compiler. +.PHONY: test-art-target-default +test-art-target-default: test-art-target-oat-default test-art-target-run-test-default + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +# All target tests that run solely with the optimizing compiler. +.PHONY: test-art-target-optimizing +test-art-target-optimizing: test-art-target-oat-optimizing test-art-target-run-test-optimizing + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +# All target tests that run solely on the interpreter. +.PHONY: test-art-target-interpreter +test-art-target-interpreter: test-art-target-oat-interpreter test-art-target-run-test-interpreter + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +# Primary target architecture variants: +.PHONY: test-art-target$(ART_PHONY_TEST_TARGET_SUFFIX) +test-art-target$(ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-gtest$(ART_PHONY_TEST_TARGET_SUFFIX) \ + test-art-target-oat$(ART_PHONY_TEST_TARGET_SUFFIX) test-art-target-run-test$(ART_PHONY_TEST_TARGET_SUFFIX) + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +.PHONY: test-art-target-default$(ART_PHONY_TEST_TARGET_SUFFIX) +test-art-target-default$(ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-oat-default$(ART_PHONY_TEST_TARGET_SUFFIX) \ + test-art-target-run-test-default$(ART_PHONY_TEST_TARGET_SUFFIX) + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +.PHONY: test-art-target-optimizing$(ART_PHONY_TEST_TARGET_SUFFIX) +test-art-target-optimizing$(ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-oat-optimizing$(ART_PHONY_TEST_TARGET_SUFFIX) \ + test-art-target-run-test-optimizing$(ART_PHONY_TEST_TARGET_SUFFIX) + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +.PHONY: test-art-target-interpreter$(ART_PHONY_TEST_TARGET_SUFFIX) +test-art-target-interpreter$(ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-oat-interpreter$(ART_PHONY_TEST_TARGET_SUFFIX) \ + test-art-target-run-test-interpreter$(ART_PHONY_TEST_TARGET_SUFFIX) + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +# Secondary target architecture variants: +ifdef TARGET_2ND_ARCH +.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-oat$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) test-art-target-run-test$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +.PHONY: test-art-target-default$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) +test-art-target-default$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-oat-default$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) \ + test-art-target-run-test-default$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +.PHONY: test-art-target-optimizing$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) +test-art-target-optimizing$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-oat-optimizing$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) \ + test-art-target-run-test-optimizing$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +.PHONY: test-art-target-interpreter$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) +test-art-target-interpreter$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-oat-interpreter$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) \ + test-art-target-run-test-interpreter$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) endif -.PHONY: test-art-target-run-test-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX) -test-art-target-run-test-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-sync $(DX) $(HOST_OUT_EXECUTABLES)/jasmin - DX=$(abspath $(DX)) JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) art/test/run-test $(addprefix --runtime-option ,$(DALVIKVM_FLAGS)) $$($(2)run_test_$(1)) $(1) - @echo test-art-target-run-test-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX) PASSED -endef - -define declare-test-art-target-run-test - - ifdef TARGET_2ND_ARCH - $(call declare-test-art-target-run-test-impl,$(1),2ND_) - - TEST_ART_TARGET_RUN_TEST_TARGETS$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) += test-art-target-run-test-$(1)$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) - - ifneq ($(ART_PHONY_TEST_TARGET_SUFFIX),) - # Link primary to non-suffix -test-art-target-run-test-$(1): test-art-target-run-test-$(1)$(ART_PHONY_TEST_TARGET_SUFFIX) - endif - endif - $(call declare-test-art-target-run-test-impl,$(1),) - - TEST_ART_TARGET_RUN_TEST_TARGETS$(ART_PHONY_TEST_TARGET_SUFFIX) += test-art-target-run-test-$(1)$(ART_PHONY_TEST_TARGET_SUFFIX) - -test-art-run-test-$(1): test-art-host-run-test-$(1) test-art-target-run-test-$(1) - -endef - -$(foreach test, $(TEST_ART_RUN_TESTS), $(eval $(call declare-test-art-target-run-test,$(test)))) - - -define declare-test-art-target-run-test -.PHONY: test-art-target-run-test$(1) -test-art-target-run-test$(1): $(TEST_ART_TARGET_RUN_TEST_TARGETS$(1)) - @echo test-art-target-run-test$(1) PASSED -endef -$(eval $(call call-art-multi-target-rule,declare-test-art-target-run-test,test-art-target-run-test)) - ######################################################################## -# oat-target and oat-target-sync targets +# oat-target and oat-target-sync rules -OAT_TARGET_TARGETS := +OAT_TARGET_RULES := # $(1): input jar or apk target location define declare-oat-target-target @@ -365,7 +332,7 @@ $$(OUT_OAT_FILE): $(PRODUCT_OUT)/$(1) $(DEFAULT_DEX_PREOPT_BUILT_IMAGE) $(DEX2OA endif -OAT_TARGET_TARGETS += oat-target-$(1) +OAT_TARGET_RULES += oat-target-$(1) endef $(foreach file,\ @@ -375,7 +342,7 @@ $(foreach file,\ $(eval $(call declare-oat-target-target,$(subst $(PRODUCT_OUT)/,,$(file))))) .PHONY: oat-target -oat-target: $(ART_TARGET_DEPENDENCIES) $(DEFAULT_DEX_PREOPT_INSTALLED_IMAGE) $(OAT_TARGET_TARGETS) +oat-target: $(ART_TARGET_DEPENDENCIES) $(DEFAULT_DEX_PREOPT_INSTALLED_IMAGE) $(OAT_TARGET_RULES) .PHONY: oat-target-sync oat-target-sync: oat-target @@ -396,73 +363,16 @@ build-art-target: $(ART_TARGET_EXECUTABLES) $(ART_TARGET_GTEST_EXECUTABLES) $(TA ######################################################################## # "m art-host" for just building the files needed to run the art script .PHONY: art-host -art-host: $(HOST_OUT_EXECUTABLES)/art $(HOST_OUT)/bin/dalvikvm $(HOST_OUT)/lib/libart.so $(HOST_OUT)/bin/dex2oat $(HOST_CORE_IMG_OUT) $(HOST_OUT)/lib/libjavacore.so +ifeq ($(HOST_PREFER_32_BIT),true) +art-host: $(HOST_OUT_EXECUTABLES)/art $(HOST_OUT)/bin/dalvikvm32 $(HOST_OUT)/lib/libart.so $(HOST_OUT)/bin/dex2oat $(HOST_CORE_IMG_OUT) $(HOST_OUT)/lib/libjavacore.so +else +art-host: $(HOST_OUT_EXECUTABLES)/art $(HOST_OUT)/bin/dalvikvm64 $(HOST_OUT)/bin/dalvikvm32 $(HOST_OUT)/lib/libart.so $(HOST_OUT)/bin/dex2oat $(HOST_CORE_IMG_OUT) $(HOST_OUT)/lib/libjavacore.so +endif .PHONY: art-host-debug art-host-debug: art-host $(HOST_OUT)/lib/libartd.so $(HOST_OUT)/bin/dex2oatd ######################################################################## -# oatdump targets - -ART_DUMP_OAT_PATH ?= $(OUT_DIR) - -OATDUMP := $(HOST_OUT_EXECUTABLES)/oatdump$(HOST_EXECUTABLE_SUFFIX) -OATDUMPD := $(HOST_OUT_EXECUTABLES)/oatdumpd$(HOST_EXECUTABLE_SUFFIX) -# TODO: for now, override with debug version for better error reporting -OATDUMP := $(OATDUMPD) - -.PHONY: dump-oat -dump-oat: dump-oat-core dump-oat-boot - -.PHONY: dump-oat-core -dump-oat-core: dump-oat-core-host dump-oat-core-target - -.PHONY: dump-oat-core-host -ifeq ($(ART_BUILD_HOST),true) -dump-oat-core-host: $(HOST_CORE_IMG_OUT) $(OATDUMP) - $(OATDUMP) --image=$(HOST_CORE_IMG_LOCATION) --output=$(ART_DUMP_OAT_PATH)/core.host.oatdump.txt - @echo Output in $(ART_DUMP_OAT_PATH)/core.host.oatdump.txt -endif - -.PHONY: dump-oat-core-target -ifeq ($(ART_BUILD_TARGET),true) -dump-oat-core-target: $(TARGET_CORE_IMG_OUT) $(OATDUMP) - $(OATDUMP) --image=$(TARGET_CORE_IMG_LOCATION) --output=$(ART_DUMP_OAT_PATH)/core.target.oatdump.txt --instruction-set=$(TARGET_ARCH) - @echo Output in $(ART_DUMP_OAT_PATH)/core.target.oatdump.txt -endif - -.PHONY: dump-oat-boot-$(TARGET_ARCH) -ifeq ($(ART_BUILD_TARGET_NDEBUG),true) -dump-oat-boot-$(TARGET_ARCH): $(DEFAULT_DEX_PREOPT_BUILT_IMAGE_FILENAME) $(OATDUMP) - $(OATDUMP) --image=$(DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION) --output=$(ART_DUMP_OAT_PATH)/boot.$(TARGET_ARCH).oatdump.txt --instruction-set=$(TARGET_ARCH) - @echo Output in $(ART_DUMP_OAT_PATH)/boot.$(TARGET_ARCH).oatdump.txt -endif - -ifdef TARGET_2ND_ARCH -dump-oat-boot-$(TARGET_2ND_ARCH): $(2ND_DEFAULT_DEX_PREOPT_BUILT_IMAGE_FILENAME) $(OATDUMP) - $(OATDUMP) --image=$(2ND_DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION) --output=$(ART_DUMP_OAT_PATH)/boot.$(TARGET_2ND_ARCH).oatdump.txt --instruction-set=$(TARGET_2ND_ARCH) - @echo Output in $(ART_DUMP_OAT_PATH)/boot.$(TARGET_2ND_ARCH).oatdump.txt -endif - -.PHONY: dump-oat-boot -dump-oat-boot: dump-oat-boot-$(TARGET_ARCH) -ifdef TARGET_2ND_ARCH -dump-oat-boot: dump-oat-boot-$(TARGET_2ND_ARCH) -endif - -.PHONY: dump-oat-Calculator -ifeq ($(ART_BUILD_TARGET_NDEBUG),true) -dump-oat-Calculator: $(TARGET_OUT_APPS)/Calculator.odex $(DEFAULT_DEX_PREOPT_BUILT_IMAGE) $(OATDUMP) - $(OATDUMP) --oat-file=$< --output=$(ART_DUMP_OAT_PATH)/Calculator.oatdump.txt - @echo Output in $(ART_DUMP_OAT_PATH)/Calculator.oatdump.txt -endif - -######################################################################## -# cpplint targets to style check art source files - -include $(art_build_path)/Android.cpplint.mk - -######################################################################## # targets to switch back and forth from libdvm to libart .PHONY: use-art diff --git a/build/Android.common.mk b/build/Android.common.mk index 09f34b3092..150b404d5b 100644 --- a/build/Android.common.mk +++ b/build/Android.common.mk @@ -17,156 +17,22 @@ ifndef ANDROID_COMMON_MK ANDROID_COMMON_MK = true -ART_SUPPORTED_ARCH := arm arm64 mips x86 x86_64 +ART_TARGET_SUPPORTED_ARCH := arm arm64 mips x86 x86_64 +ART_HOST_SUPPORTED_ARCH := x86 x86_64 -ifeq (,$(filter $(TARGET_ARCH),$(ART_SUPPORTED_ARCH))) +ifeq (,$(filter $(TARGET_ARCH),$(ART_TARGET_SUPPORTED_ARCH))) $(warning unsupported TARGET_ARCH=$(TARGET_ARCH)) endif - -# These can be overridden via the environment or by editing to -# enable/disable certain build configuration. -# -# For example, to disable everything but the host debug build you use: -# -# (export ART_BUILD_TARGET_NDEBUG=false && export ART_BUILD_TARGET_DEBUG=false && export ART_BUILD_HOST_NDEBUG=false && ...) -# -# Beware that tests may use the non-debug build for performance, notable 055-enum-performance -# -ART_BUILD_TARGET_NDEBUG ?= true -ART_BUILD_TARGET_DEBUG ?= true -ART_BUILD_HOST_NDEBUG ?= true -ART_BUILD_HOST_DEBUG ?= true - -ifeq ($(HOST_PREFER_32_BIT),true) -ART_HOST_ARCH := $(HOST_2ND_ARCH) -else -ART_HOST_ARCH := $(HOST_ARCH) -endif - -ifeq ($(ART_BUILD_TARGET_NDEBUG),false) -$(info Disabling ART_BUILD_TARGET_NDEBUG) -endif -ifeq ($(ART_BUILD_TARGET_DEBUG),false) -$(info Disabling ART_BUILD_TARGET_DEBUG) -endif -ifeq ($(ART_BUILD_HOST_NDEBUG),false) -$(info Disabling ART_BUILD_HOST_NDEBUG) -endif -ifeq ($(ART_BUILD_HOST_DEBUG),false) -$(info Disabling ART_BUILD_HOST_DEBUG) -endif - -# -# Used to enable smart mode -# -ART_SMALL_MODE := false -ifneq ($(wildcard art/SMALL_ART),) -$(info Enabling ART_SMALL_MODE because of existence of art/SMALL_ART) -ART_SMALL_MODE := true -endif -ifeq ($(WITH_ART_SMALL_MODE), true) -ART_SMALL_MODE := true -endif - -# -# Used to enable SEA mode -# -ART_SEA_IR_MODE := false -ifneq ($(wildcard art/SEA_IR_ART),) -$(info Enabling ART_SEA_IR_MODE because of existence of art/SEA_IR_ART) -ART_SEA_IR_MODE := true -endif -ifeq ($(WITH_ART_SEA_IR_MODE), true) -ART_SEA_IR_MODE := true -endif - -# -# Used to enable portable mode -# -ART_USE_PORTABLE_COMPILER := false -ifneq ($(wildcard art/USE_PORTABLE_COMPILER),) -$(info Enabling ART_USE_PORTABLE_COMPILER because of existence of art/USE_PORTABLE_COMPILER) -ART_USE_PORTABLE_COMPILER := true -endif -ifeq ($(WITH_ART_USE_PORTABLE_COMPILER),true) -$(info Enabling ART_USE_PORTABLE_COMPILER because WITH_ART_USE_PORTABLE_COMPILER=true) -ART_USE_PORTABLE_COMPILER := true -endif - -# -# Used to enable optimizing compiler -# -ART_USE_OPTIMIZING_COMPILER := false -ifneq ($(wildcard art/USE_OPTIMIZING_COMPILER),) -$(info Enabling ART_USE_OPTIMIZING_COMPILER because of existence of art/USE_OPTIMIZING_COMPILER) -ART_USE_OPTIMIZING_COMPILER := true -endif -ifeq ($(WITH_ART_USE_OPTIMIZING_COMPILER), true) -ART_USE_OPTIMIZING_COMPILER := true +ifeq (,$(filter $(HOST_ARCH),$(ART_HOST_SUPPORTED_ARCH))) +$(warning unsupported HOST_ARCH=$(HOST_ARCH)) endif -ifeq ($(ART_USE_OPTIMIZING_COMPILER),true) -DEX2OAT_FLAGS := --compiler-backend=Optimizing -DALVIKVM_FLAGS += -Xcompiler-option --compiler-backend=Optimizing -endif - -# -# Used to change the default GC. Valid values are CMS, SS, GSS. The default is CMS. -# -ART_DEFAULT_GC_TYPE ?= CMS -ART_DEFAULT_GC_TYPE_CFLAGS := -DART_DEFAULT_GC_TYPE_IS_$(ART_DEFAULT_GC_TYPE) - -ifeq ($(ART_USE_PORTABLE_COMPILER),true) - LLVM_ROOT_PATH := external/llvm - # Don't fail a dalvik minimal host build. - -include $(LLVM_ROOT_PATH)/llvm.mk -endif - -# Clang build support. - -# Host. -ART_HOST_CLANG := false -ifneq ($(WITHOUT_HOST_CLANG),true) - # By default, host builds use clang for better warnings. - ART_HOST_CLANG := true -endif - -# Clang on the target: only enabled for ARM64. Target builds use GCC by default. -ART_TARGET_CLANG := -ART_TARGET_CLANG_arm := -ART_TARGET_CLANG_arm64 := true -ART_TARGET_CLANG_mips := -ART_TARGET_CLANG_x86 := -ART_TARGET_CLANG_x86_64 := - -define set-target-local-clang-vars - LOCAL_CLANG := $(ART_TARGET_CLANG) - $(foreach arch,$(ART_SUPPORTED_ARCH), - ifneq ($$(ART_TARGET_CLANG_$(arch)),) - LOCAL_CLANG_$(arch) := $$(ART_TARGET_CLANG_$(arch)) - endif) -endef - -# directory used for dalvik-cache on device -ART_DALVIK_CACHE_DIR := /data/dalvik-cache - -# directory used for gtests on device -ART_NATIVETEST_DIR := /data/nativetest/art -ART_NATIVETEST_OUT := $(TARGET_OUT_DATA_NATIVE_TESTS)/art - -# directory used for oat tests on device -ART_TEST_DIR := /data/art-test -ART_TEST_OUT := $(TARGET_OUT_DATA)/art-test - # Primary vs. secondary 2ND_TARGET_ARCH := $(TARGET_2ND_ARCH) -ART_PHONY_TEST_TARGET_SUFFIX := -2ND_ART_PHONY_TEST_TARGET_SUFFIX := +TARGET_INSTRUCTION_SET_FEATURES := $(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) +2ND_TARGET_INSTRUCTION_SET_FEATURES := $($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) ifdef TARGET_2ND_ARCH - art_test_primary_suffix := - art_test_secondary_suffix := ifneq ($(filter %64,$(TARGET_ARCH)),) - art_test_primary_suffix := 64 ART_PHONY_TEST_TARGET_SUFFIX := 64 2ND_ART_PHONY_TEST_TARGET_SUFFIX := 32 ART_TARGET_ARCH_32 := $(TARGET_2ND_ARCH) @@ -176,268 +42,38 @@ ifdef TARGET_2ND_ARCH $(error Do not know what to do with this multi-target configuration!) endif else + ART_PHONY_TEST_TARGET_SUFFIX := 32 + 2ND_ART_PHONY_TEST_TARGET_SUFFIX := ART_TARGET_ARCH_32 := $(TARGET_ARCH) ART_TARGET_ARCH_64 := endif -ART_CPP_EXTENSION := .cc - ART_HOST_SHLIB_EXTENSION := $(HOST_SHLIB_SUFFIX) ART_HOST_SHLIB_EXTENSION ?= .so - -ART_C_INCLUDES := \ - external/gtest/include \ - external/valgrind/main/include \ - external/valgrind/main \ - external/vixl/src \ - external/zlib \ - frameworks/compile/mclinker/include - -art_cflags := \ - -fno-rtti \ - -std=gnu++11 \ - -ggdb3 \ - -Wall \ - -Werror \ - -Wextra \ - -Wno-sign-promo \ - -Wno-unused-parameter \ - -Wstrict-aliasing \ - -fstrict-aliasing - -ART_TARGET_CLANG_CFLAGS := -ART_TARGET_CLANG_CFLAGS_arm := -ART_TARGET_CLANG_CFLAGS_arm64 := -ART_TARGET_CLANG_CFLAGS_mips := -ART_TARGET_CLANG_CFLAGS_x86 := -ART_TARGET_CLANG_CFLAGS_x86_64 := - -# these are necessary for Clang ARM64 ART builds -ART_TARGET_CLANG_CFLAGS_arm64 += \ - -Wno-implicit-exception-spec-mismatch \ - -DNVALGRIND \ - -Wno-unused-value - -ifeq ($(ART_SMALL_MODE),true) - art_cflags += -DART_SMALL_MODE=1 -endif - -ifeq ($(ART_SEA_IR_MODE),true) - art_cflags += -DART_SEA_IR_MODE=1 -endif - -art_non_debug_cflags := \ - -O3 - -ifeq ($(HOST_OS),linux) - art_non_debug_cflags += \ - -Wframe-larger-than=1728 -endif - -# FIXME: upstream LLVM has a vectorizer bug that needs to be fixed -ART_TARGET_CLANG_CFLAGS_arm64 += \ - -fno-vectorize - -art_debug_cflags := \ - -O1 \ - -DDYNAMIC_ANNOTATIONS_ENABLED=1 \ - -UNDEBUG - -ART_HOST_CFLAGS := $(art_cflags) -DANDROID_SMP=1 -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS) -ART_HOST_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=default -ART_HOST_CFLAGS += $(ART_DEFAULT_GC_TYPE_CFLAGS) - -ART_TARGET_CFLAGS := $(art_cflags) -DART_TARGET -DART_BASE_ADDRESS=$(LIBART_IMG_TARGET_BASE_ADDRESS) -ifeq ($(TARGET_CPU_SMP),true) - ART_TARGET_CFLAGS += -DANDROID_SMP=1 -else - ifeq ($(TARGET_CPU_SMP),false) - ART_TARGET_CFLAGS += -DANDROID_SMP=0 - else - $(warning TARGET_CPU_SMP should be (true|false), found $(TARGET_CPU_SMP)) - # Make sure we emit barriers for the worst case. - ART_TARGET_CFLAGS += -DANDROID_SMP=1 - endif -endif -ART_TARGET_CFLAGS += $(ART_DEFAULT_GC_TYPE_CFLAGS) - -# DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES is set in ../build/core/dex_preopt.mk based on -# the TARGET_CPU_VARIANT -ifeq ($(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES),) -$(error Required DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES is not set) -endif -ART_TARGET_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) - -# Enable thread-safety for GCC 4.6, and clang, but not for GCC 4.7 or later where this feature was -# removed. Warn when -Wthread-safety is not used. -ifneq ($(filter 4.6 4.6.%, $(TARGET_GCC_VERSION)),) - ART_TARGET_CFLAGS += -Wthread-safety +ifeq ($(HOST_PREFER_32_BIT),true) + ART_PHONY_TEST_HOST_SUFFIX := 32 + 2ND_ART_PHONY_TEST_HOST_SUFFIX := + ART_HOST_ARCH_32 := x86 + ART_HOST_ARCH_64 := + ART_HOST_ARCH := x86 + 2ND_ART_HOST_ARCH := + 2ND_HOST_ARCH := + ART_HOST_LIBRARY_PATH := $(HOST_LIBRARY_PATH) + 2ND_ART_HOST_LIBRARY_PATH := + ART_HOST_OUT_SHARED_LIBRARIES := $(2ND_HOST_OUT_SHARED_LIBRARIES) + 2ND_ART_HOST_OUT_SHARED_LIBRARIES := else - # FIXME: add -Wthread-safety when the problem is fixed - ifeq ($(ART_TARGET_CLANG),true) - ART_TARGET_CFLAGS += - else - # Warn if -Wthread-safety is not suport and not doing a top-level or 'mma' build. - ifneq ($(ONE_SHOT_MAKEFILE),) - # Enable target GCC 4.6 with: export TARGET_GCC_VERSION_EXP=4.6 - $(info Using target GCC $(TARGET_GCC_VERSION) disables thread-safety checks.) - endif - endif -endif -# We compile with GCC 4.6 or clang on the host, both of which support -Wthread-safety. -ART_HOST_CFLAGS += -Wthread-safety - -# To use oprofile_android --callgraph, uncomment this and recompile with "mmm art -B -j16" -# ART_TARGET_CFLAGS += -fno-omit-frame-pointer -marm -mapcs - -# Addition CPU specific CFLAGS. -ifeq ($(TARGET_ARCH),arm) - ifneq ($(filter cortex-a15, $(TARGET_CPU_VARIANT)),) - # Fake a ARM feature for LPAE support. - ART_TARGET_CFLAGS += -D__ARM_FEATURE_LPAE=1 - endif -endif - -ART_HOST_NON_DEBUG_CFLAGS := $(art_non_debug_cflags) -ART_TARGET_NON_DEBUG_CFLAGS := $(art_non_debug_cflags) - -# TODO: move -fkeep-inline-functions to art_debug_cflags when target gcc > 4.4 (and -lsupc++) -ART_HOST_DEBUG_CFLAGS := $(art_debug_cflags) -fkeep-inline-functions -ART_HOST_DEBUG_LDLIBS := -lsupc++ - -ifneq ($(HOST_OS),linux) - # Some Mac OS pthread header files are broken with -fkeep-inline-functions. - ART_HOST_DEBUG_CFLAGS := $(filter-out -fkeep-inline-functions,$(ART_HOST_DEBUG_CFLAGS)) - # Mac OS doesn't have libsupc++. - ART_HOST_DEBUG_LDLIBS := $(filter-out -lsupc++,$(ART_HOST_DEBUG_LDLIBS)) -endif - -ART_TARGET_DEBUG_CFLAGS := $(art_debug_cflags) - -# $(1): ndebug_or_debug -define set-target-local-cflags-vars - LOCAL_CFLAGS += $(ART_TARGET_CFLAGS) - LOCAL_CFLAGS_x86 += $(ART_TARGET_CFLAGS_x86) - art_target_cflags_ndebug_or_debug := $(1) - ifeq ($$(art_target_cflags_ndebug_or_debug),debug) - LOCAL_CFLAGS += $(ART_TARGET_DEBUG_CFLAGS) - else - LOCAL_CFLAGS += $(ART_TARGET_NON_DEBUG_CFLAGS) - endif - - # TODO: Also set when ART_TARGET_CLANG_$(arch)!=false and ART_TARGET_CLANG==true - $(foreach arch,$(ART_SUPPORTED_ARCH), - ifeq ($$(ART_TARGET_CLANG_$(arch)),true) - LOCAL_CFLAGS_$(arch) += $$(ART_TARGET_CLANG_CFLAGS_$(arch)) - endif) -endef - -ART_BUILD_TARGET := false -ART_BUILD_HOST := false -ART_BUILD_NDEBUG := false -ART_BUILD_DEBUG := false -ifeq ($(ART_BUILD_TARGET_NDEBUG),true) - ART_BUILD_TARGET := true - ART_BUILD_NDEBUG := true + ART_PHONY_TEST_HOST_SUFFIX := 64 + 2ND_ART_PHONY_TEST_HOST_SUFFIX := 32 + ART_HOST_ARCH_32 := x86 + ART_HOST_ARCH_64 := x86_64 + ART_HOST_ARCH := x86_64 + 2ND_ART_HOST_ARCH := x86 + 2ND_HOST_ARCH := x86 + ART_HOST_LIBRARY_PATH := $(HOST_LIBRARY_PATH) + 2ND_ART_HOST_LIBRARY_PATH := $(HOST_LIBRARY_PATH)32 + ART_HOST_OUT_SHARED_LIBRARIES := $(HOST_OUT_SHARED_LIBRARIES) + 2ND_ART_HOST_OUT_SHARED_LIBRARIES := $(2ND_HOST_OUT_SHARED_LIBRARIES) endif -ifeq ($(ART_BUILD_TARGET_DEBUG),true) - ART_BUILD_TARGET := true - ART_BUILD_DEBUG := true -endif -ifeq ($(ART_BUILD_HOST_NDEBUG),true) - ART_BUILD_HOST := true - ART_BUILD_NDEBUG := true -endif -ifeq ($(ART_BUILD_HOST_DEBUG),true) - ART_BUILD_HOST := true - ART_BUILD_DEBUG := true -endif - -# Helper function to call a function twice with a target suffix -# $(1): The generator function for the rules -# Has one argument, the suffix -define call-art-multi-target - $(call $(1),$(ART_PHONY_TEST_TARGET_SUFFIX)) - - ifdef TARGET_2ND_ARCH - $(call $(1),$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)) - endif -endef - -# Helper function to combine two variables with suffixes together. -# $(1): The base name. -define combine-art-multi-target-var - ifdef TARGET_2ND_ARCH - ifneq ($(ART_PHONY_TEST_TARGET_SUFFIX),) - ifneq ($(2ND_ART_PHONY_TEST_TARGET_SUFFIX),) -$(1) := $($(1)$(ART_PHONY_TEST_TARGET_SUFFIX)) $($(1)$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)) - endif - endif - endif -endef - - -# Helper function to define a variable twice with a target suffix. Assume the name generated is -# derived from $(2) so we can create a combined var. -# $(1): The generator function for the rules -# Has one argument, the suffix -define call-art-multi-target-var - $(call $(1),$(ART_PHONY_TEST_TARGET_SUFFIX)) - - ifdef TARGET_2ND_ARCH - $(call $(1),$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)) - - # Link both together, if it makes sense - ifneq ($(ART_PHONY_TEST_TARGET_SUFFIX),) - ifneq ($(2ND_ART_PHONY_TEST_TARGET_SUFFIX),) -$(2) := $(2)$(ART_PHONY_TEST_TARGET_SUFFIX) $(2)$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) - endif - endif - - endif -endef - -# Helper function to call a function twice with a target suffix. Assume it generates make rules -# with the given name, and link them. -# $(1): The generator function for the rules -# Has one argument, the suffix -# $(2): The base rule name, necessary for the link -# We assume we can link the names together easily... -define call-art-multi-target-rule - $(call $(1),$(ART_PHONY_TEST_TARGET_SUFFIX)) - - ifdef TARGET_2ND_ARCH - $(call $(1),$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)) - - # Link both together, if it makes sense - ifneq ($(ART_PHONY_TEST_TARGET_SUFFIX),) - ifneq ($(2ND_ART_PHONY_TEST_TARGET_SUFFIX),) -.PHONY: $(2) -$(2): $(2)$(ART_PHONY_TEST_TARGET_SUFFIX) $(2)$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) - endif - endif - endif -endef - -HOST_CORE_OAT := $(HOST_OUT_JAVA_LIBRARIES)/$(ART_HOST_ARCH)/core.oat -TARGET_CORE_OAT := $(ART_TEST_DIR)/$(DEX2OAT_TARGET_ARCH)/core.oat -ifdef TARGET_2ND_ARCH -2ND_TARGET_CORE_OAT := $(2ND_ART_TEST_DIR)/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH)/core.oat -endif - -HOST_CORE_OAT_OUT := $(HOST_OUT_JAVA_LIBRARIES)/$(ART_HOST_ARCH)/core.oat -TARGET_CORE_OAT_OUT := $(ART_TEST_OUT)/$(DEX2OAT_TARGET_ARCH)/core.oat -ifdef TARGET_2ND_ARCH -2ND_TARGET_CORE_OAT_OUT := $(ART_TEST_OUT)/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH)/core.oat -endif - -HOST_CORE_IMG_OUT := $(HOST_OUT_JAVA_LIBRARIES)/$(ART_HOST_ARCH)/core.art -TARGET_CORE_IMG_OUT := $(ART_TEST_OUT)/$(DEX2OAT_TARGET_ARCH)/core.art -ifdef TARGET_2ND_ARCH -2ND_TARGET_CORE_IMG_OUT := $(ART_TEST_OUT)/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH)/core.art -endif - -HOST_CORE_IMG_LOCATION := $(HOST_OUT_JAVA_LIBRARIES)/core.art -TARGET_CORE_IMG_LOCATION := $(ART_TEST_OUT)/core.art endif # ANDROID_COMMON_MK diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk new file mode 100644 index 0000000000..5cfdbfd816 --- /dev/null +++ b/build/Android.common_build.mk @@ -0,0 +1,323 @@ +# +# 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. +# + +ifndef ANDROID_COMMON_BUILD_MK +ANDROID_COMMON_BUILD_MK = true + +include art/build/Android.common.mk + +# These can be overridden via the environment or by editing to +# enable/disable certain build configuration. +# +# For example, to disable everything but the host debug build you use: +# +# (export ART_BUILD_TARGET_NDEBUG=false && export ART_BUILD_TARGET_DEBUG=false && export ART_BUILD_HOST_NDEBUG=false && ...) +# +# Beware that tests may use the non-debug build for performance, notable 055-enum-performance +# +ART_BUILD_TARGET_NDEBUG ?= true +ART_BUILD_TARGET_DEBUG ?= true +ART_BUILD_HOST_NDEBUG ?= true +ART_BUILD_HOST_DEBUG ?= true + +ifeq ($(ART_BUILD_TARGET_NDEBUG),false) +$(info Disabling ART_BUILD_TARGET_NDEBUG) +endif +ifeq ($(ART_BUILD_TARGET_DEBUG),false) +$(info Disabling ART_BUILD_TARGET_DEBUG) +endif +ifeq ($(ART_BUILD_HOST_NDEBUG),false) +$(info Disabling ART_BUILD_HOST_NDEBUG) +endif +ifeq ($(ART_BUILD_HOST_DEBUG),false) +$(info Disabling ART_BUILD_HOST_DEBUG) +endif + +# +# Used to enable smart mode +# +ART_SMALL_MODE := false +ifneq ($(wildcard art/SMALL_ART),) +$(info Enabling ART_SMALL_MODE because of existence of art/SMALL_ART) +ART_SMALL_MODE := true +endif +ifeq ($(WITH_ART_SMALL_MODE), true) +ART_SMALL_MODE := true +endif + +# +# Used to enable SEA mode +# +ART_SEA_IR_MODE := false +ifneq ($(wildcard art/SEA_IR_ART),) +$(info Enabling ART_SEA_IR_MODE because of existence of art/SEA_IR_ART) +ART_SEA_IR_MODE := true +endif +ifeq ($(WITH_ART_SEA_IR_MODE), true) +ART_SEA_IR_MODE := true +endif + +# +# Used to enable portable mode +# +ART_USE_PORTABLE_COMPILER := false +ifneq ($(wildcard art/USE_PORTABLE_COMPILER),) +$(info Enabling ART_USE_PORTABLE_COMPILER because of existence of art/USE_PORTABLE_COMPILER) +ART_USE_PORTABLE_COMPILER := true +endif +ifeq ($(WITH_ART_USE_PORTABLE_COMPILER),true) +$(info Enabling ART_USE_PORTABLE_COMPILER because WITH_ART_USE_PORTABLE_COMPILER=true) +ART_USE_PORTABLE_COMPILER := true +endif + +# +# Used to enable optimizing compiler +# +ART_USE_OPTIMIZING_COMPILER := false +ifneq ($(wildcard art/USE_OPTIMIZING_COMPILER),) +$(info Enabling ART_USE_OPTIMIZING_COMPILER because of existence of art/USE_OPTIMIZING_COMPILER) +ART_USE_OPTIMIZING_COMPILER := true +endif +ifeq ($(WITH_ART_USE_OPTIMIZING_COMPILER), true) +ART_USE_OPTIMIZING_COMPILER := true +endif + +ifeq ($(ART_USE_OPTIMIZING_COMPILER),true) +DEX2OAT_FLAGS := --compiler-backend=Optimizing +DALVIKVM_FLAGS += -Xcompiler-option --compiler-backend=Optimizing +endif + +# +# Used to change the default GC. Valid values are CMS, SS, GSS. The default is CMS. +# +ART_DEFAULT_GC_TYPE ?= CMS +ART_DEFAULT_GC_TYPE_CFLAGS := -DART_DEFAULT_GC_TYPE_IS_$(ART_DEFAULT_GC_TYPE) + +ifeq ($(ART_USE_PORTABLE_COMPILER),true) + LLVM_ROOT_PATH := external/llvm + # Don't fail a dalvik minimal host build. + -include $(LLVM_ROOT_PATH)/llvm.mk +endif + +# Clang build support. + +# Host. +ART_HOST_CLANG := false +ifneq ($(WITHOUT_HOST_CLANG),true) + # By default, host builds use clang for better warnings. + ART_HOST_CLANG := true +endif + +# Clang on the target. Target builds use GCC by default. +ART_TARGET_CLANG := +ART_TARGET_CLANG_arm := +ART_TARGET_CLANG_arm64 := +ART_TARGET_CLANG_mips := +ART_TARGET_CLANG_x86 := +ART_TARGET_CLANG_x86_64 := + +define set-target-local-clang-vars + LOCAL_CLANG := $(ART_TARGET_CLANG) + $(foreach arch,$(ART_TARGET_SUPPORTED_ARCH), + ifneq ($$(ART_TARGET_CLANG_$(arch)),) + LOCAL_CLANG_$(arch) := $$(ART_TARGET_CLANG_$(arch)) + endif) +endef + +ART_CPP_EXTENSION := .cc + +ART_C_INCLUDES := \ + external/gtest/include \ + external/valgrind/main/include \ + external/valgrind/main \ + external/vixl/src \ + external/zlib \ + frameworks/compile/mclinker/include + +art_cflags := \ + -fno-rtti \ + -std=gnu++11 \ + -ggdb3 \ + -Wall \ + -Werror \ + -Wextra \ + -Wno-sign-promo \ + -Wno-unused-parameter \ + -Wstrict-aliasing \ + -fstrict-aliasing + +ART_TARGET_CLANG_CFLAGS := +ART_TARGET_CLANG_CFLAGS_arm := +ART_TARGET_CLANG_CFLAGS_arm64 := +ART_TARGET_CLANG_CFLAGS_mips := +ART_TARGET_CLANG_CFLAGS_x86 := +ART_TARGET_CLANG_CFLAGS_x86_64 := + +# these are necessary for Clang ARM64 ART builds +ART_TARGET_CLANG_CFLAGS_arm64 += \ + -Wno-implicit-exception-spec-mismatch \ + -DNVALGRIND \ + -Wno-unused-value + +ifeq ($(ART_SMALL_MODE),true) + art_cflags += -DART_SMALL_MODE=1 +endif + +ifeq ($(ART_SEA_IR_MODE),true) + art_cflags += -DART_SEA_IR_MODE=1 +endif + +art_non_debug_cflags := \ + -O3 + +ifeq ($(HOST_OS),linux) + art_non_debug_cflags += -Wframe-larger-than=1728 +endif + +# FIXME: upstream LLVM has a vectorizer bug that needs to be fixed +ART_TARGET_CLANG_CFLAGS_arm64 += \ + -fno-vectorize + +art_debug_cflags := \ + -O1 \ + -DDYNAMIC_ANNOTATIONS_ENABLED=1 \ + -UNDEBUG + +ifndef LIBART_IMG_HOST_BASE_ADDRESS + $(error LIBART_IMG_HOST_BASE_ADDRESS unset) +endif +ART_HOST_CFLAGS := $(art_cflags) -DANDROID_SMP=1 -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS) +ART_HOST_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=default +ART_HOST_CFLAGS += $(ART_DEFAULT_GC_TYPE_CFLAGS) + +ifndef LIBART_IMG_TARGET_BASE_ADDRESS + $(error LIBART_IMG_TARGET_BASE_ADDRESS unset) +endif +ART_TARGET_CFLAGS := $(art_cflags) -DART_TARGET -DART_BASE_ADDRESS=$(LIBART_IMG_TARGET_BASE_ADDRESS) +ifeq ($(TARGET_CPU_SMP),true) + ART_TARGET_CFLAGS += -DANDROID_SMP=1 +else + ifeq ($(TARGET_CPU_SMP),false) + ART_TARGET_CFLAGS += -DANDROID_SMP=0 + else + $(warning TARGET_CPU_SMP should be (true|false), found $(TARGET_CPU_SMP)) + # Make sure we emit barriers for the worst case. + ART_TARGET_CFLAGS += -DANDROID_SMP=1 + endif +endif +ART_TARGET_CFLAGS += $(ART_DEFAULT_GC_TYPE_CFLAGS) + +# DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES is set in ../build/core/dex_preopt.mk based on +# the TARGET_CPU_VARIANT +ifeq ($(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES),) +$(error Required DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES is not set) +endif +ART_TARGET_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) + +# Enable thread-safety for GCC 4.6, and clang, but not for GCC 4.7 or later where this feature was +# removed. Warn when -Wthread-safety is not used. +ifneq ($(filter 4.6 4.6.%, $(TARGET_GCC_VERSION)),) + ART_TARGET_CFLAGS += -Wthread-safety +else + # FIXME: add -Wthread-safety when the problem is fixed + ifeq ($(ART_TARGET_CLANG),true) + ART_TARGET_CFLAGS += + else + # Warn if -Wthread-safety is not supported and not doing a top-level or 'mma' build. + ifneq ($(ONE_SHOT_MAKEFILE),) + # Enable target GCC 4.6 with: export TARGET_GCC_VERSION_EXP=4.6 + $(info Using target GCC $(TARGET_GCC_VERSION) disables thread-safety checks.) + endif + endif +endif +# We compile with GCC 4.6 or clang on the host, both of which support -Wthread-safety. +ART_HOST_CFLAGS += -Wthread-safety + +# To use oprofile_android --callgraph, uncomment this and recompile with "mmm art -B -j16" +# ART_TARGET_CFLAGS += -fno-omit-frame-pointer -marm -mapcs + +# Addition CPU specific CFLAGS. +ifeq ($(TARGET_ARCH),arm) + ifneq ($(filter cortex-a15, $(TARGET_CPU_VARIANT)),) + # Fake a ARM feature for LPAE support. + ART_TARGET_CFLAGS += -D__ARM_FEATURE_LPAE=1 + endif +endif + +ART_HOST_NON_DEBUG_CFLAGS := $(art_non_debug_cflags) +ART_TARGET_NON_DEBUG_CFLAGS := $(art_non_debug_cflags) + +# TODO: move -fkeep-inline-functions to art_debug_cflags when target gcc > 4.4 (and -lsupc++) +ART_HOST_DEBUG_CFLAGS := $(art_debug_cflags) -fkeep-inline-functions +ART_HOST_DEBUG_LDLIBS := -lsupc++ + +ifneq ($(HOST_OS),linux) + # Some Mac OS pthread header files are broken with -fkeep-inline-functions. + ART_HOST_DEBUG_CFLAGS := $(filter-out -fkeep-inline-functions,$(ART_HOST_DEBUG_CFLAGS)) + # Mac OS doesn't have libsupc++. + ART_HOST_DEBUG_LDLIBS := $(filter-out -lsupc++,$(ART_HOST_DEBUG_LDLIBS)) +endif + +ART_TARGET_DEBUG_CFLAGS := $(art_debug_cflags) + +# $(1): ndebug_or_debug +define set-target-local-cflags-vars + LOCAL_CFLAGS += $(ART_TARGET_CFLAGS) + LOCAL_CFLAGS_x86 += $(ART_TARGET_CFLAGS_x86) + art_target_cflags_ndebug_or_debug := $(1) + ifeq ($$(art_target_cflags_ndebug_or_debug),debug) + LOCAL_CFLAGS += $(ART_TARGET_DEBUG_CFLAGS) + else + LOCAL_CFLAGS += $(ART_TARGET_NON_DEBUG_CFLAGS) + endif + + # TODO: Also set when ART_TARGET_CLANG_$(arch)!=false and ART_TARGET_CLANG==true + $(foreach arch,$(ART_SUPPORTED_ARCH), + ifeq ($$(ART_TARGET_CLANG_$(arch)),true) + LOCAL_CFLAGS_$(arch) += $$(ART_TARGET_CLANG_CFLAGS_$(arch)) + endif) + + # Clear locally used variables. + art_target_cflags_ndebug_or_debug := +endef + +ART_BUILD_TARGET := false +ART_BUILD_HOST := false +ART_BUILD_NDEBUG := false +ART_BUILD_DEBUG := false +ifeq ($(ART_BUILD_TARGET_NDEBUG),true) + ART_BUILD_TARGET := true + ART_BUILD_NDEBUG := true +endif +ifeq ($(ART_BUILD_TARGET_DEBUG),true) + ART_BUILD_TARGET := true + ART_BUILD_DEBUG := true +endif +ifeq ($(ART_BUILD_HOST_NDEBUG),true) + ART_BUILD_HOST := true + ART_BUILD_NDEBUG := true +endif +ifeq ($(ART_BUILD_HOST_DEBUG),true) + ART_BUILD_HOST := true + ART_BUILD_DEBUG := true +endif + +# Clear locally defined variables that aren't necessary in the rest of the build system. +ART_DEFAULT_GC_TYPE := +ART_DEFAULT_GC_TYPE_CFLAGS := +art_cflags := + +endif # ANDROID_COMMON_BUILD_MK diff --git a/build/Android.common_path.mk b/build/Android.common_path.mk new file mode 100644 index 0000000000..8c0d9f29e8 --- /dev/null +++ b/build/Android.common_path.mk @@ -0,0 +1,75 @@ +# +# 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. +# + +ifndef ANDROID_COMMON_PATH_MK +ANDROID_COMMON_PATH_MK := true + +include art/build/Android.common.mk + +# Directory used for dalvik-cache on device. +ART_TARGET_DALVIK_CACHE_DIR := /data/dalvik-cache + +# Directory used for gtests on device. +ART_TARGET_NATIVETEST_DIR := /data/nativetest/art +ART_TARGET_NATIVETEST_OUT := $(TARGET_OUT_DATA_NATIVE_TESTS)/art + +# Directory used for oat tests on device. +ART_TARGET_TEST_DIR := /data/art-test +ART_TARGET_TEST_OUT := $(TARGET_OUT_DATA)/art-test + +# Directory used for temporary test files on the host. +ART_HOST_TEST_DIR := /tmp/test-art-$(shell echo $$PPID) + +# Core.oat location on the device. +TARGET_CORE_OAT := $(ART_TARGET_TEST_DIR)/$(DEX2OAT_TARGET_ARCH)/core.oat +ifdef TARGET_2ND_ARCH +2ND_TARGET_CORE_OAT := $(ART_TARGET_TEST_DIR)/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH)/core.oat +endif + +# Core.oat locations under the out directory. +HOST_CORE_OAT_OUT := $(HOST_OUT_JAVA_LIBRARIES)/$(ART_HOST_ARCH)/core.oat +ifneq ($(HOST_PREFER_32_BIT),true) +2ND_HOST_CORE_OAT_OUT := $(HOST_OUT_JAVA_LIBRARIES)/$(2ND_ART_HOST_ARCH)/core.oat +endif +TARGET_CORE_OAT_OUT := $(ART_TARGET_TEST_OUT)/$(DEX2OAT_TARGET_ARCH)/core.oat +ifdef TARGET_2ND_ARCH +2ND_TARGET_CORE_OAT_OUT := $(ART_TARGET_TEST_OUT)/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH)/core.oat +endif + +# Core.art locations under the out directory. +HOST_CORE_IMG_OUT := $(HOST_OUT_JAVA_LIBRARIES)/$(ART_HOST_ARCH)/core.art +ifneq ($(HOST_PREFER_32_BIT),true) +2ND_HOST_CORE_IMG_OUT := $(HOST_OUT_JAVA_LIBRARIES)/$(2ND_ART_HOST_ARCH)/core.art +endif +TARGET_CORE_IMG_OUT := $(ART_TARGET_TEST_OUT)/$(DEX2OAT_TARGET_ARCH)/core.art +ifdef TARGET_2ND_ARCH +2ND_TARGET_CORE_IMG_OUT := $(ART_TARGET_TEST_OUT)/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH)/core.art +endif + +# Oat location of core.art. +HOST_CORE_IMG_LOCATION := $(HOST_OUT_JAVA_LIBRARIES)/core.art +TARGET_CORE_IMG_LOCATION := $(ART_TARGET_TEST_OUT)/core.art + +# Jar files for core.art. +TARGET_CORE_JARS := core-libart conscrypt okhttp core-junit bouncycastle +HOST_CORE_JARS := $(addsuffix -hostdex,$(TARGET_CORE_JARS)) + +HOST_CORE_DEX_LOCATIONS := $(foreach jar,$(HOST_CORE_JARS), $(HOST_OUT_JAVA_LIBRARIES)/$(jar).jar) +TARGET_CORE_DEX_LOCATIONS := $(foreach jar,$(TARGET_CORE_JARS),/$(DEXPREOPT_BOOT_JAR_DIR)/$(jar).jar) + +HOST_CORE_DEX_FILES := $(foreach jar,$(HOST_CORE_JARS), $(call intermediates-dir-for,JAVA_LIBRARIES,$(jar),t,COMMON)/javalib.jar) +TARGET_CORE_DEX_FILES := $(foreach jar,$(TARGET_CORE_JARS),$(call intermediates-dir-for,JAVA_LIBRARIES,$(jar), ,COMMON)/javalib.jar) +endif # ANDROID_COMMON_PATH_MK diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk new file mode 100644 index 0000000000..542e888cfa --- /dev/null +++ b/build/Android.common_test.mk @@ -0,0 +1,120 @@ +# +# 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. +# + +ifndef ANDROID_COMMON_TEST_MK +ANDROID_COMMON_TEST_MK = true + +include art/build/Android.common_path.mk + +# List of known broken tests that we won't attempt to execute. The test name must be the full +# rule name such as test-art-host-oat-optimizing-HelloWorld64. +ART_TEST_KNOWN_BROKEN := \ + test-art-host-oat-optimizing-SignalTest64 \ + test-art-host-oat-optimizing-SignalTest32 + +# List of known failing tests that when executed won't cause test execution to not finish. +# The test name must be the full rule name such as test-art-host-oat-optimizing-HelloWorld64. +ART_TEST_KNOWN_FAILING := + +# Keep going after encountering a test failure? +ART_TEST_KEEP_GOING ?= false + +# Define the command run on test failure. $(1) is the name of the test. Executed by the shell. +define ART_TEST_FAILED + ( [ -f $(ART_HOST_TEST_DIR)/skipped/$(1) ] || \ + (mkdir -p $(ART_HOST_TEST_DIR)/failed/ && touch $(ART_HOST_TEST_DIR)/failed/$(1) && \ + echo $(ART_TEST_KNOWN_FAILING) | grep -q $(1) \ + && (echo -e "$(1) \e[91mKNOWN FAILURE\e[0m") \ + || (echo -e "$(1) \e[91mFAILED\e[0m"))) +endef + +# Define the command run on test success. $(1) is the name of the test. Executed by the shell. +# The command checks prints "PASSED" then checks to see if this was a top-level make target (e.g. +# "mm test-art-host-oat-HelloWorld32"), if it was then it does nothing, otherwise it creates a file +# to be printed in the passing test summary. +define ART_TEST_PASSED + ( echo -e "$(1) \e[92mPASSED\e[0m" && \ + (echo $(MAKECMDGOALS) | grep -q $(1) || \ + (mkdir -p $(ART_HOST_TEST_DIR)/passed/ && touch $(ART_HOST_TEST_DIR)/passed/$(1)))) +endef + +# Define the command run on test success of multiple prerequisites. $(1) is the name of the test. +# When the test is a top-level make target then a summary of the ran tests is produced. Executed by +# the shell. +define ART_TEST_PREREQ_FINISHED + (echo -e "$(1) \e[32mCOMPLETE\e[0m" && \ + (echo $(MAKECMDGOALS) | grep -q -v $(1) || \ + (([ -d $(ART_HOST_TEST_DIR)/passed/ ] \ + && (echo -e "\e[92mPASSING TESTS\e[0m" && ls -1 $(ART_HOST_TEST_DIR)/passed/) \ + || (echo -e "\e[91mNO TESTS PASSED\e[0m")) && \ + ([ -d $(ART_HOST_TEST_DIR)/skipped/ ] \ + && (echo -e "\e[93mSKIPPED TESTS\e[0m" && ls -1 $(ART_HOST_TEST_DIR)/skipped/) \ + || (echo -e "\e[92mNO TESTS SKIPPED\e[0m")) && \ + ([ -d $(ART_HOST_TEST_DIR)/failed/ ] \ + && (echo -e "\e[91mFAILING TESTS\e[0m" && ls -1 $(ART_HOST_TEST_DIR)/failed/) \ + || (echo -e "\e[92mNO TESTS FAILED\e[0m")) \ + && ([ ! -d $(ART_HOST_TEST_DIR)/failed/ ] && rm -r $(ART_HOST_TEST_DIR) \ + || (rm -r $(ART_HOST_TEST_DIR) && false))))) +endef + +# Define the command executed by the shell ahead of running an art test. $(1) is the name of the +# test. +define ART_TEST_SKIP + ((echo $(ART_TEST_KNOWN_BROKEN) | grep -q -v $(1) \ + && ([ ! -d $(ART_HOST_TEST_DIR)/failed/ ] || [ $(ART_TEST_KEEP_GOING) = true ])\ + && echo -e "$(1) \e[95mRUNNING\e[0m") \ + || ((mkdir -p $(ART_HOST_TEST_DIR)/skipped/ && touch $(ART_HOST_TEST_DIR)/skipped/$(1) \ + && ([ -d $(ART_HOST_TEST_DIR)/failed/ ] \ + && echo -e "$(1) \e[93mSKIPPING DUE TO EARLIER FAILURE\e[0m") \ + || echo -e "$(1) \e[93mSKIPPING BROKEN TEST\e[0m") && false)) +endef + +# Create a build rule to create the dex file for a test. +# $(1): module prefix, e.g. art-test-dex +# $(2): input test directory in art/test, e.g. HelloWorld +# $(3): target output module path (default module path is used on host) +# $(4): additional dependencies +# $(5): a make variable used to collate dependencies +define build-art-test-dex + ifeq ($(ART_BUILD_TARGET),true) + include $(CLEAR_VARS) + LOCAL_MODULE := $(1)-$(2) + LOCAL_SRC_FILES := $(call all-java-files-under, $(2)) + LOCAL_NO_STANDARD_LIBRARIES := true + LOCAL_DEX_PREOPT := false + LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_test.mk $(4) + LOCAL_MODULE_TAGS := tests + LOCAL_JAVA_LIBRARIES := $(TARGET_CORE_JARS) + LOCAL_MODULE_PATH := $(3) + LOCAL_DEX_PREOPT_IMAGE_LOCATION := $(TARGET_CORE_IMG_OUT) + include $(BUILD_JAVA_LIBRARY) + $(5) := $$(LOCAL_INSTALLED_MODULE) + endif + ifeq ($(ART_BUILD_HOST),true) + include $(CLEAR_VARS) + LOCAL_MODULE := $(1)-$(2) + LOCAL_SRC_FILES := $(call all-java-files-under, $(2)) + LOCAL_NO_STANDARD_LIBRARIES := true + LOCAL_DEX_PREOPT := false + LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_test.mk $(4) + LOCAL_JAVA_LIBRARIES := $(HOST_CORE_JARS) + LOCAL_DEX_PREOPT_IMAGE := $(HOST_CORE_IMG_LOCATION) + include $(BUILD_HOST_DALVIK_JAVA_LIBRARY) + $(5)-host := $$(LOCAL_INSTALLED_MODULE) + endif +endef + +endif # ANDROID_COMMON_TEST_MK diff --git a/build/Android.cpplint.mk b/build/Android.cpplint.mk index 1ecad211d5..7abf863a59 100644 --- a/build/Android.cpplint.mk +++ b/build/Android.cpplint.mk @@ -14,6 +14,8 @@ # limitations under the License. # +include art/build/Android.common_build.mk + ART_CPPLINT := art/tools/cpplint.py ART_CPPLINT_FILTER := --filter=-whitespace/line_length,-build/include,-readability/function,-readability/streams,-readability/todo,-runtime/references,-runtime/sizeof,-runtime/threadsafe_fn,-runtime/printf ART_CPPLINT_SRC := $(shell find art -name "*.h" -o -name "*$(ART_CPP_EXTENSION)" | grep -v art/compiler/llvm/generated/) diff --git a/build/Android.executable.mk b/build/Android.executable.mk index 49e73841e4..d887acd1cd 100644 --- a/build/Android.executable.mk +++ b/build/Android.executable.mk @@ -14,7 +14,7 @@ # limitations under the License. # -include art/build/Android.common.mk +include art/build/Android.common_build.mk ART_HOST_EXECUTABLES ?= ART_TARGET_EXECUTABLES ?= @@ -85,13 +85,13 @@ define build-art-executable LOCAL_SHARED_LIBRARIES += libartd endif - LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk + LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.executable.mk ifeq ($$(art_target_or_host),target) LOCAL_MODULE_TARGET_ARCH := $(ART_SUPPORTED_ARCH) - LOCAL_MULTILIB := $$(art_multilib) endif + LOCAL_MULTILIB := $$(art_multilib) include external/libcxx/libcxx.mk ifeq ($$(art_target_or_host),target) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 4c2cda47f8..45bdbcb333 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -14,147 +14,254 @@ # limitations under the License. # +# The path for which all the dex files are relative, not actually the current directory. +LOCAL_PATH := art/test + +include art/build/Android.common_test.mk +include art/build/Android.common_path.mk + +# Subdirectories in art/test which contain dex files used as inputs for gtests. +GTEST_DEX_DIRECTORIES := \ + AbstractMethod \ + AllFields \ + ExceptionHandle \ + GetMethodSignature \ + Interfaces \ + Main \ + MyClass \ + MyClassNatives \ + Nested \ + NonStaticLeafMethods \ + ProtoCompare \ + ProtoCompare2 \ + StaticLeafMethods \ + Statics \ + StaticsFromCode \ + Transaction \ + XandY + +# Create build rules for each dex file recording the dependency. +$(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval $(call build-art-test-dex,art-gtest,$(dir), \ + $(ART_TARGET_NATIVETEST_OUT),art/build/Android.gtest.mk,ART_GTEST_$(dir)_DEX))) + +# Dex file dependencies for each gtest. +ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MyClass Nested Statics StaticsFromCode +ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod +ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature +ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle +ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives +ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods +ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY +ART_GTEST_proxy_test_DEX_DEPS := Interfaces +ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods +ART_GTEST_stub_test_DEX_DEPS := AllFields +ART_GTEST_transaction_test_DEX_DEPS := Transaction + +# The elf writer test has dependencies on core.oat. +ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_OAT_OUT) $(2ND_HOST_CORE_OAT_OUT) +ART_GTEST_elf_writer_test_TARGET_DEPS := $(TARGET_CORE_OAT_OUT) $(2ND_TARGET_CORE_OAT_OUT) + +# The path for which all the source files are relative, not actually the current directory. LOCAL_PATH := art RUNTIME_GTEST_COMMON_SRC_FILES := \ - runtime/arch/arch_test.cc \ - runtime/arch/stub_test.cc \ - runtime/barrier_test.cc \ - runtime/base/bit_field_test.cc \ - runtime/base/bit_vector_test.cc \ - runtime/base/hex_dump_test.cc \ - runtime/base/histogram_test.cc \ - runtime/base/mutex_test.cc \ - runtime/base/scoped_flock_test.cc \ - runtime/base/timing_logger_test.cc \ - runtime/base/unix_file/fd_file_test.cc \ - runtime/base/unix_file/mapped_file_test.cc \ - runtime/base/unix_file/null_file_test.cc \ - runtime/base/unix_file/random_access_file_utils_test.cc \ - runtime/base/unix_file/string_file_test.cc \ - runtime/class_linker_test.cc \ - runtime/dex_file_test.cc \ - runtime/dex_file_verifier_test.cc \ - runtime/dex_instruction_visitor_test.cc \ - runtime/dex_method_iterator_test.cc \ - runtime/entrypoints/math_entrypoints_test.cc \ - runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc \ - runtime/entrypoints_order_test.cc \ - runtime/exception_test.cc \ - runtime/gc/accounting/space_bitmap_test.cc \ - runtime/gc/heap_test.cc \ - runtime/gc/space/dlmalloc_space_base_test.cc \ - runtime/gc/space/dlmalloc_space_static_test.cc \ - runtime/gc/space/dlmalloc_space_random_test.cc \ - runtime/gc/space/rosalloc_space_base_test.cc \ - runtime/gc/space/rosalloc_space_static_test.cc \ - runtime/gc/space/rosalloc_space_random_test.cc \ - runtime/gc/space/large_object_space_test.cc \ - runtime/gtest_test.cc \ - runtime/handle_scope_test.cc \ - runtime/indenter_test.cc \ - runtime/indirect_reference_table_test.cc \ - runtime/instruction_set_test.cc \ - runtime/intern_table_test.cc \ - runtime/leb128_test.cc \ - runtime/mem_map_test.cc \ - runtime/mirror/dex_cache_test.cc \ - runtime/mirror/object_test.cc \ - runtime/parsed_options_test.cc \ - runtime/reference_table_test.cc \ - runtime/thread_pool_test.cc \ - runtime/transaction_test.cc \ - runtime/utils_test.cc \ - runtime/verifier/method_verifier_test.cc \ - runtime/verifier/reg_type_test.cc \ - runtime/zip_archive_test.cc + runtime/arch/arch_test.cc \ + runtime/arch/stub_test.cc \ + runtime/barrier_test.cc \ + runtime/base/bit_field_test.cc \ + runtime/base/bit_vector_test.cc \ + runtime/base/hex_dump_test.cc \ + runtime/base/histogram_test.cc \ + runtime/base/mutex_test.cc \ + runtime/base/scoped_flock_test.cc \ + runtime/base/timing_logger_test.cc \ + runtime/base/unix_file/fd_file_test.cc \ + runtime/base/unix_file/mapped_file_test.cc \ + runtime/base/unix_file/null_file_test.cc \ + runtime/base/unix_file/random_access_file_utils_test.cc \ + runtime/base/unix_file/string_file_test.cc \ + runtime/class_linker_test.cc \ + runtime/dex_file_test.cc \ + runtime/dex_file_verifier_test.cc \ + runtime/dex_instruction_visitor_test.cc \ + runtime/dex_method_iterator_test.cc \ + runtime/entrypoints/math_entrypoints_test.cc \ + runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc \ + runtime/entrypoints_order_test.cc \ + runtime/exception_test.cc \ + runtime/gc/accounting/space_bitmap_test.cc \ + runtime/gc/heap_test.cc \ + runtime/gc/space/dlmalloc_space_base_test.cc \ + runtime/gc/space/dlmalloc_space_static_test.cc \ + runtime/gc/space/dlmalloc_space_random_test.cc \ + runtime/gc/space/rosalloc_space_base_test.cc \ + runtime/gc/space/rosalloc_space_static_test.cc \ + runtime/gc/space/rosalloc_space_random_test.cc \ + runtime/gc/space/large_object_space_test.cc \ + runtime/gtest_test.cc \ + runtime/handle_scope_test.cc \ + runtime/indenter_test.cc \ + runtime/indirect_reference_table_test.cc \ + runtime/instruction_set_test.cc \ + runtime/intern_table_test.cc \ + runtime/leb128_test.cc \ + runtime/mem_map_test.cc \ + runtime/mirror/dex_cache_test.cc \ + runtime/mirror/object_test.cc \ + runtime/parsed_options_test.cc \ + runtime/reference_table_test.cc \ + runtime/thread_pool_test.cc \ + runtime/transaction_test.cc \ + runtime/utils_test.cc \ + runtime/verifier/method_verifier_test.cc \ + runtime/verifier/reg_type_test.cc \ + runtime/zip_archive_test.cc COMPILER_GTEST_COMMON_SRC_FILES := \ - runtime/jni_internal_test.cc \ - runtime/proxy_test.cc \ - runtime/reflection_test.cc \ - compiler/dex/local_value_numbering_test.cc \ - compiler/dex/mir_optimization_test.cc \ - compiler/driver/compiler_driver_test.cc \ - compiler/elf_writer_test.cc \ - compiler/image_test.cc \ - compiler/jni/jni_compiler_test.cc \ - compiler/oat_test.cc \ - compiler/optimizing/codegen_test.cc \ - compiler/optimizing/dominator_test.cc \ - compiler/optimizing/find_loops_test.cc \ - compiler/optimizing/graph_test.cc \ - compiler/optimizing/linearize_test.cc \ - compiler/optimizing/liveness_test.cc \ - compiler/optimizing/live_interval_test.cc \ - compiler/optimizing/live_ranges_test.cc \ - compiler/optimizing/parallel_move_test.cc \ - compiler/optimizing/pretty_printer_test.cc \ - compiler/optimizing/register_allocator_test.cc \ - compiler/optimizing/ssa_test.cc \ - compiler/output_stream_test.cc \ - compiler/utils/arena_allocator_test.cc \ - compiler/utils/dedupe_set_test.cc \ - compiler/utils/arm/managed_register_arm_test.cc \ - compiler/utils/arm64/managed_register_arm64_test.cc \ - compiler/utils/x86/managed_register_x86_test.cc \ + runtime/jni_internal_test.cc \ + runtime/proxy_test.cc \ + runtime/reflection_test.cc \ + compiler/dex/local_value_numbering_test.cc \ + compiler/dex/mir_optimization_test.cc \ + compiler/driver/compiler_driver_test.cc \ + compiler/elf_writer_test.cc \ + compiler/image_test.cc \ + compiler/jni/jni_compiler_test.cc \ + compiler/oat_test.cc \ + compiler/optimizing/codegen_test.cc \ + compiler/optimizing/dominator_test.cc \ + compiler/optimizing/find_loops_test.cc \ + compiler/optimizing/graph_test.cc \ + compiler/optimizing/linearize_test.cc \ + compiler/optimizing/liveness_test.cc \ + compiler/optimizing/live_interval_test.cc \ + compiler/optimizing/live_ranges_test.cc \ + compiler/optimizing/parallel_move_test.cc \ + compiler/optimizing/pretty_printer_test.cc \ + compiler/optimizing/register_allocator_test.cc \ + compiler/optimizing/ssa_test.cc \ + compiler/output_stream_test.cc \ + compiler/utils/arena_allocator_test.cc \ + compiler/utils/dedupe_set_test.cc \ + compiler/utils/arm/managed_register_arm_test.cc \ + compiler/utils/arm64/managed_register_arm64_test.cc \ + compiler/utils/x86/managed_register_x86_test.cc \ ifeq ($(ART_SEA_IR_MODE),true) COMPILER_GTEST_COMMON_SRC_FILES += \ - compiler/utils/scoped_hashtable_test.cc \ - compiler/sea_ir/types/type_data_test.cc \ - compiler/sea_ir/types/type_inference_visitor_test.cc \ - compiler/sea_ir/ir/regions_test.cc + compiler/utils/scoped_hashtable_test.cc \ + compiler/sea_ir/types/type_data_test.cc \ + compiler/sea_ir/types/type_inference_visitor_test.cc \ + compiler/sea_ir/ir/regions_test.cc endif RUNTIME_GTEST_TARGET_SRC_FILES := \ - $(RUNTIME_GTEST_COMMON_SRC_FILES) + $(RUNTIME_GTEST_COMMON_SRC_FILES) RUNTIME_GTEST_HOST_SRC_FILES := \ - $(RUNTIME_GTEST_COMMON_SRC_FILES) + $(RUNTIME_GTEST_COMMON_SRC_FILES) COMPILER_GTEST_TARGET_SRC_FILES := \ - $(COMPILER_GTEST_COMMON_SRC_FILES) + $(COMPILER_GTEST_COMMON_SRC_FILES) COMPILER_GTEST_HOST_SRC_FILES := \ - $(COMPILER_GTEST_COMMON_SRC_FILES) \ - compiler/utils/x86/assembler_x86_test.cc \ - compiler/utils/x86_64/assembler_x86_64_test.cc - -ART_HOST_GTEST_EXECUTABLES := -ART_TARGET_GTEST_EXECUTABLES$(ART_PHONY_TEST_TARGET_SUFFIX) := -ART_TARGET_GTEST_EXECUTABLES$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) := -ART_HOST_GTEST_TARGETS := -ART_HOST_VALGRIND_GTEST_TARGETS := -ART_TARGET_GTEST_TARGETS$(ART_PHONY_TEST_TARGET_SUFFIX) := -ART_TARGET_GTEST_TARGETS$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) := + $(COMPILER_GTEST_COMMON_SRC_FILES) \ + compiler/utils/x86/assembler_x86_test.cc \ + compiler/utils/x86_64/assembler_x86_64_test.cc ART_TEST_CFLAGS := ifeq ($(ART_USE_PORTABLE_COMPILER),true) ART_TEST_CFLAGS += -DART_USE_PORTABLE_COMPILER=1 endif -# Build a make target for a target test. -# (1) Prefix for variables -define build-art-test-make-target -.PHONY: $$(art_gtest_target)$($(1)ART_PHONY_TEST_TARGET_SUFFIX) -$$(art_gtest_target)$($(1)ART_PHONY_TEST_TARGET_SUFFIX): $(ART_NATIVETEST_OUT)/$(TARGET_$(1)ARCH)/$$(LOCAL_MODULE) test-art-target-sync - adb shell touch $(ART_TEST_DIR)/$(TARGET_$(1)ARCH)/$$@ - adb shell rm $(ART_TEST_DIR)/$(TARGET_$(1)ARCH)/$$@ - adb shell chmod 755 $(ART_NATIVETEST_DIR)/$(TARGET_$(1)ARCH)/$$(notdir $$<) - adb shell sh -c "$(ART_NATIVETEST_DIR)/$(TARGET_$(1)ARCH)/$$(notdir $$<) && touch $(ART_TEST_DIR)/$(TARGET_$(1)ARCH)/$$@" - $(hide) (adb pull $(ART_TEST_DIR)/$(TARGET_$(1)ARCH)/$$@ /tmp/ && echo $$@ PASSED) || (echo $$@ FAILED && exit 1) - $(hide) rm /tmp/$$@ +# Variables holding collections of gtest pre-requisits used to run a number of gtests. +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 := + +# 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): 2ND_ or undefined - used to differentiate between the primary and secondary architecture. +define define-art-gtest-rule-target + gtest_rule := test-art-target-gtest-$(1)$$($(2)ART_PHONY_TEST_TARGET_SUFFIX) + + # Add the test dependencies to test-art-target-sync, which will be a prerequisite for the test + # to ensure files are pushed to the device. + TEST_ART_TARGET_SYNC_DEPS += \ + $$(ART_GTEST_$(1)_TARGET_DEPS) \ + $(foreach file,$(ART_GTEST_$(1)_DEX_DEPS),$(ART_GTEST_$(file)_DEX)) \ + $$(ART_TARGET_NATIVETEST_OUT)/$$(TARGET_$(2)ARCH)/$(1) \ + $$($(2)TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so + +.PHONY: $$(gtest_rule) +$$(gtest_rule): test-art-target-sync + $(hide) adb shell touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID + $(hide) adb shell rm $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID + $(hide) adb shell chmod 755 $(ART_TARGET_NATIVETEST_DIR)/$(TARGET_$(2)ARCH)/$(1) + $(hide) $$(call ART_TEST_SKIP,$$@) && \ + (adb shell sh -c "$(ART_TARGET_NATIVETEST_DIR)/$(TARGET_$(2)ARCH)/$(1) && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID" \ + && (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID /tmp/ \ + && $$(call ART_TEST_PASSED,$$@)) \ + || $$(call ART_TEST_FAILED,$$@)) + $(hide) rm /tmp/$$@-$$$$PPID + + ART_TEST_TARGET_GTEST$($(2)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += $$(gtest_rule) + ART_TEST_TARGET_GTEST_RULES += $$(gtest_rule) + ART_TEST_TARGET_GTEST_$(1)_RULES += $$(gtest_rule) + + # Clear locally defined variables. + gtest_rule := +endef # define-art-gtest-rule-target - ART_TARGET_GTEST_TARGETS$($(1)ART_PHONY_TEST_TARGET_SUFFIX) += $$(art_gtest_target)$($(1)ART_PHONY_TEST_TARGET_SUFFIX) -endef +# Define make rules for a host gtests. +# $(1): gtest name - the name of the test we're building such as leb128_test. +# $(2): 2ND_ or undefined - used to differentiate between the primary and secondary architecture. +define define-art-gtest-rule-host + gtest_rule := test-art-host-gtest-$(1)$$($(2)ART_PHONY_TEST_HOST_SUFFIX) + gtest_exe := $$(HOST_OUT_EXECUTABLES)/$(1)$$($(2)ART_PHONY_TEST_HOST_SUFFIX) + # Dependencies for all host gtests. + gtest_deps := $$(HOST_CORE_DEX_LOCATIONS) \ + $$($(2)ART_HOST_LIBRARY_PATH)/libjavacore$$(ART_HOST_SHLIB_EXTENSION) +.PHONY: $$(gtest_rule) +$$(gtest_rule): $$(gtest_exe) $$(ART_GTEST_$(1)_HOST_DEPS) $(foreach file,$(ART_GTEST_$(1)_DEX_DEPS),$(ART_GTEST_$(file)_DEX-host)) $$(gtest_deps) + $(hide) ($$(call ART_TEST_SKIP,$$@) && $$< && $$(call ART_TEST_PASSED,$$@)) \ + || $$(call ART_TEST_FAILED,$$@) + + ART_TEST_HOST_GTEST$$($(2)ART_PHONY_TEST_HOST_SUFFIX)_RULES += $$(gtest_rule) + ART_TEST_HOST_GTEST_RULES += $$(gtest_rule) + ART_TEST_HOST_GTEST_$(1)_RULES += $$(gtest_rule) + +.PHONY: valgrind-$$(gtest_rule) +valgrind-$$(gtest_rule): $$(gtest_exe) $$(ART_GTEST_$(1)_HOST_DEPS) $(foreach file,$(ART_GTEST_$(1)_DEX_DEPS),$(ART_GTEST_$(file)_DEX-host)) $$(gtest_deps) + $(hide) $$(call ART_TEST_SKIP,$$@) && \ + valgrind --leak-check=full --error-exitcode=1 $$< && $$(call ART_TEST_PASSED,$$@) \ + || $$(call ART_TEST_FAILED,$$@) + + ART_TEST_HOST_VALGRIND_GTEST$$($(2)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 := +endef # define-art-gtest-rule-host + +# Define the rules to build and run host and target gtests. # $(1): target or host # $(2): file name # $(3): extra C includes # $(4): extra shared libraries -define build-art-test +define define-art-gtest ifneq ($(1),target) ifneq ($(1),host) $$(error expected target or host for argument 1, received $(1)) @@ -166,94 +273,103 @@ define build-art-test art_gtest_extra_c_includes := $(3) art_gtest_extra_shared_libraries := $(4) + include $$(CLEAR_VARS) art_gtest_name := $$(notdir $$(basename $$(art_gtest_filename))) - - include $(CLEAR_VARS) - LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) LOCAL_MODULE := $$(art_gtest_name) ifeq ($$(art_target_or_host),target) LOCAL_MODULE_TAGS := tests endif + LOCAL_CPP_EXTENSION := $$(ART_CPP_EXTENSION) LOCAL_SRC_FILES := $$(art_gtest_filename) runtime/common_runtime_test.cc - LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime $(3) - LOCAL_SHARED_LIBRARIES += libartd $(4) - # dex2oatd is needed to go with libartd - LOCAL_REQUIRED_MODULES := dex2oatd + LOCAL_C_INCLUDES += $$(ART_C_INCLUDES) art/runtime $$(art_gtest_extra_c_includes) + LOCAL_SHARED_LIBRARIES += libartd $$(art_gtest_extra_shared_libraries) - LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk - LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.gtest.mk + # LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk + # LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.gtest.mk # Mac OS linker doesn't understand --export-dynamic. - ifneq ($(HOST_OS)-$$(art_target_or_host),darwin-host) + ifneq ($$(HOST_OS)-$$(art_target_or_host),darwin-host) # Allow jni_compiler_test to find Java_MyClassNatives_bar within itself using dlopen(NULL, ...). LOCAL_LDFLAGS := -Wl,--export-dynamic -Wl,-u,Java_MyClassNatives_bar -Wl,-u,Java_MyClassNatives_sbar endif - LOCAL_CFLAGS := $(ART_TEST_CFLAGS) + LOCAL_CFLAGS := $$(ART_TEST_CFLAGS) include external/libcxx/libcxx.mk ifeq ($$(art_target_or_host),target) - $(call set-target-local-clang-vars) - $(call set-target-local-cflags-vars,debug) + $$(eval $$(call set-target-local-clang-vars)) + $$(eval $$(call set-target-local-cflags-vars,debug)) LOCAL_SHARED_LIBRARIES += libdl libicuuc libicui18n libnativehelper libz libcutils libvixl LOCAL_STATIC_LIBRARIES += libgtest_libc++ - LOCAL_MODULE_PATH_32 := $(ART_NATIVETEST_OUT)/$(ART_TARGET_ARCH_32) - LOCAL_MODULE_PATH_64 := $(ART_NATIVETEST_OUT)/$(ART_TARGET_ARCH_64) + LOCAL_MODULE_PATH_32 := $$(ART_TARGET_NATIVETEST_OUT)/$$(ART_TARGET_ARCH_32) + LOCAL_MODULE_PATH_64 := $$(ART_TARGET_NATIVETEST_OUT)/$$(ART_TARGET_ARCH_64) LOCAL_MULTILIB := both - include $(BUILD_EXECUTABLE) - - ART_TARGET_GTEST_EXECUTABLES$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_NATIVETEST_OUT)/$(TARGET_ARCH)/$$(LOCAL_MODULE) - art_gtest_target := test-art-$$(art_target_or_host)-gtest-$$(art_gtest_name) + include $$(BUILD_EXECUTABLE) + ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES := ifdef TARGET_2ND_ARCH - $(call build-art-test-make-target,2ND_) - - ART_TARGET_GTEST_EXECUTABLES$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_NATIVETEST_OUT)/$(TARGET_2ND_ARCH)/$$(LOCAL_MODULE) - - # Bind the primary to the non-suffix rule - ifneq ($(ART_PHONY_TEST_TARGET_SUFFIX),) -$$(art_gtest_target): $$(art_gtest_target)$(ART_PHONY_TEST_TARGET_SUFFIX) - endif + $$(eval $$(call define-art-gtest-rule-target,$$(art_gtest_name),2ND_)) endif - $(call build-art-test-make-target,) + $$(eval $$(call define-art-gtest-rule-target,$$(art_gtest_name),)) + + # A rule to run the different architecture versions of the gtest. +.PHONY: test-art-target-gtest-$$(art_gtest_name) +test-art-target-gtest-$$(art_gtest_name): $$(ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES) + $$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) + # Clear locally defined variables. + ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES := else # host - LOCAL_CLANG := $(ART_HOST_CLANG) - LOCAL_CFLAGS += $(ART_HOST_CFLAGS) $(ART_HOST_DEBUG_CFLAGS) + LOCAL_CLANG := $$(ART_HOST_CLANG) + LOCAL_CFLAGS += $$(ART_HOST_CFLAGS) $$(ART_HOST_DEBUG_CFLAGS) LOCAL_SHARED_LIBRARIES += libicuuc-host libicui18n-host libnativehelper libz-host LOCAL_STATIC_LIBRARIES += libcutils libvixl - ifneq ($(WITHOUT_HOST_CLANG),true) - # GCC host compiled tests fail with this linked, presumably due to destructors that run. - LOCAL_STATIC_LIBRARIES += libgtest_libc++_host + ifneq ($$(WITHOUT_HOST_CLANG),true) + # GCC host compiled tests fail with this linked, presumably due to destructors that run. + LOCAL_STATIC_LIBRARIES += libgtest_libc++_host endif LOCAL_LDLIBS += -lpthread -ldl LOCAL_IS_HOST_MODULE := true - include $(BUILD_HOST_EXECUTABLE) - art_gtest_exe := $(HOST_OUT_EXECUTABLES)/$$(LOCAL_MODULE) - ART_HOST_GTEST_EXECUTABLES += $$(art_gtest_exe) - art_gtest_target := test-art-$$(art_target_or_host)-gtest-$$(art_gtest_name) -.PHONY: $$(art_gtest_target) -$$(art_gtest_target): $$(art_gtest_exe) test-art-host-dependencies - $$< - @echo $$@ PASSED - - ART_HOST_GTEST_TARGETS += $$(art_gtest_target) - -.PHONY: valgrind-$$(art_gtest_target) -valgrind-$$(art_gtest_target): $$(art_gtest_exe) test-art-host-dependencies - valgrind --leak-check=full --error-exitcode=1 $$< - @echo $$@ PASSED - - ART_HOST_VALGRIND_GTEST_TARGETS += valgrind-$$(art_gtest_target) - endif -endef + LOCAL_MULTILIB := both + LOCAL_MODULE_STEM_32 := $$(art_gtest_name)32 + LOCAL_MODULE_STEM_64 := $$(art_gtest_name)64 + include $$(BUILD_HOST_EXECUTABLE) + + ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES := + ART_TEST_HOST_VALGRIND_GTEST_$$(art_gtest_name)_RULES := + ifneq ($$(HOST_PREFER_32_BIT),true) + $$(eval $$(call define-art-gtest-rule-host,$$(art_gtest_name),2ND_)) + endif + $$(eval $$(call define-art-gtest-rule-host,$$(art_gtest_name),)) + + # Rules to run the different architecture versions of the gtest. +.PHONY: test-art-host-gtest-$$(art_gtest_name) +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 locally defined variables. + ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES := + ART_TEST_HOST_VALGRIND_GTEST_$$(art_gtest_name)_RULES := + endif # host_or_target + + # Clear locally defined variables. + art_target_or_host := + art_gtest_filename := + art_gtest_extra_c_includes := + art_gtest_extra_shared_libraries := + art_gtest_name := +endef # define-art-gtest ifeq ($(ART_BUILD_TARGET),true) - $(foreach file,$(RUNTIME_GTEST_TARGET_SRC_FILES), $(eval $(call build-art-test,target,$(file),,))) - $(foreach file,$(COMPILER_GTEST_TARGET_SRC_FILES), $(eval $(call build-art-test,target,$(file),art/compiler,libartd-compiler))) + $(foreach file,$(RUNTIME_GTEST_TARGET_SRC_FILES), $(eval $(call define-art-gtest,target,$(file),,))) + $(foreach file,$(COMPILER_GTEST_TARGET_SRC_FILES), $(eval $(call define-art-gtest,target,$(file),art/compiler,libartd-compiler))) endif ifeq ($(ART_BUILD_HOST),true) - $(foreach file,$(RUNTIME_GTEST_HOST_SRC_FILES), $(eval $(call build-art-test,host,$(file),,))) - $(foreach file,$(COMPILER_GTEST_HOST_SRC_FILES), $(eval $(call build-art-test,host,$(file),art/compiler,libartd-compiler))) + $(foreach file,$(RUNTIME_GTEST_HOST_SRC_FILES), $(eval $(call define-art-gtest,host,$(file),,))) + $(foreach file,$(COMPILER_GTEST_HOST_SRC_FILES), $(eval $(call define-art-gtest,host,$(file),art/compiler,libartd-compiler))) endif # Used outside the art project to get a list of the current tests @@ -261,3 +377,87 @@ RUNTIME_TARGET_GTEST_MAKE_TARGETS := $(foreach file, $(RUNTIME_GTEST_TARGET_SRC_FILES), $(eval RUNTIME_TARGET_GTEST_MAKE_TARGETS += $$(notdir $$(basename $$(file))))) COMPILER_TARGET_GTEST_MAKE_TARGETS := $(foreach file, $(COMPILER_GTEST_TARGET_SRC_FILES), $(eval COMPILER_TARGET_GTEST_MAKE_TARGETS += $$(notdir $$(basename $$(file))))) + +# Define all the combinations of host/target, valgrind and suffix such as: +# test-art-host-gtest or valgrind-test-art-host-gtest64 +# $(1): host or target +# $(2): HOST or TARGET +# $(3): valgrind- or undefined +# $(4): undefined, 32 or 64 +define define-test-art-gtest-combination + ifeq ($(1),host) + ifneq ($(2),HOST) + $$(error argument mismatch $(1) and ($2)) + endif + else + ifneq ($(1),target) + $$(error found $(1) expected host or target) + endif + ifneq ($(2),TARGET) + $$(error argument mismatch $(1) and ($2)) + endif + endif + + rule_name := $(3)test-art-$(1)-gtest$(4) + dependencies := $$(ART_TEST_$(2)_GTEST$(4)_RULES) + +.PHONY: $$(rule_name) +$$(rule_name): $$(dependencies) + $(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) + + # Clear locally defined variables. + rule_name := + 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,,$(ART_PHONY_TEST_TARGET_SUFFIX))) +ifdef TARGET_2ND_ARCH +$(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))) +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))) +endif + +# Clear locally defined variables. +define-art-gtest-rule-target := +define-art-gtest-rule-host := +define-art-gtest := +define-test-art-gtest-combination := +RUNTIME_GTEST_COMMON_SRC_FILES := +COMPILER_GTEST_COMMON_SRC_FILES := +RUNTIME_GTEST_TARGET_SRC_FILES := +RUNTIME_GTEST_HOST_SRC_FILES := +COMPILER_GTEST_TARGET_SRC_FILES := +COMPILER_GTEST_HOST_SRC_FILES := +ART_TEST_CFLAGS := +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_GTEST_class_linker_test_DEX_DEPS := +ART_GTEST_compiler_driver_test_DEX_DEPS := +ART_GTEST_dex_file_test_DEX_DEPS := +ART_GTEST_exception_test_DEX_DEPS := +ART_GTEST_elf_writer_test_HOST_DEPS := +ART_GTEST_elf_writer_test_TARGET_DEPS := +ART_GTEST_jni_compiler_test_DEX_DEPS := +ART_GTEST_jni_internal_test_DEX_DEPS := +ART_GTEST_object_test_DEX_DEPS := +ART_GTEST_proxy_test_DEX_DEPS := +ART_GTEST_reflection_test_DEX_DEPS := +ART_GTEST_stub_test_DEX_DEPS := +ART_GTEST_transaction_test_DEX_DEPS := +$(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_GTEST_TEST_$(dir)_DEX :=)) +GTEST_DEX_DIRECTORIES := +LOCAL_PATH := diff --git a/build/Android.oat.mk b/build/Android.oat.mk index fbb7eb36c6..916fd58046 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -20,47 +20,62 @@ # # The main rules to build the default "boot" image are in # build/core/dex_preopt_libart.mk -TARGET_CORE_JARS := core-libart conscrypt okhttp core-junit bouncycastle -HOST_CORE_JARS := $(addsuffix -hostdex,$(TARGET_CORE_JARS)) -HOST_CORE_DEX_LOCATIONS := $(foreach jar,$(HOST_CORE_JARS), $(HOST_OUT_JAVA_LIBRARIES)/$(jar).jar) -TARGET_CORE_DEX_LOCATIONS := $(foreach jar,$(TARGET_CORE_JARS),/$(DEXPREOPT_BOOT_JAR_DIR)/$(jar).jar) +include art/build/Android.common_path.mk -HOST_CORE_DEX_FILES := $(foreach jar,$(HOST_CORE_JARS), $(call intermediates-dir-for,JAVA_LIBRARIES,$(jar),t,COMMON)/javalib.jar) -TARGET_CORE_DEX_FILES := $(foreach jar,$(TARGET_CORE_JARS),$(call intermediates-dir-for,JAVA_LIBRARIES,$(jar), ,COMMON)/javalib.jar) +# Use dex2oat debug version for better error reporting +# $(1): 2ND_ or undefined, 2ND_ for 32-bit host builds. +define create-core-oat-host-rules +$$($(1)HOST_CORE_IMG_OUT): $$(HOST_CORE_DEX_FILES) $$(DEX2OATD_DEPENDENCY) + @echo "host dex2oat: $$@ ($$?)" + @mkdir -p $$(dir $$@) + $$(hide) $$(DEX2OATD) --runtime-arg -Xms16m --runtime-arg -Xmx16m \ + --image-classes=$$(PRELOADED_CLASSES) $$(addprefix --dex-file=,$$(HOST_CORE_DEX_FILES)) \ + $$(addprefix --dex-location=,$$(HOST_CORE_DEX_LOCATIONS)) --oat-file=$$($(1)HOST_CORE_OAT_OUT) \ + --oat-location=$$($(1)HOST_CORE_OAT) --image=$$($(1)HOST_CORE_IMG_OUT) \ + --base=$$(LIBART_IMG_HOST_BASE_ADDRESS) --instruction-set=$$($(1)ART_HOST_ARCH) \ + --instruction-set-features=$$($(1)HOST_INSTRUCTION_SET_FEATURES) \ + --host --android-root=$$(HOST_OUT) -TARGET_INSTRUCTION_SET_FEATURES := $(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) +# This "renaming" eases declaration in art/Android.mk +HOST_CORE_IMG_OUT$($(1)ART_PHONY_TEST_HOST_SUFFIX) := $($(1)HOST_CORE_IMG_OUT) -# Use dex2oat debug version for better error reporting -$(HOST_CORE_IMG_OUT): $(HOST_CORE_DEX_FILES) $(DEX2OATD_DEPENDENCY) - @echo "host dex2oat: $@ ($?)" - @mkdir -p $(dir $@) - $(hide) $(DEX2OATD) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix \ - --dex-file=,$(HOST_CORE_DEX_FILES)) $(addprefix --dex-location=,$(HOST_CORE_DEX_LOCATIONS)) --oat-file=$(HOST_CORE_OAT_OUT) \ - --oat-location=$(HOST_CORE_OAT) --image=$(HOST_CORE_IMG_OUT) --base=$(LIBART_IMG_HOST_BASE_ADDRESS) \ - --instruction-set=$(ART_HOST_ARCH) --host --android-root=$(HOST_OUT) +$$($(1)HOST_CORE_OAT_OUT): $$($(1)HOST_CORE_IMG_OUT) +endef # create-core-oat-host-rules -$(HOST_CORE_OAT_OUT): $(HOST_CORE_IMG_OUT) +$(eval $(call create-core-oat-host-rules,)) +ifneq ($(HOST_PREFER_32_BIT),true) +$(eval $(call create-core-oat-host-rules,2ND_)) +endif -define create-oat-target-targets +IMPLICIT_CHECKS_arm := null,stack +IMPLICIT_CHECKS_arm64 := none +IMPLICIT_CHECKS_x86 := none +IMPLICIT_CHECKS_x86_64 := none +IMPLICIT_CHECKS_mips := none +define create-core-oat-target-rules $$($(1)TARGET_CORE_IMG_OUT): $$($(1)TARGET_CORE_DEX_FILES) $$(DEX2OATD_DEPENDENCY) @echo "target dex2oat: $$@ ($$?)" @mkdir -p $$(dir $$@) - $$(hide) $$(DEX2OATD) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$$(PRELOADED_CLASSES) $$(addprefix \ - --dex-file=,$$(TARGET_CORE_DEX_FILES)) $$(addprefix --dex-location=,$$(TARGET_CORE_DEX_LOCATIONS)) --oat-file=$$($(1)TARGET_CORE_OAT_OUT) \ - --oat-location=$$($(1)TARGET_CORE_OAT) --image=$$($(1)TARGET_CORE_IMG_OUT) --base=$$(LIBART_IMG_TARGET_BASE_ADDRESS) \ - --instruction-set=$$($(1)TARGET_ARCH) --instruction-set-features=$$(TARGET_INSTRUCTION_SET_FEATURES) --android-root=$$(PRODUCT_OUT)/system + $$(hide) $$(DEX2OATD) --runtime-arg -Xms16m --runtime-arg -Xmx16m \ + --image-classes=$$(PRELOADED_CLASSES) $$(addprefix --dex-file=,$$(TARGET_CORE_DEX_FILES)) \ + $$(addprefix --dex-location=,$$(TARGET_CORE_DEX_LOCATIONS)) --oat-file=$$($(1)TARGET_CORE_OAT_OUT) \ + --oat-location=$$($(1)TARGET_CORE_OAT) --image=$$($(1)TARGET_CORE_IMG_OUT) \ + --base=$$(LIBART_IMG_TARGET_BASE_ADDRESS) --instruction-set=$$($(1)TARGET_ARCH) \ + --instruction-set-features=$$($(1)TARGET_INSTRUCTION_SET_FEATURES) \ + --implicit-checks=$(IMPLICIT_CHECKS_$($(1)TARGET_ARCH)) \ + --android-root=$$(PRODUCT_OUT)/system # This "renaming" eases declaration in art/Android.mk TARGET_CORE_IMG_OUT$($(1)ART_PHONY_TEST_TARGET_SUFFIX) := $($(1)TARGET_CORE_IMG_OUT) $$($(1)TARGET_CORE_OAT_OUT): $$($(1)TARGET_CORE_IMG_OUT) -endef +endef # create-core-oat-target-rules ifdef TARGET_2ND_ARCH -$(eval $(call create-oat-target-targets,2ND_)) +$(eval $(call create-core-oat-target-rules,2ND_)) endif -$(eval $(call create-oat-target-targets,)) +$(eval $(call create-core-oat-target-rules,)) ifeq ($(ART_BUILD_HOST),true) diff --git a/compiler/Android.mk b/compiler/Android.mk index 6d2f5d1ab1..e197c97196 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -16,7 +16,7 @@ LOCAL_PATH := $(call my-dir) -include art/build/Android.common.mk +include art/build/Android.common_build.mk LIBART_COMPILER_SRC_FILES := \ compiled_method.cc \ @@ -249,13 +249,14 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT ifeq ($$(art_target_or_host),host) LOCAL_LDLIBS += -ldl -lpthread endif - LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk + LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk ifeq ($$(art_target_or_host),target) LOCAL_SHARED_LIBRARIES += libcutils libvixl include $(BUILD_SHARED_LIBRARY) else # host LOCAL_STATIC_LIBRARIES += libcutils libvixl + LOCAL_MULTILIB := both include $(BUILD_HOST_SHARED_LIBRARY) endif diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h index 5050d4eb78..45cf2fba7f 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -371,10 +371,10 @@ class CommonCompilerTest : public CommonRuntimeTest { void CompileMethod(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { CHECK(method != nullptr); TimingLogger timings("CommonTest::CompileMethod", false, false); - timings.StartSplit("CompileOne"); + TimingLogger::ScopedTiming t(__FUNCTION__, &timings); compiler_driver_->CompileOne(method, &timings); + TimingLogger::ScopedTiming t2("MakeExecutable", &timings); MakeExecutable(method); - timings.EndSplit(); } void CompileDirectMethod(Handle<mirror::ClassLoader> class_loader, const char* class_name, diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc index 7441daccfe..f098a34ea7 100644 --- a/compiler/compiled_method.cc +++ b/compiler/compiled_method.cc @@ -86,7 +86,11 @@ uint32_t CompiledCode::AlignCode(uint32_t offset, InstructionSet instruction_set } size_t CompiledCode::CodeDelta() const { - switch (instruction_set_) { + return CodeDelta(instruction_set_); +} + +size_t CompiledCode::CodeDelta(InstructionSet instruction_set) { + switch (instruction_set) { case kArm: case kArm64: case kMips: @@ -98,7 +102,7 @@ size_t CompiledCode::CodeDelta() const { return 1; } default: - LOG(FATAL) << "Unknown InstructionSet: " << instruction_set_; + LOG(FATAL) << "Unknown InstructionSet: " << instruction_set; return 0; } } diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h index 23cd250678..b8cd851a1f 100644 --- a/compiler/compiled_method.h +++ b/compiler/compiled_method.h @@ -67,6 +67,7 @@ class CompiledCode { // returns the difference between the code address and a usable PC. // mainly to cope with kThumb2 where the lower bit must be set. size_t CodeDelta() const; + static size_t CodeDelta(InstructionSet instruction_set); // Returns a pointer suitable for invoking the code at the argument // code_pointer address. Mainly to cope with kThumb2 where the diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h index 2b097b5c73..6eccb0ecb7 100644 --- a/compiler/dex/bb_optimizations.h +++ b/compiler/dex/bb_optimizations.h @@ -139,7 +139,7 @@ class CodeLayout : public PassME { class NullCheckEliminationAndTypeInference : public PassME { public: NullCheckEliminationAndTypeInference() - : PassME("NCE_TypeInference", kRepeatingPreOrderDFSTraversal, "4_post_nce_cfg") { + : PassME("NCE_TypeInference", kRepeatingTopologicalSortTraversal, "4_post_nce_cfg") { } void Start(PassDataHolder* data) const { @@ -169,7 +169,8 @@ class NullCheckEliminationAndTypeInference : public PassME { class ClassInitCheckElimination : public PassME { public: - ClassInitCheckElimination() : PassME("ClInitCheckElimination", kRepeatingPreOrderDFSTraversal) { + ClassInitCheckElimination() + : PassME("ClInitCheckElimination", kRepeatingTopologicalSortTraversal) { } bool Gate(const PassDataHolder* data) const { diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index de9ac4bd01..caecb7a48e 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -527,6 +527,13 @@ enum FixupKind { std::ostream& operator<<(std::ostream& os, const FixupKind& kind); +enum VolatileKind { + kNotVolatile, // Load/Store is not volatile + kVolatile // Load/Store is volatile +}; + +std::ostream& operator<<(std::ostream& os, const VolatileKind& kind); + } // namespace art #endif // ART_COMPILER_DEX_COMPILER_ENUMS_H_ diff --git a/compiler/dex/dataflow_iterator.h b/compiler/dex/dataflow_iterator.h index 62973afc38..66c524fc98 100644 --- a/compiler/dex/dataflow_iterator.h +++ b/compiler/dex/dataflow_iterator.h @@ -337,16 +337,10 @@ namespace art { * @param mir_graph The MIRGraph considered. */ explicit TopologicalSortIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder() != nullptr ? - mir_graph->GetTopologicalSortOrder()->Size() : 0) { + : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder()->Size()) { // Extra setup for TopologicalSortIterator. idx_ = start_idx_; block_id_list_ = mir_graph->GetTopologicalSortOrder(); - - if (mir_graph->GetTopologicalSortOrder() == nullptr) { - /* Compute the topological order */ - mir_graph->ComputeTopologicalSortOrder(); - } } /** @@ -375,16 +369,10 @@ namespace art { * @param mir_graph The MIRGraph considered. */ explicit RepeatingTopologicalSortIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder() != nullptr ? - mir_graph->GetTopologicalSortOrder()->Size() : 0) { + : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder()->Size()) { // Extra setup for RepeatingTopologicalSortIterator. idx_ = start_idx_; block_id_list_ = mir_graph->GetTopologicalSortOrder(); - - if (mir_graph->GetTopologicalSortOrder() == nullptr) { - /* Compute the topological order */ - mir_graph->ComputeTopologicalSortOrder(); - } } /** diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index b8d190a9eb..72990b4be0 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -114,19 +114,20 @@ CompilationUnit::~CompilationUnit() { void CompilationUnit::StartTimingSplit(const char* label) { if (compiler_driver->GetDumpPasses()) { - timings.StartSplit(label); + timings.StartTiming(label); } } void CompilationUnit::NewTimingSplit(const char* label) { if (compiler_driver->GetDumpPasses()) { - timings.NewSplit(label); + timings.EndTiming(); + timings.StartTiming(label); } } void CompilationUnit::EndTiming() { if (compiler_driver->GetDumpPasses()) { - timings.EndSplit(); + timings.EndTiming(); if (enable_debug & (1 << kDebugTimings)) { LOG(INFO) << "TIMINGS " << PrettyMethod(method_idx, *dex_file); LOG(INFO) << Dumpable<TimingLogger>(timings); @@ -134,9 +135,6 @@ void CompilationUnit::EndTiming() { } } -// Enable opcodes that mostly work, but produce assertion errors (thus breaking libartd.so). -#define ARM64_USE_EXPERIMENTAL_OPCODES 0 - // TODO: Remove this when we are able to compile everything. int arm64_support_list[] = { Instruction::NOP, @@ -149,6 +147,9 @@ int arm64_support_list[] = { Instruction::MOVE_OBJECT, Instruction::MOVE_OBJECT_FROM16, Instruction::MOVE_OBJECT_16, + Instruction::MOVE_RESULT, + Instruction::MOVE_RESULT_WIDE, + Instruction::MOVE_RESULT_OBJECT, Instruction::MOVE_EXCEPTION, Instruction::RETURN_VOID, Instruction::RETURN, @@ -163,9 +164,19 @@ int arm64_support_list[] = { Instruction::CONST_WIDE, Instruction::CONST_WIDE_HIGH16, Instruction::CONST_STRING, + Instruction::CONST_STRING_JUMBO, + Instruction::CONST_CLASS, Instruction::MONITOR_ENTER, Instruction::MONITOR_EXIT, - // Instruction::THROW, + Instruction::CHECK_CAST, + Instruction::INSTANCE_OF, + Instruction::ARRAY_LENGTH, + Instruction::NEW_INSTANCE, + Instruction::NEW_ARRAY, + Instruction::FILLED_NEW_ARRAY, + Instruction::FILLED_NEW_ARRAY_RANGE, + Instruction::FILL_ARRAY_DATA, + Instruction::THROW, Instruction::GOTO, Instruction::GOTO_16, Instruction::GOTO_32, @@ -188,89 +199,72 @@ int arm64_support_list[] = { Instruction::IF_GEZ, Instruction::IF_GTZ, Instruction::IF_LEZ, - Instruction::NEG_INT, - Instruction::NOT_INT, - Instruction::NEG_FLOAT, - Instruction::INT_TO_BYTE, - Instruction::INT_TO_CHAR, - Instruction::INT_TO_SHORT, - Instruction::ADD_INT, - Instruction::SUB_INT, - Instruction::MUL_INT, - Instruction::DIV_INT, - Instruction::REM_INT, - Instruction::AND_INT, - Instruction::OR_INT, - Instruction::XOR_INT, - Instruction::SHL_INT, - Instruction::SHR_INT, - Instruction::USHR_INT, - Instruction::ADD_FLOAT, - Instruction::SUB_FLOAT, - Instruction::MUL_FLOAT, - Instruction::DIV_FLOAT, - Instruction::ADD_INT_2ADDR, - Instruction::SUB_INT_2ADDR, - Instruction::MUL_INT_2ADDR, - Instruction::DIV_INT_2ADDR, - Instruction::REM_INT_2ADDR, - Instruction::AND_INT_2ADDR, - Instruction::OR_INT_2ADDR, - Instruction::XOR_INT_2ADDR, - Instruction::SHL_INT_2ADDR, - Instruction::SHR_INT_2ADDR, - Instruction::USHR_INT_2ADDR, - Instruction::ADD_FLOAT_2ADDR, - Instruction::SUB_FLOAT_2ADDR, - Instruction::MUL_FLOAT_2ADDR, - Instruction::DIV_FLOAT_2ADDR, - Instruction::ADD_INT_LIT16, - Instruction::RSUB_INT, - Instruction::MUL_INT_LIT16, - Instruction::DIV_INT_LIT16, - Instruction::REM_INT_LIT16, - Instruction::AND_INT_LIT16, - Instruction::OR_INT_LIT16, - Instruction::XOR_INT_LIT16, - Instruction::ADD_INT_LIT8, - Instruction::RSUB_INT_LIT8, - Instruction::MUL_INT_LIT8, - Instruction::DIV_INT_LIT8, - Instruction::REM_INT_LIT8, - Instruction::AND_INT_LIT8, - Instruction::OR_INT_LIT8, - Instruction::XOR_INT_LIT8, - Instruction::SHL_INT_LIT8, - Instruction::SHR_INT_LIT8, - Instruction::USHR_INT_LIT8, - Instruction::SGET, - Instruction::SGET_BOOLEAN, - Instruction::SGET_BYTE, - Instruction::SGET_CHAR, - Instruction::SGET_SHORT, - Instruction::SGET_OBJECT, - Instruction::SPUT, - Instruction::SPUT_OBJECT, - Instruction::SPUT_BOOLEAN, - Instruction::SPUT_BYTE, - Instruction::SPUT_CHAR, - Instruction::SPUT_SHORT, - Instruction::CMPL_FLOAT, - Instruction::CMPG_FLOAT, + Instruction::UNUSED_3E, + Instruction::UNUSED_3F, + Instruction::UNUSED_40, + Instruction::UNUSED_41, + Instruction::UNUSED_42, + Instruction::UNUSED_43, + Instruction::AGET, + Instruction::AGET_WIDE, + Instruction::AGET_OBJECT, + Instruction::AGET_BOOLEAN, + Instruction::AGET_BYTE, + Instruction::AGET_CHAR, + Instruction::AGET_SHORT, + Instruction::APUT, + Instruction::APUT_WIDE, + Instruction::APUT_OBJECT, + Instruction::APUT_BOOLEAN, + Instruction::APUT_BYTE, + Instruction::APUT_CHAR, + Instruction::APUT_SHORT, Instruction::IGET, + Instruction::IGET_WIDE, Instruction::IGET_OBJECT, Instruction::IGET_BOOLEAN, Instruction::IGET_BYTE, Instruction::IGET_CHAR, Instruction::IGET_SHORT, Instruction::IPUT, + Instruction::IPUT_WIDE, Instruction::IPUT_OBJECT, Instruction::IPUT_BOOLEAN, Instruction::IPUT_BYTE, Instruction::IPUT_CHAR, Instruction::IPUT_SHORT, + Instruction::SGET, + Instruction::SGET_WIDE, + Instruction::SGET_OBJECT, + Instruction::SGET_BOOLEAN, + Instruction::SGET_BYTE, + Instruction::SGET_CHAR, + Instruction::SGET_SHORT, + Instruction::SPUT, + Instruction::SPUT_WIDE, + Instruction::SPUT_OBJECT, + Instruction::SPUT_BOOLEAN, + Instruction::SPUT_BYTE, + Instruction::SPUT_CHAR, + Instruction::SPUT_SHORT, + Instruction::INVOKE_VIRTUAL, + Instruction::INVOKE_SUPER, + Instruction::INVOKE_DIRECT, + Instruction::INVOKE_STATIC, + Instruction::INVOKE_INTERFACE, + Instruction::RETURN_VOID_BARRIER, + Instruction::INVOKE_VIRTUAL_RANGE, + Instruction::INVOKE_SUPER_RANGE, + Instruction::INVOKE_DIRECT_RANGE, + Instruction::INVOKE_STATIC_RANGE, + Instruction::INVOKE_INTERFACE_RANGE, + Instruction::UNUSED_79, + Instruction::UNUSED_7A, + Instruction::NEG_INT, + Instruction::NOT_INT, Instruction::NEG_LONG, Instruction::NOT_LONG, + Instruction::NEG_FLOAT, Instruction::NEG_DOUBLE, Instruction::INT_TO_LONG, Instruction::INT_TO_FLOAT, @@ -284,6 +278,20 @@ int arm64_support_list[] = { Instruction::DOUBLE_TO_INT, Instruction::DOUBLE_TO_LONG, Instruction::DOUBLE_TO_FLOAT, + Instruction::INT_TO_BYTE, + Instruction::INT_TO_CHAR, + Instruction::INT_TO_SHORT, + Instruction::ADD_INT, + Instruction::SUB_INT, + Instruction::MUL_INT, + Instruction::DIV_INT, + Instruction::REM_INT, + Instruction::AND_INT, + Instruction::OR_INT, + Instruction::XOR_INT, + Instruction::SHL_INT, + Instruction::SHR_INT, + Instruction::USHR_INT, Instruction::ADD_LONG, Instruction::SUB_LONG, Instruction::MUL_LONG, @@ -295,12 +303,27 @@ int arm64_support_list[] = { Instruction::SHL_LONG, Instruction::SHR_LONG, Instruction::USHR_LONG, - // Instruction::REM_FLOAT, + Instruction::ADD_FLOAT, + Instruction::SUB_FLOAT, + Instruction::MUL_FLOAT, + Instruction::DIV_FLOAT, + Instruction::REM_FLOAT, Instruction::ADD_DOUBLE, Instruction::SUB_DOUBLE, Instruction::MUL_DOUBLE, Instruction::DIV_DOUBLE, - // Instruction::REM_DOUBLE, + Instruction::REM_DOUBLE, + Instruction::ADD_INT_2ADDR, + Instruction::SUB_INT_2ADDR, + Instruction::MUL_INT_2ADDR, + Instruction::DIV_INT_2ADDR, + Instruction::REM_INT_2ADDR, + Instruction::AND_INT_2ADDR, + Instruction::OR_INT_2ADDR, + Instruction::XOR_INT_2ADDR, + Instruction::SHL_INT_2ADDR, + Instruction::SHR_INT_2ADDR, + Instruction::USHR_INT_2ADDR, Instruction::ADD_LONG_2ADDR, Instruction::SUB_LONG_2ADDR, Instruction::MUL_LONG_2ADDR, @@ -312,13 +335,64 @@ int arm64_support_list[] = { Instruction::SHL_LONG_2ADDR, Instruction::SHR_LONG_2ADDR, Instruction::USHR_LONG_2ADDR, - // Instruction::REM_FLOAT_2ADDR, + Instruction::ADD_FLOAT_2ADDR, + Instruction::SUB_FLOAT_2ADDR, + Instruction::MUL_FLOAT_2ADDR, + Instruction::DIV_FLOAT_2ADDR, + Instruction::REM_FLOAT_2ADDR, Instruction::ADD_DOUBLE_2ADDR, Instruction::SUB_DOUBLE_2ADDR, Instruction::MUL_DOUBLE_2ADDR, Instruction::DIV_DOUBLE_2ADDR, - // Instruction::REM_DOUBLE_2ADDR, - // TODO(Arm64): Enable compiler pass + Instruction::REM_DOUBLE_2ADDR, + Instruction::ADD_INT_LIT16, + Instruction::RSUB_INT, + Instruction::MUL_INT_LIT16, + Instruction::DIV_INT_LIT16, + Instruction::REM_INT_LIT16, + Instruction::AND_INT_LIT16, + Instruction::OR_INT_LIT16, + Instruction::XOR_INT_LIT16, + Instruction::ADD_INT_LIT8, + Instruction::RSUB_INT_LIT8, + Instruction::MUL_INT_LIT8, + Instruction::DIV_INT_LIT8, + Instruction::REM_INT_LIT8, + Instruction::AND_INT_LIT8, + Instruction::OR_INT_LIT8, + Instruction::XOR_INT_LIT8, + Instruction::SHL_INT_LIT8, + Instruction::SHR_INT_LIT8, + Instruction::USHR_INT_LIT8, + Instruction::IGET_QUICK, + Instruction::IGET_WIDE_QUICK, + Instruction::IGET_OBJECT_QUICK, + Instruction::IPUT_QUICK, + Instruction::IPUT_WIDE_QUICK, + Instruction::IPUT_OBJECT_QUICK, + Instruction::INVOKE_VIRTUAL_QUICK, + Instruction::INVOKE_VIRTUAL_RANGE_QUICK, + Instruction::UNUSED_EB, + Instruction::UNUSED_EC, + Instruction::UNUSED_ED, + Instruction::UNUSED_EE, + Instruction::UNUSED_EF, + Instruction::UNUSED_F0, + Instruction::UNUSED_F1, + Instruction::UNUSED_F2, + Instruction::UNUSED_F3, + Instruction::UNUSED_F4, + Instruction::UNUSED_F5, + Instruction::UNUSED_F6, + Instruction::UNUSED_F7, + Instruction::UNUSED_F8, + Instruction::UNUSED_F9, + Instruction::UNUSED_FA, + Instruction::UNUSED_FB, + Instruction::UNUSED_FC, + Instruction::UNUSED_FD, + Instruction::UNUSED_FE, + Instruction::UNUSED_FF, // ----- ExtendedMIROpcode ----- kMirOpPhi, kMirOpCopy, @@ -334,88 +408,6 @@ int arm64_support_list[] = { kMirOpCheck, kMirOpCheckPart2, kMirOpSelect, - -#if ARM64_USE_EXPERIMENTAL_OPCODES - Instruction::MOVE_RESULT, - Instruction::MOVE_RESULT_WIDE, - Instruction::MOVE_RESULT_OBJECT, - Instruction::CONST_STRING_JUMBO, - Instruction::CONST_CLASS, - Instruction::CHECK_CAST, - Instruction::INSTANCE_OF, - Instruction::ARRAY_LENGTH, - Instruction::NEW_INSTANCE, - Instruction::NEW_ARRAY, - Instruction::FILLED_NEW_ARRAY, - Instruction::FILLED_NEW_ARRAY_RANGE, - Instruction::FILL_ARRAY_DATA, - // Instruction::UNUSED_3E, - // Instruction::UNUSED_3F, - // Instruction::UNUSED_40, - // Instruction::UNUSED_41, - // Instruction::UNUSED_42, - // Instruction::UNUSED_43, - Instruction::AGET, - Instruction::AGET_WIDE, - Instruction::AGET_OBJECT, - Instruction::AGET_BOOLEAN, - Instruction::AGET_BYTE, - Instruction::AGET_CHAR, - Instruction::AGET_SHORT, - Instruction::APUT, - Instruction::APUT_WIDE, - Instruction::APUT_OBJECT, - Instruction::APUT_BOOLEAN, - Instruction::APUT_BYTE, - Instruction::APUT_CHAR, - Instruction::APUT_SHORT, - Instruction::IPUT_WIDE, - Instruction::IGET_WIDE, - Instruction::SGET_WIDE, - Instruction::SPUT_WIDE, - Instruction::INVOKE_VIRTUAL, - Instruction::INVOKE_SUPER, - Instruction::INVOKE_DIRECT, - Instruction::INVOKE_STATIC, - Instruction::INVOKE_INTERFACE, - Instruction::RETURN_VOID_BARRIER, - Instruction::INVOKE_VIRTUAL_RANGE, - Instruction::INVOKE_SUPER_RANGE, - Instruction::INVOKE_DIRECT_RANGE, - Instruction::INVOKE_STATIC_RANGE, - Instruction::INVOKE_INTERFACE_RANGE, - // Instruction::UNUSED_79, - // Instruction::UNUSED_7A, - // Instruction::IGET_QUICK, - // Instruction::IGET_WIDE_QUICK, - // Instruction::IGET_OBJECT_QUICK, - // Instruction::IPUT_QUICK, - // Instruction::IPUT_WIDE_QUICK, - // Instruction::IPUT_OBJECT_QUICK, - // Instruction::INVOKE_VIRTUAL_QUICK, - // Instruction::INVOKE_VIRTUAL_RANGE_QUICK, - // Instruction::UNUSED_EB, - // Instruction::UNUSED_EC, - // Instruction::UNUSED_ED, - // Instruction::UNUSED_EE, - // Instruction::UNUSED_EF, - // Instruction::UNUSED_F0, - // Instruction::UNUSED_F1, - // Instruction::UNUSED_F2, - // Instruction::UNUSED_F3, - // Instruction::UNUSED_F4, - // Instruction::UNUSED_F5, - // Instruction::UNUSED_F6, - // Instruction::UNUSED_F7, - // Instruction::UNUSED_F8, - // Instruction::UNUSED_F9, - // Instruction::UNUSED_FA, - // Instruction::UNUSED_FB, - // Instruction::UNUSED_FC, - // Instruction::UNUSED_FD, - // Instruction::UNUSED_FE, - // Instruction::UNUSED_FF, -#endif /* ARM64_USE_EXPERIMENTAL_OPCODES */ }; // TODO: Remove this when we are able to compile everything. @@ -792,10 +784,11 @@ static CompiledMethod* CompileMethod(CompilerDriver& driver, uint16_t class_def_idx, uint32_t method_idx, jobject class_loader, const DexFile& dex_file, void* llvm_compilation_unit) { - VLOG(compiler) << "Compiling " << PrettyMethod(method_idx, dex_file) << "..."; + std::string method_name = PrettyMethod(method_idx, dex_file); + VLOG(compiler) << "Compiling " << method_name << "..."; if (code_item->insns_size_in_code_units_ >= 0x10000) { LOG(INFO) << "Method size exceeds compiler limits: " << code_item->insns_size_in_code_units_ - << " in " << PrettyMethod(method_idx, dex_file); + << " in " << method_name; return NULL; } @@ -827,8 +820,7 @@ static CompiledMethod* CompileMethod(CompilerDriver& driver, cu.compiler_flip_match = false; bool use_match = !cu.compiler_method_match.empty(); bool match = use_match && (cu.compiler_flip_match ^ - (PrettyMethod(method_idx, dex_file).find(cu.compiler_method_match) != - std::string::npos)); + (method_name.find(cu.compiler_method_match) != std::string::npos)); if (!use_match || match) { cu.disable_opt = kCompilerOptimizerDisableFlags; cu.enable_debug = kCompilerDebugFlags; @@ -839,7 +831,7 @@ static CompiledMethod* CompileMethod(CompilerDriver& driver, if (gVerboseMethods.size() != 0) { cu.verbose = false; for (size_t i = 0; i < gVerboseMethods.size(); ++i) { - if (PrettyMethod(method_idx, dex_file).find(gVerboseMethods[i]) + if (method_name.find(gVerboseMethods[i]) != std::string::npos) { cu.verbose = true; break; @@ -873,7 +865,9 @@ static CompiledMethod* CompileMethod(CompilerDriver& driver, (1 << kPromoteCompilerTemps)); } else if (cu.instruction_set == kX86_64) { // TODO(X86_64): enable optimizations once backend is mature enough. - cu.disable_opt = ~(uint32_t)0; + cu.disable_opt |= ( + (1 << kLoadStoreElimination) | + (1 << kPromoteRegs)); } else if (cu.instruction_set == kArm64) { // TODO(Arm64): enable optimizations once backend is mature enough. cu.disable_opt = ~(uint32_t)0; @@ -894,27 +888,21 @@ static CompiledMethod* CompileMethod(CompilerDriver& driver, cu.mir_graph->EnableOpcodeCounting(); } - // Check early if we should skip this compilation if the profiler is enabled. - if (cu.compiler_driver->ProfilePresent()) { - std::string methodname = PrettyMethod(method_idx, dex_file); - if (cu.mir_graph->SkipCompilation(methodname)) { - return NULL; - } - } - /* Build the raw MIR graph */ cu.mir_graph->InlineMethod(code_item, access_flags, invoke_type, class_def_idx, method_idx, class_loader, dex_file); // TODO(Arm64): Remove this when we are able to compile everything. if (!CanCompileMethod(method_idx, dex_file, cu)) { - VLOG(compiler) << "Cannot compile method : " << PrettyMethod(method_idx, dex_file); + VLOG(compiler) << cu.instruction_set << ": Cannot compile method : " << method_name; return nullptr; } cu.NewTimingSplit("MIROpt:CheckFilters"); - if (cu.mir_graph->SkipCompilation()) { - VLOG(compiler) << "Skipping method : " << PrettyMethod(method_idx, dex_file); + std::string skip_message; + if (cu.mir_graph->SkipCompilation(&skip_message)) { + VLOG(compiler) << cu.instruction_set << ": Skipping method : " + << method_name << " Reason = " << skip_message; return nullptr; } @@ -922,6 +910,13 @@ static CompiledMethod* CompileMethod(CompilerDriver& driver, PassDriverMEOpts pass_driver(&cu); pass_driver.Launch(); + /* For non-leaf methods check if we should skip compilation when the profiler is enabled. */ + if (cu.compiler_driver->ProfilePresent() + && !cu.mir_graph->MethodIsLeaf() + && cu.mir_graph->SkipCompilationByName(method_name)) { + return nullptr; + } + if (cu.enable_debug & (1 << kDebugDumpCheckStats)) { cu.mir_graph->DumpCheckStats(); } @@ -937,7 +932,7 @@ static CompiledMethod* CompileMethod(CompilerDriver& driver, if (cu.enable_debug & (1 << kDebugShowMemoryUsage)) { if (cu.arena_stack.PeakBytesAllocated() > 256 * 1024) { MemStats stack_stats(cu.arena_stack.GetPeakStats()); - LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable<MemStats>(stack_stats); + LOG(INFO) << method_name << " " << Dumpable<MemStats>(stack_stats); } } cu.arena_stack.Reset(); @@ -945,7 +940,8 @@ static CompiledMethod* CompileMethod(CompilerDriver& driver, CompiledMethod* result = NULL; if (cu.mir_graph->PuntToInterpreter()) { - return NULL; + VLOG(compiler) << cu.instruction_set << ": Punted method to interpreter: " << method_name; + return nullptr; } cu.cg->Materialize(); @@ -955,21 +951,21 @@ static CompiledMethod* CompileMethod(CompilerDriver& driver, cu.NewTimingSplit("Cleanup"); if (result) { - VLOG(compiler) << "Compiled " << PrettyMethod(method_idx, dex_file); + VLOG(compiler) << cu.instruction_set << ": Compiled " << method_name; } else { - VLOG(compiler) << "Deferred " << PrettyMethod(method_idx, dex_file); + VLOG(compiler) << cu.instruction_set << ": Deferred " << method_name; } if (cu.enable_debug & (1 << kDebugShowMemoryUsage)) { if (cu.arena.BytesAllocated() > (1 * 1024 *1024)) { MemStats mem_stats(cu.arena.GetMemStats()); - LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable<MemStats>(mem_stats); + LOG(INFO) << method_name << " " << Dumpable<MemStats>(mem_stats); } } if (cu.enable_debug & (1 << kDebugShowSummaryMemoryUsage)) { LOG(INFO) << "MEMINFO " << cu.arena.BytesAllocated() << " " << cu.mir_graph->GetNumBlocks() - << " " << PrettyMethod(method_idx, dex_file); + << " " << method_name; } cu.EndTiming(); diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc index 1350665097..e372206228 100644 --- a/compiler/dex/mir_analysis.cc +++ b/compiler/dex/mir_analysis.cc @@ -941,7 +941,8 @@ void MIRGraph::AnalyzeBlock(BasicBlock* bb, MethodStats* stats) { } } -bool MIRGraph::ComputeSkipCompilation(MethodStats* stats, bool skip_default) { +bool MIRGraph::ComputeSkipCompilation(MethodStats* stats, bool skip_default, + std::string* skip_message) { float count = stats->dex_instructions; stats->math_ratio = stats->math_ops / count; stats->fp_ratio = stats->fp_ops / count; @@ -994,6 +995,8 @@ bool MIRGraph::ComputeSkipCompilation(MethodStats* stats, bool skip_default) { // If significant in size and high proportion of expensive operations, skip. if (cu_->compiler_driver->GetCompilerOptions().IsSmallMethod(GetNumDalvikInsns()) && (stats->heavyweight_ratio > 0.3)) { + *skip_message = "Is a small method with heavyweight ratio " + + std::to_string(stats->heavyweight_ratio); return true; } @@ -1003,7 +1006,7 @@ bool MIRGraph::ComputeSkipCompilation(MethodStats* stats, bool skip_default) { /* * Will eventually want this to be a bit more sophisticated and happen at verification time. */ -bool MIRGraph::SkipCompilation() { +bool MIRGraph::SkipCompilation(std::string* skip_message) { const CompilerOptions& compiler_options = cu_->compiler_driver->GetCompilerOptions(); CompilerOptions::CompilerFilter compiler_filter = compiler_options.GetCompilerFilter(); if (compiler_filter == CompilerOptions::kEverything) { @@ -1012,10 +1015,12 @@ bool MIRGraph::SkipCompilation() { // Contains a pattern we don't want to compile? if (PuntToInterpreter()) { + *skip_message = "Punt to interpreter set"; return true; } if (!compiler_options.IsCompilationEnabled()) { + *skip_message = "Compilation disabled"; return true; } @@ -1041,6 +1046,9 @@ bool MIRGraph::SkipCompilation() { // If size < cutoff, assume we'll compile - but allow removal. bool skip_compilation = (GetNumDalvikInsns() >= default_cutoff); + if (skip_compilation) { + *skip_message = "#Insns >= default_cutoff: " + std::to_string(GetNumDalvikInsns()); + } /* * Filter 1: Huge methods are likely to be machine generated, but some aren't. @@ -1048,6 +1056,7 @@ bool MIRGraph::SkipCompilation() { */ if (compiler_options.IsHugeMethod(GetNumDalvikInsns())) { skip_compilation = true; + *skip_message = "Huge method: " + std::to_string(GetNumDalvikInsns()); // If we're got a huge number of basic blocks, don't bother with further analysis. if (static_cast<size_t>(num_blocks_) > (compiler_options.GetHugeMethodThreshold() / 2)) { return true; @@ -1055,6 +1064,7 @@ bool MIRGraph::SkipCompilation() { } else if (compiler_options.IsLargeMethod(GetNumDalvikInsns()) && /* If it's large and contains no branches, it's likely to be machine generated initialization */ (GetBranchCount() == 0)) { + *skip_message = "Large method with no branches"; return true; } else if (compiler_filter == CompilerOptions::kSpeed) { // If not huge, compile. @@ -1063,6 +1073,7 @@ bool MIRGraph::SkipCompilation() { // Filter 2: Skip class initializers. if (((cu_->access_flags & kAccConstructor) != 0) && ((cu_->access_flags & kAccStatic) != 0)) { + *skip_message = "Class initializer"; return true; } @@ -1092,7 +1103,7 @@ bool MIRGraph::SkipCompilation() { AnalyzeBlock(bb, &stats); } - return ComputeSkipCompilation(&stats, skip_compilation); + return ComputeSkipCompilation(&stats, skip_compilation, skip_message); } void MIRGraph::DoCacheFieldLoweringInfo() { @@ -1285,7 +1296,7 @@ void MIRGraph::DoCacheMethodLoweringInfo() { method_lowering_infos_.GetRawStorage(), count); } -bool MIRGraph::SkipCompilation(const std::string& methodname) { +bool MIRGraph::SkipCompilationByName(const std::string& methodname) { return cu_->compiler_driver->SkipCompilation(methodname); } diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index 63a55707e5..baa46d61bd 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -27,6 +27,7 @@ #include "dex/quick/dex_file_method_inliner.h" #include "leb128.h" #include "pass_driver_me_post_opt.h" +#include "utils/scoped_arena_containers.h" namespace art { @@ -1437,22 +1438,24 @@ void MIRGraph::SSATransformationEnd() { } void MIRGraph::ComputeTopologicalSortOrder() { - std::queue<BasicBlock*> q; - std::map<int, int> visited_cnt_values; - // Clear the nodes. ClearAllVisitedFlags(); // Create the topological order if need be. - if (topological_order_ != nullptr) { - topological_order_ = new (arena_) GrowableArray<BasicBlockId>(arena_, 0); + if (topological_order_ == nullptr) { + topological_order_ = new (arena_) GrowableArray<BasicBlockId>(arena_, GetNumBlocks()); } topological_order_->Reset(); + ScopedArenaAllocator allocator(&cu_->arena_stack); + ScopedArenaQueue<BasicBlock*> q(allocator.Adapter()); + ScopedArenaVector<size_t> visited_cnt_values(GetNumBlocks(), 0u, allocator.Adapter()); + // Set up visitedCntValues map for all BB. The default value for this counters in the map is zero. // also fill initial queue. GrowableArray<BasicBlock*>::Iterator iterator(&block_list_); + size_t num_blocks = 0u; while (true) { BasicBlock* bb = iterator.Next(); @@ -1464,7 +1467,8 @@ void MIRGraph::ComputeTopologicalSortOrder() { continue; } - visited_cnt_values[bb->id] = bb->predecessors->Size(); + num_blocks += 1u; + size_t unvisited_predecessor_count = bb->predecessors->Size(); GrowableArray<BasicBlockId>::Iterator pred_iterator(bb->predecessors); // To process loops we should not wait for dominators. @@ -1475,53 +1479,75 @@ void MIRGraph::ComputeTopologicalSortOrder() { break; } - if (pred_bb->dominators == nullptr || pred_bb->hidden == true) { - continue; - } - - // Skip the backward branch. - if (pred_bb->dominators->IsBitSet(bb->id) != 0) { - visited_cnt_values[bb->id]--; + // Skip the backward branch or hidden predecessor. + if (pred_bb->hidden || + (pred_bb->dominators != nullptr && pred_bb->dominators->IsBitSet(bb->id))) { + unvisited_predecessor_count -= 1u; } } + visited_cnt_values[bb->id] = unvisited_predecessor_count; + // Add entry block to queue. - if (visited_cnt_values[bb->id] == 0) { + if (unvisited_predecessor_count == 0) { q.push(bb); } } - while (q.size() > 0) { - // Get top. - BasicBlock* bb = q.front(); - q.pop(); + // We can theoretically get a cycle where none of the blocks dominates the other. Therefore + // don't stop when the queue is empty, continue until we've processed all the blocks. + // (In practice, we've seen this for a monitor-exit catch handler that erroneously tries to + // handle its own exceptions being broken into two blocks by a jump to to the monitor-exit + // from another catch hanler. http://b/15745363 .) + AllNodesIterator candidate_iter(this); // For the empty queue case. + while (num_blocks != 0u) { + num_blocks -= 1u; + BasicBlock* bb = nullptr; + if (!q.empty()) { + // Get top. + bb = q.front(); + q.pop(); + } else { + // Find some block we didn't visit yet that has at least one visited predecessor. + while (bb == nullptr) { + BasicBlock* candidate = candidate_iter.Next(); + DCHECK(candidate != nullptr); + if (candidate->visited || candidate->hidden) { + continue; + } + GrowableArray<BasicBlockId>::Iterator iter(candidate->predecessors); + for (BasicBlock* pred_bb = GetBasicBlock(iter.Next()); pred_bb != nullptr; + pred_bb = GetBasicBlock(iter.Next())) { + if (!pred_bb->hidden && pred_bb->visited) { + bb = candidate; + break; + } + } + } + } DCHECK_EQ(bb->hidden, false); - - if (bb->IsExceptionBlock() == true) { - continue; - } + DCHECK_EQ(bb->visited, false); // We've visited all the predecessors. So, we can visit bb. - if (bb->visited == false) { - bb->visited = true; + bb->visited = true; - // Now add the basic block. - topological_order_->Insert(bb->id); + // Now add the basic block. + topological_order_->Insert(bb->id); - // Reduce visitedCnt for all the successors and add into the queue ones with visitedCnt equals to zero. - ChildBlockIterator succIter(bb, this); - BasicBlock* successor = succIter.Next(); - while (successor != nullptr) { - // one more predecessor was visited. - visited_cnt_values[successor->id]--; - - if (visited_cnt_values[successor->id] <= 0 && successor->visited == false && successor->hidden == false) { - q.push(successor); - } + // Reduce visitedCnt for all the successors and add into the queue ones with visitedCnt equals to zero. + ChildBlockIterator succIter(bb, this); + BasicBlock* successor = succIter.Next(); + for ( ; successor != nullptr; successor = succIter.Next()) { + if (successor->visited || successor->hidden) { + continue; + } - // Take next successor. - successor = succIter.Next(); + // one more predecessor was visited. + DCHECK_NE(visited_cnt_values[successor->id], 0u); + visited_cnt_values[successor->id] -= 1u; + if (visited_cnt_values[successor->id] == 0u) { + q.push(successor); } } } diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index 15c0aa4eaf..f4e28e6c0e 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -559,12 +559,12 @@ class MIRGraph { * Examine the graph to determine whether it's worthwile to spend the time compiling * this method. */ - bool SkipCompilation(); + bool SkipCompilation(std::string* skip_message); /* * Should we skip the compilation of this method based on its name? */ - bool SkipCompilation(const std::string& methodname); + bool SkipCompilationByName(const std::string& methodname); /* * Parse dex method and add MIR at current insert point. Returns id (which is @@ -701,6 +701,7 @@ class MIRGraph { void BasicBlockOptimization(); GrowableArray<BasicBlockId>* GetTopologicalSortOrder() { + DCHECK(topological_order_ != nullptr); return topological_order_; } @@ -1127,7 +1128,8 @@ class MIRGraph { void CountChecks(BasicBlock* bb); void AnalyzeBlock(BasicBlock* bb, struct MethodStats* stats); - bool ComputeSkipCompilation(struct MethodStats* stats, bool skip_default); + bool ComputeSkipCompilation(struct MethodStats* stats, bool skip_default, + std::string* skip_message); CompilationUnit* const cu_; GrowableArray<int>* ssa_base_vregs_; diff --git a/compiler/dex/mir_optimization_test.cc b/compiler/dex/mir_optimization_test.cc index 29c353a4e6..9b2e798383 100644 --- a/compiler/dex/mir_optimization_test.cc +++ b/compiler/dex/mir_optimization_test.cc @@ -190,10 +190,12 @@ class ClassInitCheckEliminationTest : public testing::Test { void PerformClassInitCheckElimination() { cu_.mir_graph->SSATransformationStart(); cu_.mir_graph->ComputeDFSOrders(); + cu_.mir_graph->ComputeDominators(); + cu_.mir_graph->ComputeTopologicalSortOrder(); cu_.mir_graph->SSATransformationEnd(); bool gate_result = cu_.mir_graph->EliminateClassInitChecksGate(); ASSERT_TRUE(gate_result); - RepeatingPreOrderDfsIterator iterator(cu_.mir_graph.get()); + RepeatingTopologicalSortIterator iterator(cu_.mir_graph.get()); bool change = false; for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) { change = cu_.mir_graph->EliminateClassInitChecks(bb); diff --git a/compiler/dex/pass_driver_me.h b/compiler/dex/pass_driver_me.h index 7d76fb83d4..031c5cf87d 100644 --- a/compiler/dex/pass_driver_me.h +++ b/compiler/dex/pass_driver_me.h @@ -62,6 +62,12 @@ class PassDriverME: public PassDriver<PassDriverType> { case kPostOrderDOMTraversal: DoWalkBasicBlocks<PostOrderDOMIterator>(&pass_me_data_holder_, me_pass); break; + case kTopologicalSortTraversal: + DoWalkBasicBlocks<TopologicalSortIterator>(&pass_me_data_holder_, me_pass); + break; + case kRepeatingTopologicalSortTraversal: + DoWalkBasicBlocks<RepeatingTopologicalSortIterator>(&pass_me_data_holder_, me_pass); + break; case kAllNodes: DoWalkBasicBlocks<AllNodesIterator>(&pass_me_data_holder_, me_pass); break; diff --git a/compiler/dex/pass_driver_me_post_opt.cc b/compiler/dex/pass_driver_me_post_opt.cc index cb63f4184f..14108af632 100644 --- a/compiler/dex/pass_driver_me_post_opt.cc +++ b/compiler/dex/pass_driver_me_post_opt.cc @@ -36,6 +36,7 @@ const Pass* const PassDriver<PassDriverMEPostOpt>::g_passes[] = { GetPassInstance<CalculatePredecessors>(), GetPassInstance<DFSOrders>(), GetPassInstance<BuildDomination>(), + GetPassInstance<TopologicalSortOrders>(), GetPassInstance<DefBlockMatrix>(), GetPassInstance<CreatePhiNodes>(), GetPassInstance<ClearVisitedFlag>(), diff --git a/compiler/dex/post_opt_passes.h b/compiler/dex/post_opt_passes.h index 445c46d038..a1b0df4b32 100644 --- a/compiler/dex/post_opt_passes.h +++ b/compiler/dex/post_opt_passes.h @@ -127,6 +127,23 @@ class BuildDomination : public PassME { }; /** + * @class TopologicalSortOrders + * @brief Compute the topological sort order of the MIR graph + */ +class TopologicalSortOrders : public PassME { + public: + TopologicalSortOrders() : PassME("TopologicalSortOrders") { + } + + void Start(PassDataHolder* data) const { + DCHECK(data != nullptr); + CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit; + DCHECK(c_unit != nullptr); + c_unit->mir_graph.get()->ComputeTopologicalSortOrder(); + } +}; + +/** * @class DefBlockMatrix * @brief Calculate the matrix of definition per basic block */ diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index 590c7674f6..04d6898e36 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -316,9 +316,9 @@ void ArmMir2Lir::GenMoveException(RegLocation rl_dest) { int ex_offset = Thread::ExceptionOffset<4>().Int32Value(); RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true); RegStorage reset_reg = AllocTempRef(); - LoadRefDisp(rs_rARM_SELF, ex_offset, rl_result.reg); + LoadRefDisp(rs_rARM_SELF, ex_offset, rl_result.reg, kNotVolatile); LoadConstant(reset_reg, 0); - StoreRefDisp(rs_rARM_SELF, ex_offset, reset_reg); + StoreRefDisp(rs_rARM_SELF, ex_offset, reset_reg, kNotVolatile); FreeTemp(reset_reg); StoreValue(rl_dest, rl_result); } diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h index 44998627ca..70dce7f11e 100644 --- a/compiler/dex/quick/arm/codegen_arm.h +++ b/compiler/dex/quick/arm/codegen_arm.h @@ -33,20 +33,16 @@ class ArmMir2Lir FINAL : public Mir2Lir { LIR* CheckSuspendUsingLoad() OVERRIDE; RegStorage LoadHelper(ThreadOffset<4> offset) OVERRIDE; RegStorage LoadHelper(ThreadOffset<8> offset) OVERRIDE; - LIR* LoadBaseDispVolatile(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size) OVERRIDE; LIR* LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size) OVERRIDE; + OpSize size, VolatileKind is_volatile) OVERRIDE; LIR* LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest, int scale, OpSize size) OVERRIDE; LIR* LoadBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale, int displacement, RegStorage r_dest, OpSize size) OVERRIDE; LIR* LoadConstantNoClobber(RegStorage r_dest, int value); LIR* LoadConstantWide(RegStorage r_dest, int64_t value); - LIR* StoreBaseDispVolatile(RegStorage r_base, int displacement, RegStorage r_src, - OpSize size) OVERRIDE; LIR* StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, - OpSize size) OVERRIDE; + OpSize size, VolatileKind is_volatile) OVERRIDE; LIR* StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale, OpSize size) OVERRIDE; LIR* StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale, int displacement, diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index 916c52838a..e34d944ab2 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -723,7 +723,7 @@ bool ArmMir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) { } else { DCHECK(size == kSignedByte || size == kSignedHalf || size == k32); // Unaligned load with LDR and LDRSH is allowed on ARMv7 with SCTLR.A set to 0. - LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size); + LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, kNotVolatile); StoreValue(rl_dest, rl_result); } return true; @@ -737,13 +737,13 @@ bool ArmMir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) { if (size == k64) { // Fake unaligned STRD by two unaligned STR instructions on ARMv7 with SCTLR.A set to 0. RegLocation rl_value = LoadValueWide(rl_src_value, kCoreReg); - StoreBaseDisp(rl_address.reg, 0, rl_value.reg.GetLow(), k32); - StoreBaseDisp(rl_address.reg, 4, rl_value.reg.GetHigh(), k32); + StoreBaseDisp(rl_address.reg, 0, rl_value.reg.GetLow(), k32, kNotVolatile); + StoreBaseDisp(rl_address.reg, 4, rl_value.reg.GetHigh(), k32, kNotVolatile); } else { DCHECK(size == kSignedByte || size == kSignedHalf || size == k32); // Unaligned store with STR and STRSH is allowed on ARMv7 with SCTLR.A set to 0. RegLocation rl_value = LoadValue(rl_src_value, kCoreReg); - StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size); + StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size, kNotVolatile); } return true; } @@ -1230,7 +1230,7 @@ void ArmMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, } FreeTemp(reg_len); } - LoadBaseDisp(reg_ptr, data_offset, rl_result.reg, size); + LoadBaseDisp(reg_ptr, data_offset, rl_result.reg, size, kNotVolatile); MarkPossibleNullPointerException(opt_flags); if (!constant_index) { FreeTemp(reg_ptr); @@ -1330,7 +1330,7 @@ void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, FreeTemp(reg_len); } - StoreBaseDisp(reg_ptr, data_offset, rl_src.reg, size); + StoreBaseDisp(reg_ptr, data_offset, rl_src.reg, size, kNotVolatile); MarkPossibleNullPointerException(opt_flags); } else { /* reg_ptr -> array data */ diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc index b236f99311..bc8f95b1ca 100644 --- a/compiler/dex/quick/arm/utility_arm.cc +++ b/compiler/dex/quick/arm/utility_arm.cc @@ -961,31 +961,37 @@ LIR* ArmMir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStorag return load; } -LIR* ArmMir2Lir::LoadBaseDispVolatile(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size) { - // Only 64-bit load needs special handling. - if (UNLIKELY(size == k64 || size == kDouble)) { - DCHECK(!r_dest.IsFloat()); // See RegClassForFieldLoadSave(). - // If the cpu supports LPAE, aligned LDRD is atomic - fall through to LoadBaseDisp(). - if (!cu_->compiler_driver->GetInstructionSetFeatures().HasLpae()) { - // Use LDREXD for the atomic load. (Expect displacement > 0, don't optimize for == 0.) - RegStorage r_ptr = AllocTemp(); - OpRegRegImm(kOpAdd, r_ptr, r_base, displacement); - LIR* lir = NewLIR3(kThumb2Ldrexd, r_dest.GetLowReg(), r_dest.GetHighReg(), r_ptr.GetReg()); - FreeTemp(r_ptr); - return lir; - } - } - return LoadBaseDisp(r_base, displacement, r_dest, size); -} - LIR* ArmMir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size) { + OpSize size, VolatileKind is_volatile) { // TODO: base this on target. if (size == kWord) { size = k32; } - return LoadBaseDispBody(r_base, displacement, r_dest, size); + LIR* load; + if (UNLIKELY(is_volatile == kVolatile && + (size == k64 || size == kDouble) && + !cu_->compiler_driver->GetInstructionSetFeatures().HasLpae())) { + // Only 64-bit load needs special handling. + // If the cpu supports LPAE, aligned LDRD is atomic - fall through to LoadBaseDisp(). + DCHECK(!r_dest.IsFloat()); // See RegClassForFieldLoadSave(). + // Use LDREXD for the atomic load. (Expect displacement > 0, don't optimize for == 0.) + RegStorage r_ptr = AllocTemp(); + OpRegRegImm(kOpAdd, r_ptr, r_base, displacement); + LIR* lir = NewLIR3(kThumb2Ldrexd, r_dest.GetLowReg(), r_dest.GetHighReg(), r_ptr.GetReg()); + FreeTemp(r_ptr); + return lir; + } else { + load = LoadBaseDispBody(r_base, displacement, r_dest, size); + } + + if (UNLIKELY(is_volatile == kVolatile)) { + // Without context sensitive analysis, we must issue the most conservative barriers. + // In this case, either a load or store may follow so we issue both barriers. + GenMemBarrier(kLoadLoad); + GenMemBarrier(kLoadStore); + } + + return load; } @@ -1081,49 +1087,58 @@ LIR* ArmMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegStora return store; } -LIR* ArmMir2Lir::StoreBaseDispVolatile(RegStorage r_base, int displacement, RegStorage r_src, - OpSize size) { - // Only 64-bit store needs special handling. - if (UNLIKELY(size == k64 || size == kDouble)) { - DCHECK(!r_src.IsFloat()); // See RegClassForFieldLoadSave(). +LIR* ArmMir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, + OpSize size, VolatileKind is_volatile) { + if (UNLIKELY(is_volatile == kVolatile)) { + // There might have been a store before this volatile one so insert StoreStore barrier. + GenMemBarrier(kStoreStore); + } + + LIR* store; + if (UNLIKELY(is_volatile == kVolatile && + (size == k64 || size == kDouble) && + !cu_->compiler_driver->GetInstructionSetFeatures().HasLpae())) { + // Only 64-bit store needs special handling. // If the cpu supports LPAE, aligned STRD is atomic - fall through to StoreBaseDisp(). - if (!cu_->compiler_driver->GetInstructionSetFeatures().HasLpae()) { - // Use STREXD for the atomic store. (Expect displacement > 0, don't optimize for == 0.) - RegStorage r_ptr = AllocTemp(); + // Use STREXD for the atomic store. (Expect displacement > 0, don't optimize for == 0.) + DCHECK(!r_src.IsFloat()); // See RegClassForFieldLoadSave(). + RegStorage r_ptr = AllocTemp(); + OpRegRegImm(kOpAdd, r_ptr, r_base, displacement); + LIR* fail_target = NewLIR0(kPseudoTargetLabel); + // We have only 5 temporary registers available and if r_base, r_src and r_ptr already + // take 4, we can't directly allocate 2 more for LDREXD temps. In that case clobber r_ptr + // in LDREXD and recalculate it from r_base. + RegStorage r_temp = AllocTemp(); + RegStorage r_temp_high = AllocFreeTemp(); // We may not have another temp. + if (r_temp_high.Valid()) { + NewLIR3(kThumb2Ldrexd, r_temp.GetReg(), r_temp_high.GetReg(), r_ptr.GetReg()); + FreeTemp(r_temp_high); + FreeTemp(r_temp); + } else { + // If we don't have another temp, clobber r_ptr in LDREXD and reload it. + NewLIR3(kThumb2Ldrexd, r_temp.GetReg(), r_ptr.GetReg(), r_ptr.GetReg()); + FreeTemp(r_temp); // May need the temp for kOpAdd. OpRegRegImm(kOpAdd, r_ptr, r_base, displacement); - LIR* fail_target = NewLIR0(kPseudoTargetLabel); - // We have only 5 temporary registers available and if r_base, r_src and r_ptr already - // take 4, we can't directly allocate 2 more for LDREXD temps. In that case clobber r_ptr - // in LDREXD and recalculate it from r_base. - RegStorage r_temp = AllocTemp(); - RegStorage r_temp_high = AllocFreeTemp(); // We may not have another temp. - if (r_temp_high.Valid()) { - NewLIR3(kThumb2Ldrexd, r_temp.GetReg(), r_temp_high.GetReg(), r_ptr.GetReg()); - FreeTemp(r_temp_high); - FreeTemp(r_temp); - } else { - // If we don't have another temp, clobber r_ptr in LDREXD and reload it. - NewLIR3(kThumb2Ldrexd, r_temp.GetReg(), r_ptr.GetReg(), r_ptr.GetReg()); - FreeTemp(r_temp); // May need the temp for kOpAdd. - OpRegRegImm(kOpAdd, r_ptr, r_base, displacement); - } - LIR* lir = NewLIR4(kThumb2Strexd, r_temp.GetReg(), r_src.GetLowReg(), r_src.GetHighReg(), - r_ptr.GetReg()); - OpCmpImmBranch(kCondNe, r_temp, 0, fail_target); - FreeTemp(r_ptr); - return lir; } + store = NewLIR4(kThumb2Strexd, r_temp.GetReg(), r_src.GetLowReg(), r_src.GetHighReg(), + r_ptr.GetReg()); + OpCmpImmBranch(kCondNe, r_temp, 0, fail_target); + FreeTemp(r_ptr); + } else { + // TODO: base this on target. + if (size == kWord) { + size = k32; + } + + store = StoreBaseDispBody(r_base, displacement, r_src, size); } - return StoreBaseDisp(r_base, displacement, r_src, size); -} -LIR* ArmMir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, - OpSize size) { - // TODO: base this on target. - if (size == kWord) { - size = k32; + if (UNLIKELY(is_volatile == kVolatile)) { + // A load might follow the volatile store so insert a StoreLoad barrier. + GenMemBarrier(kStoreLoad); } - return StoreBaseDispBody(r_base, displacement, r_src, size); + + return store; } LIR* ArmMir2Lir::OpFpRegCopy(RegStorage r_dest, RegStorage r_src) { diff --git a/compiler/dex/quick/arm64/arm64_lir.h b/compiler/dex/quick/arm64/arm64_lir.h index 3f32c5194e..ac5f33b721 100644 --- a/compiler/dex/quick/arm64/arm64_lir.h +++ b/compiler/dex/quick/arm64/arm64_lir.h @@ -101,6 +101,7 @@ namespace art { // Temporary macros, used to mark code which wants to distinguish betweek zr/sp. #define A64_REG_IS_SP(reg_num) ((reg_num) == rwsp || (reg_num) == rsp) #define A64_REG_IS_ZR(reg_num) ((reg_num) == rwzr || (reg_num) == rxzr) +#define A64_REGSTORAGE_IS_SP_OR_ZR(rs) (((rs).GetRegNum() & 0x1f) == 0x1f) enum Arm64ResourceEncodingPos { kArm64GPReg0 = 0, @@ -180,9 +181,9 @@ const RegLocation arm_loc_c_return = const RegLocation arm_loc_c_return_wide = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, rs_x0, INVALID_SREG, INVALID_SREG}; const RegLocation arm_loc_c_return_float = - {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, rs_f0, INVALID_SREG, INVALID_SREG}; + {kLocPhysReg, 0, 0, 0, 1, 0, 0, 0, 1, rs_f0, INVALID_SREG, INVALID_SREG}; const RegLocation arm_loc_c_return_double = - {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, rs_d0, INVALID_SREG, INVALID_SREG}; + {kLocPhysReg, 1, 0, 0, 1, 0, 0, 0, 1, rs_d0, INVALID_SREG, INVALID_SREG}; /** * @brief Shift-type to be applied to a register via EncodeShift(). diff --git a/compiler/dex/quick/arm64/assemble_arm64.cc b/compiler/dex/quick/arm64/assemble_arm64.cc index e017e5ffb1..2ac34cd9de 100644 --- a/compiler/dex/quick/arm64/assemble_arm64.cc +++ b/compiler/dex/quick/arm64/assemble_arm64.cc @@ -632,19 +632,19 @@ uint8_t* Arm64Mir2Lir::EncodeLIRs(uint8_t* write_pos, LIR* lir) { if (static_cast<unsigned>(kind) < kFmtBitBlt) { bool is_zero = A64_REG_IS_ZR(operand); - if (kIsDebugBuild) { + if (kIsDebugBuild && (kFailOnSizeError || kReportSizeError)) { // Register usage checks: First establish register usage requirements based on the // format in `kind'. bool want_float = false; bool want_64_bit = false; - bool want_size_match = false; + bool want_var_size = true; bool want_zero = false; switch (kind) { case kFmtRegX: want_64_bit = true; // Intentional fall-through. case kFmtRegW: - want_size_match = true; + want_var_size = false; // Intentional fall-through. case kFmtRegR: want_zero = true; @@ -653,7 +653,7 @@ uint8_t* Arm64Mir2Lir::EncodeLIRs(uint8_t* write_pos, LIR* lir) { want_64_bit = true; // Intentional fall-through. case kFmtRegWOrSp: - want_size_match = true; + want_var_size = false; break; case kFmtRegROrSp: break; @@ -661,7 +661,7 @@ uint8_t* Arm64Mir2Lir::EncodeLIRs(uint8_t* write_pos, LIR* lir) { want_64_bit = true; // Intentional fall-through. case kFmtRegS: - want_size_match = true; + want_var_size = false; // Intentional fall-through. case kFmtRegF: want_float = true; @@ -672,21 +672,27 @@ uint8_t* Arm64Mir2Lir::EncodeLIRs(uint8_t* write_pos, LIR* lir) { break; } + // want_var_size == true means kind == kFmtReg{R,F}. In these two cases, we want + // the register size to be coherent with the instruction width. + if (want_var_size) { + want_64_bit = opcode_is_wide; + } + // Now check that the requirements are satisfied. RegStorage reg(operand | RegStorage::kValid); const char *expected = nullptr; if (want_float) { if (!reg.IsFloat()) { expected = "float register"; - } else if (want_size_match && (reg.IsDouble() != want_64_bit)) { + } else if (reg.IsDouble() != want_64_bit) { expected = (want_64_bit) ? "double register" : "single register"; } } else { if (reg.IsFloat()) { expected = "core register"; - } else if (want_size_match && (reg.Is64Bit() != want_64_bit)) { + } else if (reg.Is64Bit() != want_64_bit) { expected = (want_64_bit) ? "x-register" : "w-register"; - } else if (reg.GetRegNum() == 31 && is_zero != want_zero) { + } else if (A64_REGSTORAGE_IS_SP_OR_ZR(reg) && is_zero != want_zero) { expected = (want_zero) ? "zero-register" : "sp-register"; } } @@ -698,8 +704,13 @@ uint8_t* Arm64Mir2Lir::EncodeLIRs(uint8_t* write_pos, LIR* lir) { if (expected != nullptr) { LOG(WARNING) << "Method: " << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " @ 0x" << std::hex << lir->dalvik_offset; - LOG(FATAL) << "Bad argument n. " << i << " of " << encoder->name - << ". Expected " << expected << ", got 0x" << std::hex << operand; + if (kFailOnSizeError) { + LOG(FATAL) << "Bad argument n. " << i << " of " << encoder->name + << ". Expected " << expected << ", got 0x" << std::hex << operand; + } else { + LOG(WARNING) << "Bad argument n. " << i << " of " << encoder->name + << ". Expected " << expected << ", got 0x" << std::hex << operand; + } } } diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc index 1df576b743..c3f4711546 100644 --- a/compiler/dex/quick/arm64/call_arm64.cc +++ b/compiler/dex/quick/arm64/call_arm64.cc @@ -267,7 +267,7 @@ void Arm64Mir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) { MarkPossibleNullPointerException(opt_flags); LIR* slow_unlock_branch = OpCmpBranch(kCondNe, rs_w1, rs_w2, NULL); GenMemBarrier(kStoreLoad); - Store32Disp(rs_x0, mirror::Object::MonitorOffset().Int32Value(), rs_xzr); + Store32Disp(rs_x0, mirror::Object::MonitorOffset().Int32Value(), rs_wzr); LIR* unlock_success_branch = OpUnconditionalBranch(NULL); LIR* slow_path_target = NewLIR0(kPseudoTargetLabel); @@ -289,8 +289,8 @@ void Arm64Mir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) { void Arm64Mir2Lir::GenMoveException(RegLocation rl_dest) { int ex_offset = Thread::ExceptionOffset<8>().Int32Value(); RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true); - LoadRefDisp(rs_rA64_SELF, ex_offset, rl_result.reg); - StoreRefDisp(rs_rA64_SELF, ex_offset, rs_xzr); + LoadRefDisp(rs_rA64_SELF, ex_offset, rl_result.reg, kNotVolatile); + StoreRefDisp(rs_rA64_SELF, ex_offset, rs_xzr, kNotVolatile); StoreValue(rl_dest, rl_result); } diff --git a/compiler/dex/quick/arm64/codegen_arm64.h b/compiler/dex/quick/arm64/codegen_arm64.h index 4985d578f9..a887a3b150 100644 --- a/compiler/dex/quick/arm64/codegen_arm64.h +++ b/compiler/dex/quick/arm64/codegen_arm64.h @@ -26,6 +26,11 @@ namespace art { class Arm64Mir2Lir : public Mir2Lir { protected: + // If we detect a size error, FATAL out. + static constexpr bool kFailOnSizeError = false && kIsDebugBuild; + // If we detect a size error, report to LOG. + static constexpr bool kReportSizeError = false && kIsDebugBuild; + // TODO: consolidate 64-bit target support. class InToRegStorageMapper { public: @@ -71,22 +76,25 @@ class Arm64Mir2Lir : public Mir2Lir { LIR* CheckSuspendUsingLoad() OVERRIDE; RegStorage LoadHelper(ThreadOffset<4> offset) OVERRIDE; RegStorage LoadHelper(ThreadOffset<8> offset) OVERRIDE; - LIR* LoadBaseDispVolatile(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size) OVERRIDE; LIR* LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size) OVERRIDE; + OpSize size, VolatileKind is_volatile) OVERRIDE; + LIR* LoadRefDisp(RegStorage r_base, int displacement, RegStorage r_dest, + VolatileKind is_volatile) + OVERRIDE; LIR* LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest, int scale, OpSize size) OVERRIDE; + LIR* LoadRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest) OVERRIDE; LIR* LoadBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale, int displacement, RegStorage r_dest, OpSize size) OVERRIDE; LIR* LoadConstantNoClobber(RegStorage r_dest, int value); LIR* LoadConstantWide(RegStorage r_dest, int64_t value); - LIR* StoreBaseDispVolatile(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size) OVERRIDE; LIR* StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, - OpSize size) OVERRIDE; + OpSize size, VolatileKind is_volatile) OVERRIDE; + LIR* StoreRefDisp(RegStorage r_base, int displacement, RegStorage r_src, + VolatileKind is_volatile) OVERRIDE; LIR* StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale, OpSize size) OVERRIDE; + LIR* StoreRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src) OVERRIDE; LIR* StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale, int displacement, RegStorage r_src, OpSize size) OVERRIDE; void MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) OVERRIDE; @@ -285,8 +293,15 @@ class Arm64Mir2Lir : public Mir2Lir { * @see As64BitReg */ RegStorage As32BitReg(RegStorage reg) { - DCHECK(reg.Is64Bit()); DCHECK(!reg.IsPair()); + if ((kFailOnSizeError || kReportSizeError) && !reg.Is64Bit()) { + if (kFailOnSizeError) { + LOG(FATAL) << "Expected 64b register"; + } else { + LOG(WARNING) << "Expected 64b register"; + return reg; + } + } RegStorage ret_val = RegStorage(RegStorage::k32BitSolo, reg.GetRawBits() & RegStorage::kRegTypeMask); DCHECK_EQ(GetRegInfo(reg)->FindMatchingView(RegisterInfo::k32SoloStorageMask) @@ -295,6 +310,18 @@ class Arm64Mir2Lir : public Mir2Lir { return ret_val; } + RegStorage Check32BitReg(RegStorage reg) { + if ((kFailOnSizeError || kReportSizeError) && !reg.Is32Bit()) { + if (kFailOnSizeError) { + LOG(FATAL) << "Checked for 32b register"; + } else { + LOG(WARNING) << "Checked for 32b register"; + return As32BitReg(reg); + } + } + return reg; + } + /** * @brief Given register wNN (sNN), returns register xNN (dNN). * @param reg #RegStorage containing a Solo32 input register (e.g. @c w1 or @c s2). @@ -302,8 +329,15 @@ class Arm64Mir2Lir : public Mir2Lir { * @see As32BitReg */ RegStorage As64BitReg(RegStorage reg) { - DCHECK(reg.Is32Bit()); DCHECK(!reg.IsPair()); + if ((kFailOnSizeError || kReportSizeError) && !reg.Is32Bit()) { + if (kFailOnSizeError) { + LOG(FATAL) << "Expected 32b register"; + } else { + LOG(WARNING) << "Expected 32b register"; + return reg; + } + } RegStorage ret_val = RegStorage(RegStorage::k64BitSolo, reg.GetRawBits() & RegStorage::kRegTypeMask); DCHECK_EQ(GetRegInfo(reg)->FindMatchingView(RegisterInfo::k64SoloStorageMask) @@ -312,6 +346,18 @@ class Arm64Mir2Lir : public Mir2Lir { return ret_val; } + RegStorage Check64BitReg(RegStorage reg) { + if ((kFailOnSizeError || kReportSizeError) && !reg.Is64Bit()) { + if (kFailOnSizeError) { + LOG(FATAL) << "Checked for 64b register"; + } else { + LOG(WARNING) << "Checked for 64b register"; + return As64BitReg(reg); + } + } + return reg; + } + LIR* LoadFPConstantValue(RegStorage r_dest, int32_t value); LIR* LoadFPConstantValueWide(RegStorage r_dest, int64_t value); void ReplaceFixup(LIR* prev_lir, LIR* orig_lir, LIR* new_lir); diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc index cb50674f42..ac32f6e3d9 100644 --- a/compiler/dex/quick/arm64/int_arm64.cc +++ b/compiler/dex/quick/arm64/int_arm64.cc @@ -453,7 +453,7 @@ bool Arm64Mir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) { RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); // kRefReg RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); - LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size); + LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, kNotVolatile); if (size == k64) { StoreValueWide(rl_dest, rl_result); } else { @@ -476,7 +476,7 @@ bool Arm64Mir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) { DCHECK(size == kSignedByte || size == kSignedHalf || size == k32); rl_value = LoadValue(rl_src_value, kCoreReg); } - StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size); + StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size, kNotVolatile); return true; } @@ -790,7 +790,11 @@ void Arm64Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, } FreeTemp(reg_len); } - LoadBaseDisp(reg_ptr, data_offset, rl_result.reg, size); + if (rl_result.ref) { + LoadRefDisp(reg_ptr, data_offset, rl_result.reg, kNotVolatile); + } else { + LoadBaseDisp(reg_ptr, data_offset, rl_result.reg, size, kNotVolatile); + } MarkPossibleNullPointerException(opt_flags); if (!constant_index) { FreeTemp(reg_ptr); @@ -811,7 +815,11 @@ void Arm64Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, GenArrayBoundsCheck(rl_index.reg, reg_len); FreeTemp(reg_len); } - LoadBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_result.reg, scale, size); + if (rl_result.ref) { + LoadRefIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_result.reg); + } else { + LoadBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_result.reg, scale, size); + } MarkPossibleNullPointerException(opt_flags); FreeTemp(reg_ptr); StoreValue(rl_dest, rl_result); @@ -890,8 +898,11 @@ void Arm64Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, } FreeTemp(reg_len); } - - StoreBaseDisp(reg_ptr, data_offset, rl_src.reg, size); + if (rl_src.ref) { + StoreRefDisp(reg_ptr, data_offset, rl_src.reg, kNotVolatile); + } else { + StoreBaseDisp(reg_ptr, data_offset, rl_src.reg, size, kNotVolatile); + } MarkPossibleNullPointerException(opt_flags); } else { /* reg_ptr -> array data */ @@ -901,7 +912,11 @@ void Arm64Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, GenArrayBoundsCheck(rl_index.reg, reg_len); FreeTemp(reg_len); } - StoreBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_src.reg, scale, size); + if (rl_src.ref) { + StoreRefIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_src.reg); + } else { + StoreBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_src.reg, scale, size); + } MarkPossibleNullPointerException(opt_flags); } if (allocated_reg_ptr_temp) { diff --git a/compiler/dex/quick/arm64/target_arm64.cc b/compiler/dex/quick/arm64/target_arm64.cc index 06e1cda305..dfaa4837d2 100644 --- a/compiler/dex/quick/arm64/target_arm64.cc +++ b/compiler/dex/quick/arm64/target_arm64.cc @@ -789,7 +789,7 @@ RegStorage Arm64Mir2Lir::LoadHelper(ThreadOffset<4> offset) { RegStorage Arm64Mir2Lir::LoadHelper(ThreadOffset<8> offset) { // TODO(Arm64): use LoadWordDisp instead. // e.g. LoadWordDisp(rs_rA64_SELF, offset.Int32Value(), rs_rA64_LR); - LoadBaseDisp(rs_rA64_SELF, offset.Int32Value(), rs_rA64_LR, k64); + LoadBaseDisp(rs_rA64_SELF, offset.Int32Value(), rs_rA64_LR, k64, kNotVolatile); return rs_rA64_LR; } @@ -949,7 +949,7 @@ void Arm64Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) { StoreValue(rl_method, rl_src); // If Method* has been promoted, explicitly flush if (rl_method.location == kLocPhysReg) { - StoreRefDisp(TargetReg(kSp), 0, TargetReg(kArg0)); + StoreRefDisp(TargetReg(kSp), 0, TargetReg(kArg0), kNotVolatile); } if (cu_->num_ins == 0) { @@ -971,7 +971,7 @@ void Arm64Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) { } else if ((v_map->fp_location == kLocPhysReg) && t_loc->fp) { OpRegCopy(RegStorage::Solo32(v_map->FpReg), reg); } else { - StoreBaseDisp(TargetReg(kSp), SRegOffset(start_vreg + i), reg, op_size); + StoreBaseDisp(TargetReg(kSp), SRegOffset(start_vreg + i), reg, op_size, kNotVolatile); if (reg.Is64Bit()) { if (SRegOffset(start_vreg + i) + 4 != SRegOffset(start_vreg + i + 1)) { LOG(FATAL) << "64 bit value stored in non-consecutive 4 bytes slots"; @@ -1057,14 +1057,14 @@ int Arm64Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, loc = UpdateLocWide(loc); if (loc.location == kLocPhysReg) { ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); - StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k64); + StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k64, kNotVolatile); } next_arg += 2; } else { loc = UpdateLoc(loc); if (loc.location == kLocPhysReg) { ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); - StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k32); + StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k32, kNotVolatile); } next_arg++; } @@ -1122,18 +1122,27 @@ int Arm64Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); if (rl_arg.wide) { if (rl_arg.location == kLocPhysReg) { - StoreBaseDisp(TargetReg(kSp), out_offset, rl_arg.reg, k64); + StoreBaseDisp(TargetReg(kSp), out_offset, rl_arg.reg, k64, kNotVolatile); } else { LoadValueDirectWideFixed(rl_arg, regWide); - StoreBaseDisp(TargetReg(kSp), out_offset, regWide, k64); + StoreBaseDisp(TargetReg(kSp), out_offset, regWide, k64, kNotVolatile); } i++; } else { if (rl_arg.location == kLocPhysReg) { - StoreBaseDisp(TargetReg(kSp), out_offset, rl_arg.reg, k32); + if (rl_arg.ref) { + StoreRefDisp(TargetReg(kSp), out_offset, rl_arg.reg, kNotVolatile); + } else { + StoreBaseDisp(TargetReg(kSp), out_offset, rl_arg.reg, k32, kNotVolatile); + } } else { - LoadValueDirectFixed(rl_arg, regSingle); - StoreBaseDisp(TargetReg(kSp), out_offset, regSingle, k32); + if (rl_arg.ref) { + LoadValueDirectFixed(rl_arg, regSingle); + StoreRefDisp(TargetReg(kSp), out_offset, regSingle, kNotVolatile); + } else { + LoadValueDirectFixed(rl_arg, As32BitReg(regSingle)); + StoreBaseDisp(TargetReg(kSp), out_offset, As32BitReg(regSingle), k32, kNotVolatile); + } } } } diff --git a/compiler/dex/quick/arm64/utility_arm64.cc b/compiler/dex/quick/arm64/utility_arm64.cc index 37edbafa36..c8d45c637a 100644 --- a/compiler/dex/quick/arm64/utility_arm64.cc +++ b/compiler/dex/quick/arm64/utility_arm64.cc @@ -893,9 +893,7 @@ LIR* Arm64Mir2Lir::LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegSto ArmOpcode opcode = kA64Brk1d; DCHECK(r_base.Is64Bit()); // TODO: need a cleaner handling of index registers here and throughout. - if (r_index.Is32Bit()) { - r_index = As64BitReg(r_index); - } + r_index = Check32BitReg(r_index); if (r_dest.IsFloat()) { if (r_dest.IsDouble()) { @@ -918,12 +916,14 @@ LIR* Arm64Mir2Lir::LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegSto case kDouble: case kWord: case k64: + r_dest = Check64BitReg(r_dest); opcode = WIDE(kA64Ldr4rXxG); expected_scale = 3; break; case kSingle: case k32: case kReference: + r_dest = Check32BitReg(r_dest); opcode = kA64Ldr4rXxG; expected_scale = 2; break; @@ -959,6 +959,10 @@ LIR* Arm64Mir2Lir::LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegSto return load; } +LIR* Arm64Mir2Lir::LoadRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest) { + return LoadBaseIndexed(r_base, r_index, As32BitReg(r_dest), 2, kReference); +} + LIR* Arm64Mir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale, OpSize size) { LIR* store; @@ -966,9 +970,7 @@ LIR* Arm64Mir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegSt ArmOpcode opcode = kA64Brk1d; DCHECK(r_base.Is64Bit()); // TODO: need a cleaner handling of index registers here and throughout. - if (r_index.Is32Bit()) { - r_index = As64BitReg(r_index); - } + r_index = Check32BitReg(r_index); if (r_src.IsFloat()) { if (r_src.IsDouble()) { @@ -991,12 +993,14 @@ LIR* Arm64Mir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegSt case kDouble: // Intentional fall-trough. case kWord: // Intentional fall-trough. case k64: + r_src = Check64BitReg(r_src); opcode = WIDE(kA64Str4rXxG); expected_scale = 3; break; case kSingle: // Intentional fall-trough. case k32: // Intentional fall-trough. case kReference: + r_src = Check32BitReg(r_src); opcode = kA64Str4rXxG; expected_scale = 2; break; @@ -1026,6 +1030,10 @@ LIR* Arm64Mir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegSt return store; } +LIR* Arm64Mir2Lir::StoreRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src) { + return StoreBaseIndexed(r_base, r_index, As32BitReg(r_src), 2, kReference); +} + /* * Load value from base + displacement. Optionally perform null check * on base (which must have an associated s_reg and MIR). If not @@ -1042,6 +1050,7 @@ LIR* Arm64Mir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStor case kDouble: // Intentional fall-through. case kWord: // Intentional fall-through. case k64: + r_dest = Check64BitReg(r_dest); scale = 3; if (r_dest.IsFloat()) { DCHECK(r_dest.IsDouble()); @@ -1055,6 +1064,7 @@ LIR* Arm64Mir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStor case kSingle: // Intentional fall-through. case k32: // Intentional fall-trough. case kReference: + r_dest = Check32BitReg(r_dest); scale = 2; if (r_dest.IsFloat()) { DCHECK(r_dest.IsSingle()); @@ -1106,18 +1116,27 @@ LIR* Arm64Mir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStor return load; } -LIR* Arm64Mir2Lir::LoadBaseDispVolatile(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size) { +LIR* Arm64Mir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, + OpSize size, VolatileKind is_volatile) { // LoadBaseDisp() will emit correct insn for atomic load on arm64 // assuming r_dest is correctly prepared using RegClassForFieldLoadStore(). - return LoadBaseDisp(r_base, displacement, r_dest, size); -} -LIR* Arm64Mir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size) { - return LoadBaseDispBody(r_base, displacement, r_dest, size); + LIR* load = LoadBaseDispBody(r_base, displacement, r_dest, size); + + if (UNLIKELY(is_volatile == kVolatile)) { + // Without context sensitive analysis, we must issue the most conservative barriers. + // In this case, either a load or store may follow so we issue both barriers. + GenMemBarrier(kLoadLoad); + GenMemBarrier(kLoadStore); + } + + return load; } +LIR* Arm64Mir2Lir::LoadRefDisp(RegStorage r_base, int displacement, RegStorage r_dest, + VolatileKind is_volatile) { + return LoadBaseDisp(r_base, displacement, As32BitReg(r_dest), kReference, is_volatile); +} LIR* Arm64Mir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegStorage r_src, OpSize size) { @@ -1130,6 +1149,7 @@ LIR* Arm64Mir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegSto case kDouble: // Intentional fall-through. case kWord: // Intentional fall-through. case k64: + r_src = Check64BitReg(r_src); scale = 3; if (r_src.IsFloat()) { DCHECK(r_src.IsDouble()); @@ -1143,6 +1163,7 @@ LIR* Arm64Mir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegSto case kSingle: // Intentional fall-through. case k32: // Intentional fall-trough. case kReference: + r_src = Check32BitReg(r_src); scale = 2; if (r_src.IsFloat()) { DCHECK(r_src.IsSingle()); @@ -1188,16 +1209,29 @@ LIR* Arm64Mir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegSto return store; } -LIR* Arm64Mir2Lir::StoreBaseDispVolatile(RegStorage r_base, int displacement, RegStorage r_src, - OpSize size) { +LIR* Arm64Mir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, + OpSize size, VolatileKind is_volatile) { + if (UNLIKELY(is_volatile == kVolatile)) { + // There might have been a store before this volatile one so insert StoreStore barrier. + GenMemBarrier(kStoreStore); + } + // StoreBaseDisp() will emit correct insn for atomic store on arm64 // assuming r_dest is correctly prepared using RegClassForFieldLoadStore(). - return StoreBaseDisp(r_base, displacement, r_src, size); + + LIR* store = StoreBaseDispBody(r_base, displacement, r_src, size); + + if (UNLIKELY(is_volatile == kVolatile)) { + // A load might follow the volatile store so insert a StoreLoad barrier. + GenMemBarrier(kStoreLoad); + } + + return store; } -LIR* Arm64Mir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, - OpSize size) { - return StoreBaseDispBody(r_base, displacement, r_src, size); +LIR* Arm64Mir2Lir::StoreRefDisp(RegStorage r_base, int displacement, RegStorage r_src, + VolatileKind is_volatile) { + return StoreBaseDisp(r_base, displacement, As32BitReg(r_src), kReference, is_volatile); } LIR* Arm64Mir2Lir::OpFpRegCopy(RegStorage r_dest, RegStorage r_src) { diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index ec0fb43571..f31b670164 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -79,6 +79,20 @@ void Mir2Lir::MarkSafepointPC(LIR* inst) { DCHECK(safepoint_pc->u.m.def_mask->Equals(kEncodeAll)); } +void Mir2Lir::MarkSafepointPCAfter(LIR* after) { + DCHECK(!after->flags.use_def_invalid); + after->u.m.def_mask = &kEncodeAll; + // As NewLIR0 uses Append, we need to create the LIR by hand. + LIR* safepoint_pc = RawLIR(current_dalvik_offset_, kPseudoSafepointPC); + if (after->next == nullptr) { + DCHECK_EQ(after, last_lir_insn_); + AppendLIR(safepoint_pc); + } else { + InsertLIRAfter(after, safepoint_pc); + } + DCHECK(safepoint_pc->u.m.def_mask->Equals(kEncodeAll)); +} + /* Remove a LIR from the list. */ void Mir2Lir::UnlinkLIR(LIR* lir) { if (UNLIKELY(lir == first_lir_insn_)) { @@ -1112,7 +1126,7 @@ void Mir2Lir::InsertLIRBefore(LIR* current_lir, LIR* new_lir) { /* * Insert an LIR instruction after the current instruction, which cannot be the - * first instruction. + * last instruction. * * current_lir -> new_lir -> old_next */ diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index e36b592c74..b00cbeb61d 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -196,6 +196,15 @@ void Mir2Lir::MarkPossibleNullPointerException(int opt_flags) { } } +void Mir2Lir::MarkPossibleNullPointerExceptionAfter(int opt_flags, LIR* after) { + if (!cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { + if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && (opt_flags & MIR_IGNORE_NULL_CHECK)) { + return; + } + MarkSafepointPCAfter(after); + } +} + void Mir2Lir::MarkPossibleStackOverflowException() { if (!cu_->compiler_driver->GetCompilerOptions().GetExplicitStackOverflowChecks()) { MarkSafepointPC(last_lir_insn_); @@ -506,7 +515,7 @@ void Mir2Lir::GenFilledNewArray(CallInfo* info) { for (int i = 0; i < elems; i++) { RegLocation rl_arg = LoadValue(info->args[i], kCoreReg); Store32Disp(TargetReg(kRet0), - mirror::Array::DataOffset(component_size).Int32Value() + i * 4, rl_arg.reg); + mirror::Array::DataOffset(component_size).Int32Value() + i * 4, rl_arg.reg); // If the LoadValue caused a temp to be allocated, free it if (IsTemp(rl_arg.reg)) { FreeTemp(rl_arg.reg); @@ -575,7 +584,8 @@ void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, bool is_long_or_double, // Fast path, static storage base is this method's class RegLocation rl_method = LoadCurrMethod(); r_base = AllocTempRef(); - LoadRefDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), r_base); + LoadRefDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), r_base, + kNotVolatile); if (IsTemp(rl_method.reg)) { FreeTemp(rl_method.reg); } @@ -592,9 +602,10 @@ void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, bool is_long_or_double, LoadCurrMethodDirect(r_method); r_base = TargetReg(kArg0); LockTemp(r_base); - LoadRefDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base); + LoadRefDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base, + kNotVolatile); int32_t offset_of_field = ObjArray::OffsetOfElement(field_info.StorageIndex()).Int32Value(); - LoadRefDisp(r_base, offset_of_field, r_base); + LoadRefDisp(r_base, offset_of_field, r_base, kNotVolatile); // r_base now points at static storage (Class*) or NULL if the type is not yet resolved. if (!field_info.IsInitialized() && (mir->optimization_flags & MIR_IGNORE_CLINIT_CHECK) == 0) { @@ -626,14 +637,12 @@ void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, bool is_long_or_double, } else { rl_src = LoadValue(rl_src, reg_class); } - if (field_info.IsVolatile()) { - // There might have been a store before this volatile one so insert StoreStore barrier. - GenMemBarrier(kStoreStore); - StoreBaseDispVolatile(r_base, field_info.FieldOffset().Int32Value(), rl_src.reg, store_size); - // A load might follow the volatile store so insert a StoreLoad barrier. - GenMemBarrier(kStoreLoad); + if (is_object) { + StoreRefDisp(r_base, field_info.FieldOffset().Int32Value(), rl_src.reg, + field_info.IsVolatile() ? kVolatile : kNotVolatile); } else { - StoreBaseDisp(r_base, field_info.FieldOffset().Int32Value(), rl_src.reg, store_size); + StoreBaseDisp(r_base, field_info.FieldOffset().Int32Value(), rl_src.reg, store_size, + field_info.IsVolatile() ? kVolatile : kNotVolatile); } if (is_object && !mir_graph_->IsConstantNullRef(rl_src)) { MarkGCCard(rl_src.reg, r_base); @@ -672,7 +681,8 @@ void Mir2Lir::GenSget(MIR* mir, RegLocation rl_dest, // Fast path, static storage base is this method's class RegLocation rl_method = LoadCurrMethod(); r_base = AllocTempRef(); - LoadRefDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), r_base); + LoadRefDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), r_base, + kNotVolatile); } else { // Medium path, static storage base in a different class which requires checks that the other // class is initialized @@ -685,9 +695,10 @@ void Mir2Lir::GenSget(MIR* mir, RegLocation rl_dest, LoadCurrMethodDirect(r_method); r_base = TargetReg(kArg0); LockTemp(r_base); - LoadRefDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base); + LoadRefDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base, + kNotVolatile); int32_t offset_of_field = ObjArray::OffsetOfElement(field_info.StorageIndex()).Int32Value(); - LoadRefDisp(r_base, offset_of_field, r_base); + LoadRefDisp(r_base, offset_of_field, r_base, kNotVolatile); // r_base now points at static storage (Class*) or NULL if the type is not yet resolved. if (!field_info.IsInitialized() && (mir->optimization_flags & MIR_IGNORE_CLINIT_CHECK) == 0) { @@ -717,14 +728,12 @@ void Mir2Lir::GenSget(MIR* mir, RegLocation rl_dest, RegLocation rl_result = EvalLoc(rl_dest, reg_class, true); int field_offset = field_info.FieldOffset().Int32Value(); - if (field_info.IsVolatile()) { - LoadBaseDispVolatile(r_base, field_offset, rl_result.reg, load_size); - // Without context sensitive analysis, we must issue the most conservative barriers. - // In this case, either a load or store may follow so we issue both barriers. - GenMemBarrier(kLoadLoad); - GenMemBarrier(kLoadStore); + if (is_object) { + LoadRefDisp(r_base, field_offset, rl_result.reg, field_info.IsVolatile() ? kVolatile : + kNotVolatile); } else { - LoadBaseDisp(r_base, field_offset, rl_result.reg, load_size); + LoadBaseDisp(r_base, field_offset, rl_result.reg, load_size, field_info.IsVolatile() ? + kVolatile : kNotVolatile); } FreeTemp(r_base); @@ -785,17 +794,15 @@ void Mir2Lir::GenIGet(MIR* mir, int opt_flags, OpSize size, GenNullCheck(rl_obj.reg, opt_flags); RegLocation rl_result = EvalLoc(rl_dest, reg_class, true); int field_offset = field_info.FieldOffset().Int32Value(); - if (field_info.IsVolatile()) { - LoadBaseDispVolatile(rl_obj.reg, field_offset, rl_result.reg, load_size); - MarkPossibleNullPointerException(opt_flags); - // Without context sensitive analysis, we must issue the most conservative barriers. - // In this case, either a load or store may follow so we issue both barriers. - GenMemBarrier(kLoadLoad); - GenMemBarrier(kLoadStore); + LIR* load_lir; + if (is_object) { + load_lir = LoadRefDisp(rl_obj.reg, field_offset, rl_result.reg, field_info.IsVolatile() ? + kVolatile : kNotVolatile); } else { - LoadBaseDisp(rl_obj.reg, field_offset, rl_result.reg, load_size); - MarkPossibleNullPointerException(opt_flags); + load_lir = LoadBaseDisp(rl_obj.reg, field_offset, rl_result.reg, load_size, + field_info.IsVolatile() ? kVolatile : kNotVolatile); } + MarkPossibleNullPointerExceptionAfter(opt_flags, load_lir); if (is_long_or_double) { StoreValueWide(rl_dest, rl_result); } else { @@ -847,17 +854,15 @@ void Mir2Lir::GenIPut(MIR* mir, int opt_flags, OpSize size, } GenNullCheck(rl_obj.reg, opt_flags); int field_offset = field_info.FieldOffset().Int32Value(); - if (field_info.IsVolatile()) { - // There might have been a store before this volatile one so insert StoreStore barrier. - GenMemBarrier(kStoreStore); - StoreBaseDispVolatile(rl_obj.reg, field_offset, rl_src.reg, store_size); - MarkPossibleNullPointerException(opt_flags); - // A load might follow the volatile store so insert a StoreLoad barrier. - GenMemBarrier(kStoreLoad); + LIR* store; + if (is_object) { + store = StoreRefDisp(rl_obj.reg, field_offset, rl_src.reg, field_info.IsVolatile() ? + kVolatile : kNotVolatile); } else { - StoreBaseDisp(rl_obj.reg, field_offset, rl_src.reg, store_size); - MarkPossibleNullPointerException(opt_flags); + store = StoreBaseDisp(rl_obj.reg, field_offset, rl_src.reg, store_size, + field_info.IsVolatile() ? kVolatile : kNotVolatile); } + MarkPossibleNullPointerExceptionAfter(opt_flags, store); if (is_object && !mir_graph_->IsConstantNullRef(rl_src)) { MarkGCCard(rl_src.reg, rl_obj.reg); } @@ -916,9 +921,9 @@ void Mir2Lir::GenConstClass(uint32_t type_idx, RegLocation rl_dest) { // We're don't need access checks, load type from dex cache int32_t dex_cache_offset = mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(); - LoadRefDisp(rl_method.reg, dex_cache_offset, res_reg); + LoadRefDisp(rl_method.reg, dex_cache_offset, res_reg, kNotVolatile); int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value(); - LoadRefDisp(res_reg, offset_of_type, rl_result.reg); + LoadRefDisp(res_reg, offset_of_type, rl_result.reg, kNotVolatile); if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file, type_idx) || SLOW_TYPE_PATH) { // Slow path, at runtime test if type is null and if so initialize @@ -989,10 +994,10 @@ void Mir2Lir::GenConstString(uint32_t string_idx, RegLocation rl_dest) { LoadCurrMethodDirect(r_method); } LoadRefDisp(r_method, mirror::ArtMethod::DexCacheStringsOffset().Int32Value(), - TargetReg(kArg0)); + TargetReg(kArg0), kNotVolatile); // Might call out to helper, which will return resolved string in kRet0 - LoadRefDisp(TargetReg(kArg0), offset_of_string, TargetReg(kRet0)); + LoadRefDisp(TargetReg(kArg0), offset_of_string, TargetReg(kRet0), kNotVolatile); LIR* fromfast = OpCmpImmBranch(kCondEq, TargetReg(kRet0), 0, NULL); LIR* cont = NewLIR0(kPseudoTargetLabel); @@ -1031,8 +1036,9 @@ void Mir2Lir::GenConstString(uint32_t string_idx, RegLocation rl_dest) { RegLocation rl_method = LoadCurrMethod(); RegStorage res_reg = AllocTempRef(); RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true); - LoadRefDisp(rl_method.reg, mirror::ArtMethod::DexCacheStringsOffset().Int32Value(), res_reg); - LoadRefDisp(res_reg, offset_of_string, rl_result.reg); + LoadRefDisp(rl_method.reg, mirror::ArtMethod::DexCacheStringsOffset().Int32Value(), res_reg, + kNotVolatile); + LoadRefDisp(res_reg, offset_of_string, rl_result.reg, kNotVolatile); StoreValue(rl_dest, rl_result); } } @@ -1133,14 +1139,17 @@ void Mir2Lir::GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx, Re LoadCurrMethodDirect(check_class); if (use_declaring_class) { - LoadRefDisp(check_class, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), check_class); - LoadRefDisp(object.reg, mirror::Object::ClassOffset().Int32Value(), object_class); + LoadRefDisp(check_class, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), check_class, + kNotVolatile); + LoadRefDisp(object.reg, mirror::Object::ClassOffset().Int32Value(), object_class, + kNotVolatile); } else { LoadRefDisp(check_class, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), - check_class); - LoadRefDisp(object.reg, mirror::Object::ClassOffset().Int32Value(), object_class); + check_class, kNotVolatile); + LoadRefDisp(object.reg, mirror::Object::ClassOffset().Int32Value(), object_class, + kNotVolatile); int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value(); - LoadRefDisp(check_class, offset_of_type, check_class); + LoadRefDisp(check_class, offset_of_type, check_class, kNotVolatile); } LIR* ne_branchover = NULL; @@ -1196,14 +1205,14 @@ void Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_know } else if (use_declaring_class) { LoadValueDirectFixed(rl_src, TargetReg(kArg0)); // kArg0 <= ref LoadRefDisp(TargetReg(kArg1), mirror::ArtMethod::DeclaringClassOffset().Int32Value(), - class_reg); + class_reg, kNotVolatile); } else { // Load dex cache entry into class_reg (kArg2) LoadValueDirectFixed(rl_src, TargetReg(kArg0)); // kArg0 <= ref LoadRefDisp(TargetReg(kArg1), mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), - class_reg); + class_reg, kNotVolatile); int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value(); - LoadRefDisp(class_reg, offset_of_type, class_reg); + LoadRefDisp(class_reg, offset_of_type, class_reg, kNotVolatile); if (!can_assume_type_is_in_dex_cache) { // Need to test presence of type in dex cache at runtime LIR* hop_branch = OpCmpImmBranch(kCondNe, class_reg, 0, NULL); @@ -1231,7 +1240,8 @@ void Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_know /* load object->klass_ */ DCHECK_EQ(mirror::Object::ClassOffset().Int32Value(), 0); - LoadRefDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1)); + LoadRefDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1), + kNotVolatile); /* kArg0 is ref, kArg1 is ref->klass_, kArg2 is class */ LIR* branchover = NULL; if (type_known_final) { @@ -1344,13 +1354,13 @@ void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_ OpRegCopy(class_reg, TargetReg(kRet0)); // Align usage with fast path } else if (use_declaring_class) { LoadRefDisp(TargetReg(kArg1), mirror::ArtMethod::DeclaringClassOffset().Int32Value(), - class_reg); + class_reg, kNotVolatile); } else { // Load dex cache entry into class_reg (kArg2) LoadRefDisp(TargetReg(kArg1), mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), - class_reg); + class_reg, kNotVolatile); int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value(); - LoadRefDisp(class_reg, offset_of_type, class_reg); + LoadRefDisp(class_reg, offset_of_type, class_reg, kNotVolatile); if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file, type_idx)) { // Need to test presence of type in dex cache at runtime LIR* hop_branch = OpCmpImmBranch(kCondEq, class_reg, 0, NULL); @@ -1405,7 +1415,7 @@ void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_ if (load_) { m2l_->LoadRefDisp(m2l_->TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), - m2l_->TargetReg(kArg1)); + m2l_->TargetReg(kArg1), kNotVolatile); } if (m2l_->cu_->target64) { m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(8, pCheckCast), m2l_->TargetReg(kArg2), @@ -1436,7 +1446,8 @@ void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_ LIR* branch1 = OpCmpImmBranch(kCondEq, TargetReg(kArg0), 0, NULL); /* load object->klass_ */ DCHECK_EQ(mirror::Object::ClassOffset().Int32Value(), 0); - LoadRefDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1)); + LoadRefDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1), + kNotVolatile); LIR* branch2 = OpCmpBranch(kCondNe, TargetReg(kArg1), class_reg, NULL); LIR* cont = NewLIR0(kPseudoTargetLabel); diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index b3fac77c14..008ebfbd71 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -363,20 +363,27 @@ void Mir2Lir::CallRuntimeHelperRegLocationRegLocation(ThreadOffset<pointer_size> INSTANTIATE(void Mir2Lir::CallRuntimeHelperRegLocationRegLocation, RegLocation arg0, RegLocation arg1, bool safepoint_pc) +// TODO: This is a hack! Reshape the two macros into functions and move them to a better place. +#define IsSameReg(r1, r2) \ + (GetRegInfo(r1)->Master()->GetReg().GetReg() == GetRegInfo(r2)->Master()->GetReg().GetReg()) +#define TargetArgReg(arg, is_wide) \ + (GetRegInfo(TargetReg(arg))->FindMatchingView( \ + (is_wide) ? RegisterInfo::k64SoloStorageMask : RegisterInfo::k32SoloStorageMask)->GetReg()) + void Mir2Lir::CopyToArgumentRegs(RegStorage arg0, RegStorage arg1) { - if (arg1.GetReg() == TargetReg(kArg0).GetReg()) { - if (arg0.GetReg() == TargetReg(kArg1).GetReg()) { + if (IsSameReg(arg1, TargetReg(kArg0))) { + if (IsSameReg(arg0, TargetReg(kArg1))) { // Swap kArg0 and kArg1 with kArg2 as temp. - OpRegCopy(TargetReg(kArg2), arg1); - OpRegCopy(TargetReg(kArg0), arg0); - OpRegCopy(TargetReg(kArg1), TargetReg(kArg2)); + OpRegCopy(TargetArgReg(kArg2, arg1.Is64Bit()), arg1); + OpRegCopy(TargetArgReg(kArg0, arg0.Is64Bit()), arg0); + OpRegCopy(TargetArgReg(kArg1, arg1.Is64Bit()), TargetReg(kArg2)); } else { - OpRegCopy(TargetReg(kArg1), arg1); - OpRegCopy(TargetReg(kArg0), arg0); + OpRegCopy(TargetArgReg(kArg1, arg1.Is64Bit()), arg1); + OpRegCopy(TargetArgReg(kArg0, arg0.Is64Bit()), arg0); } } else { - OpRegCopy(TargetReg(kArg0), arg0); - OpRegCopy(TargetReg(kArg1), arg1); + OpRegCopy(TargetArgReg(kArg0, arg0.Is64Bit()), arg0); + OpRegCopy(TargetArgReg(kArg1, arg1.Is64Bit()), arg1); } } @@ -494,7 +501,7 @@ void Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) { StoreValue(rl_method, rl_src); // If Method* has been promoted, explicitly flush if (rl_method.location == kLocPhysReg) { - StoreRefDisp(TargetReg(kSp), 0, TargetReg(kArg0)); + StoreRefDisp(TargetReg(kSp), 0, TargetReg(kArg0), kNotVolatile); } if (cu_->num_ins == 0) { @@ -609,7 +616,8 @@ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, case 1: // Get method->dex_cache_resolved_methods_ cg->LoadRefDisp(cg->TargetReg(kArg0), mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(), - cg->TargetReg(kArg0)); + cg->TargetReg(kArg0), + kNotVolatile); // Set up direct code if known. if (direct_code != 0) { if (direct_code != static_cast<uintptr_t>(-1)) { @@ -624,7 +632,8 @@ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, CHECK_EQ(cu->dex_file, target_method.dex_file); cg->LoadRefDisp(cg->TargetReg(kArg0), ObjArray::OffsetOfElement(target_method.dex_method_index).Int32Value(), - cg->TargetReg(kArg0)); + cg->TargetReg(kArg0), + kNotVolatile); break; case 3: // Grab the code from the method* if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) { @@ -669,17 +678,20 @@ static int NextVCallInsn(CompilationUnit* cu, CallInfo* info, cg->GenNullCheck(cg->TargetReg(kArg1), info->opt_flags); // get this->klass_ [use kArg1, set kInvokeTgt] cg->LoadRefDisp(cg->TargetReg(kArg1), mirror::Object::ClassOffset().Int32Value(), - cg->TargetReg(kInvokeTgt)); + cg->TargetReg(kInvokeTgt), + kNotVolatile); cg->MarkPossibleNullPointerException(info->opt_flags); break; case 2: // Get this->klass_->vtable [usr kInvokeTgt, set kInvokeTgt] cg->LoadRefDisp(cg->TargetReg(kInvokeTgt), mirror::Class::VTableOffset().Int32Value(), - cg->TargetReg(kInvokeTgt)); + cg->TargetReg(kInvokeTgt), + kNotVolatile); break; case 3: // Get target method [use kInvokeTgt, set kArg0] cg->LoadRefDisp(cg->TargetReg(kInvokeTgt), ObjArray::OffsetOfElement(method_idx).Int32Value(), - cg->TargetReg(kArg0)); + cg->TargetReg(kArg0), + kNotVolatile); break; case 4: // Get the compiled code address [uses kArg0, sets kInvokeTgt] if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) { @@ -724,19 +736,22 @@ static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state, cg->GenNullCheck(cg->TargetReg(kArg1), info->opt_flags); // Get this->klass_ [use kArg1, set kInvokeTgt] cg->LoadRefDisp(cg->TargetReg(kArg1), mirror::Object::ClassOffset().Int32Value(), - cg->TargetReg(kInvokeTgt)); + cg->TargetReg(kInvokeTgt), + kNotVolatile); cg->MarkPossibleNullPointerException(info->opt_flags); break; case 3: // Get this->klass_->imtable [use kInvokeTgt, set kInvokeTgt] // NOTE: native pointer. cg->LoadRefDisp(cg->TargetReg(kInvokeTgt), mirror::Class::ImTableOffset().Int32Value(), - cg->TargetReg(kInvokeTgt)); + cg->TargetReg(kInvokeTgt), + kNotVolatile); break; case 4: // Get target method [use kInvokeTgt, set kArg0] // NOTE: native pointer. cg->LoadRefDisp(cg->TargetReg(kInvokeTgt), ObjArray::OffsetOfElement(method_idx % ClassLinker::kImtSize).Int32Value(), - cg->TargetReg(kArg0)); + cg->TargetReg(kArg0), + kNotVolatile); break; case 5: // Get the compiled code address [use kArg0, set kInvokeTgt] if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) { @@ -960,7 +975,7 @@ int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info, { ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); if (rl_arg.wide) { - StoreBaseDisp(TargetReg(kSp), outs_offset, arg_reg, k64); + StoreBaseDisp(TargetReg(kSp), outs_offset, arg_reg, k64, kNotVolatile); next_use += 2; } else { Store32Disp(TargetReg(kSp), outs_offset, arg_reg); @@ -1030,7 +1045,7 @@ int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, loc = UpdateLocWide(loc); if ((next_arg >= 2) && (loc.location == kLocPhysReg)) { ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); - StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k64); + StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k64, kNotVolatile); } next_arg += 2; } else { @@ -1300,7 +1315,7 @@ bool Mir2Lir::GenInlinedCharAt(CallInfo* info) { reg_off = AllocTemp(); reg_ptr = AllocTempRef(); Load32Disp(rl_obj.reg, offset_offset, reg_off); - LoadRefDisp(rl_obj.reg, value_offset, reg_ptr); + LoadRefDisp(rl_obj.reg, value_offset, reg_ptr, kNotVolatile); } if (rl_idx.is_const) { OpRegImm(kOpAdd, reg_off, mir_graph_->ConstantValue(rl_idx.orig_sreg)); @@ -1665,7 +1680,7 @@ bool Mir2Lir::GenInlinedUnsafeGet(CallInfo* info, } else { RegStorage rl_temp_offset = AllocTemp(); OpRegRegReg(kOpAdd, rl_temp_offset, rl_object.reg, rl_offset.reg); - LoadBaseDisp(rl_temp_offset, 0, rl_result.reg, k64); + LoadBaseDisp(rl_temp_offset, 0, rl_result.reg, k64, kNotVolatile); FreeTemp(rl_temp_offset); } } else { @@ -1712,7 +1727,7 @@ bool Mir2Lir::GenInlinedUnsafePut(CallInfo* info, bool is_long, } else { RegStorage rl_temp_offset = AllocTemp(); OpRegRegReg(kOpAdd, rl_temp_offset, rl_object.reg, rl_offset.reg); - StoreBaseDisp(rl_temp_offset, 0, rl_value.reg, k64); + StoreBaseDisp(rl_temp_offset, 0, rl_value.reg, k64, kNotVolatile); FreeTemp(rl_temp_offset); } } else { diff --git a/compiler/dex/quick/gen_loadstore.cc b/compiler/dex/quick/gen_loadstore.cc index 6469d9c4f1..bfb77fc222 100644 --- a/compiler/dex/quick/gen_loadstore.cc +++ b/compiler/dex/quick/gen_loadstore.cc @@ -66,7 +66,7 @@ void Mir2Lir::Workaround7250540(RegLocation rl_dest, RegStorage zero_reg) { } else { // Lives in the frame, need to store. ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); - StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), temp_reg, k32); + StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), temp_reg, k32, kNotVolatile); } if (!zero_reg.Valid()) { FreeTemp(temp_reg); @@ -93,7 +93,7 @@ void Mir2Lir::LoadValueDirect(RegLocation rl_src, RegStorage r_dest) { (rl_src.location == kLocCompilerTemp)); ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); if (rl_src.ref) { - LoadRefDisp(TargetReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest); + LoadRefDisp(TargetReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest, kNotVolatile); } else { Load32Disp(TargetReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest); } @@ -126,7 +126,7 @@ void Mir2Lir::LoadValueDirectWide(RegLocation rl_src, RegStorage r_dest) { DCHECK((rl_src.location == kLocDalvikFrame) || (rl_src.location == kLocCompilerTemp)); ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); - LoadBaseDisp(TargetReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest, k64); + LoadBaseDisp(TargetReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest, k64, kNotVolatile); } } @@ -214,7 +214,11 @@ void Mir2Lir::StoreValue(RegLocation rl_dest, RegLocation rl_src) { if (IsDirty(rl_dest.reg) && LiveOut(rl_dest.s_reg_low)) { def_start = last_lir_insn_; ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); - Store32Disp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg); + if (rl_dest.ref) { + StoreRefDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg, kNotVolatile); + } else { + Store32Disp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg); + } MarkClean(rl_dest); def_end = last_lir_insn_; if (!rl_dest.ref) { @@ -301,7 +305,7 @@ void Mir2Lir::StoreValueWide(RegLocation rl_dest, RegLocation rl_src) { DCHECK_EQ((mir_graph_->SRegToVReg(rl_dest.s_reg_low)+1), mir_graph_->SRegToVReg(GetSRegHi(rl_dest.s_reg_low))); ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); - StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg, k64); + StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg, k64, kNotVolatile); MarkClean(rl_dest); def_end = last_lir_insn_; MarkDefWide(rl_dest, def_start, def_end); @@ -365,7 +369,7 @@ void Mir2Lir::StoreFinalValueWide(RegLocation rl_dest, RegLocation rl_src) { DCHECK_EQ((mir_graph_->SRegToVReg(rl_dest.s_reg_low)+1), mir_graph_->SRegToVReg(GetSRegHi(rl_dest.s_reg_low))); ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); - StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg, k64); + StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg, k64, kNotVolatile); MarkClean(rl_dest); LIR *def_end = last_lir_insn_; MarkDefWide(rl_dest, def_start, def_end); diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc index e53105fc84..26ea6a8ec7 100644 --- a/compiler/dex/quick/mips/call_mips.cc +++ b/compiler/dex/quick/mips/call_mips.cc @@ -264,9 +264,9 @@ void MipsMir2Lir::GenMoveException(RegLocation rl_dest) { int ex_offset = Thread::ExceptionOffset<4>().Int32Value(); RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true); RegStorage reset_reg = AllocTempRef(); - LoadRefDisp(rs_rMIPS_SELF, ex_offset, rl_result.reg); + LoadRefDisp(rs_rMIPS_SELF, ex_offset, rl_result.reg, kNotVolatile); LoadConstant(reset_reg, 0); - StoreRefDisp(rs_rMIPS_SELF, ex_offset, reset_reg); + StoreRefDisp(rs_rMIPS_SELF, ex_offset, reset_reg, kNotVolatile); FreeTemp(reset_reg); StoreValue(rl_dest, rl_result); } diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h index 571adaccc1..c0ad9161f6 100644 --- a/compiler/dex/quick/mips/codegen_mips.h +++ b/compiler/dex/quick/mips/codegen_mips.h @@ -33,20 +33,16 @@ class MipsMir2Lir FINAL : public Mir2Lir { LIR* CheckSuspendUsingLoad() OVERRIDE; RegStorage LoadHelper(ThreadOffset<4> offset) OVERRIDE; RegStorage LoadHelper(ThreadOffset<8> offset) OVERRIDE; - LIR* LoadBaseDispVolatile(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size) OVERRIDE; LIR* LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size) OVERRIDE; + OpSize size, VolatileKind is_volatile) OVERRIDE; LIR* LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest, int scale, OpSize size) OVERRIDE; LIR* LoadBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale, int displacement, RegStorage r_dest, OpSize size) OVERRIDE; LIR* LoadConstantNoClobber(RegStorage r_dest, int value); LIR* LoadConstantWide(RegStorage r_dest, int64_t value); - LIR* StoreBaseDispVolatile(RegStorage r_base, int displacement, RegStorage r_src, - OpSize size) OVERRIDE; LIR* StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, - OpSize size) OVERRIDE; + OpSize size, VolatileKind is_volatile) OVERRIDE; LIR* StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale, OpSize size) OVERRIDE; LIR* StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale, int displacement, diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc index beaf6bb8ea..903a7709ca 100644 --- a/compiler/dex/quick/mips/int_mips.cc +++ b/compiler/dex/quick/mips/int_mips.cc @@ -294,7 +294,7 @@ bool MipsMir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) { RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); DCHECK(size == kSignedByte); - LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size); + LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, kNotVolatile); StoreValue(rl_dest, rl_result); return true; } @@ -310,7 +310,7 @@ bool MipsMir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) { RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); DCHECK(size == kSignedByte); RegLocation rl_value = LoadValue(rl_src_value, kCoreReg); - StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size); + StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size, kNotVolatile); return true; } @@ -524,7 +524,7 @@ void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, GenArrayBoundsCheck(rl_index.reg, reg_len); FreeTemp(reg_len); } - LoadBaseDisp(reg_ptr, 0, rl_result.reg, size); + LoadBaseDisp(reg_ptr, 0, rl_result.reg, size, kNotVolatile); FreeTemp(reg_ptr); StoreValueWide(rl_dest, rl_result); @@ -602,7 +602,7 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, FreeTemp(reg_len); } - StoreBaseDisp(reg_ptr, 0, rl_src.reg, size); + StoreBaseDisp(reg_ptr, 0, rl_src.reg, size, kNotVolatile); } else { rl_src = LoadValue(rl_src, reg_class); if (needs_range_check) { diff --git a/compiler/dex/quick/mips/utility_mips.cc b/compiler/dex/quick/mips/utility_mips.cc index 01b25f9291..b49f43617f 100644 --- a/compiler/dex/quick/mips/utility_mips.cc +++ b/compiler/dex/quick/mips/utility_mips.cc @@ -546,23 +546,31 @@ LIR* MipsMir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStora return load; } -LIR* MipsMir2Lir::LoadBaseDispVolatile(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size) { - DCHECK(size != k64 && size != kDouble); - return LoadBaseDisp(r_base, displacement, r_dest, size); -} - LIR* MipsMir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size) { + OpSize size, VolatileKind is_volatile) { + if (is_volatile == kVolatile) { + DCHECK(size != k64 && size != kDouble); + } + // TODO: base this on target. if (size == kWord) { size = k32; } + LIR* load; if (size == k64 || size == kDouble) { - return LoadBaseDispBody(r_base, displacement, r_dest.GetLow(), r_dest.GetHigh(), size); + load = LoadBaseDispBody(r_base, displacement, r_dest.GetLow(), r_dest.GetHigh(), size); } else { - return LoadBaseDispBody(r_base, displacement, r_dest, RegStorage::InvalidReg(), size); + load = LoadBaseDispBody(r_base, displacement, r_dest, RegStorage::InvalidReg(), size); } + + if (UNLIKELY(is_volatile == kVolatile)) { + // Without context sensitive analysis, we must issue the most conservative barriers. + // In this case, either a load or store may follow so we issue both barriers. + GenMemBarrier(kLoadLoad); + GenMemBarrier(kLoadStore); + } + + return load; } // FIXME: don't split r_dest into 2 containers. @@ -648,23 +656,31 @@ LIR* MipsMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, return res; } -LIR* MipsMir2Lir::StoreBaseDispVolatile(RegStorage r_base, int displacement, RegStorage r_src, - OpSize size) { - DCHECK(size != k64 && size != kDouble); - return StoreBaseDisp(r_base, displacement, r_src, size); -} - LIR* MipsMir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, - OpSize size) { + OpSize size, VolatileKind is_volatile) { + if (is_volatile == kVolatile) { + DCHECK(size != k64 && size != kDouble); + // There might have been a store before this volatile one so insert StoreStore barrier. + GenMemBarrier(kStoreStore); + } + // TODO: base this on target. if (size == kWord) { size = k32; } + LIR* store; if (size == k64 || size == kDouble) { - return StoreBaseDispBody(r_base, displacement, r_src.GetLow(), r_src.GetHigh(), size); + store = StoreBaseDispBody(r_base, displacement, r_src.GetLow(), r_src.GetHigh(), size); } else { - return StoreBaseDispBody(r_base, displacement, r_src, RegStorage::InvalidReg(), size); + store = StoreBaseDispBody(r_base, displacement, r_src, RegStorage::InvalidReg(), size); } + + if (UNLIKELY(is_volatile == kVolatile)) { + // A load might follow the volatile store so insert a StoreLoad barrier. + GenMemBarrier(kStoreLoad); + } + + return store; } LIR* MipsMir2Lir::OpThreadMem(OpKind op, ThreadOffset<4> thread_offset) { diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index 1fc416301c..5d68187d8b 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -92,7 +92,7 @@ RegStorage Mir2Lir::LoadArg(int in_position, RegisterClass reg_class, bool wide) if (!reg_arg.Valid()) { RegStorage new_reg = wide ? AllocTypedTempWide(false, reg_class) : AllocTypedTemp(false, reg_class); - LoadBaseDisp(TargetReg(kSp), offset, new_reg, wide ? k64 : k32); + LoadBaseDisp(TargetReg(kSp), offset, new_reg, wide ? k64 : k32, kNotVolatile); return new_reg; } else { // Check if we need to copy the arg to a different reg_class. @@ -120,7 +120,7 @@ RegStorage Mir2Lir::LoadArg(int in_position, RegisterClass reg_class, bool wide) // If the low part is not in a reg, we allocate a pair. Otherwise, we just load to high reg. if (!reg_arg_low.Valid()) { RegStorage new_regs = AllocTypedTempWide(false, reg_class); - LoadBaseDisp(TargetReg(kSp), offset, new_regs, k64); + LoadBaseDisp(TargetReg(kSp), offset, new_regs, k64, kNotVolatile); return new_regs; // The reg_class is OK, we can return. } else { // Assume that no ABI allows splitting a wide fp reg between a narrow fp reg and memory, @@ -193,7 +193,7 @@ void Mir2Lir::LoadArgDirect(int in_position, RegLocation rl_dest) { if (reg.Valid()) { OpRegCopy(rl_dest.reg, reg); } else { - LoadBaseDisp(TargetReg(kSp), offset, rl_dest.reg, k64); + LoadBaseDisp(TargetReg(kSp), offset, rl_dest.reg, k64, kNotVolatile); } return; } @@ -211,7 +211,7 @@ void Mir2Lir::LoadArgDirect(int in_position, RegLocation rl_dest) { OpRegCopy(rl_dest.reg.GetHigh(), reg_arg_high); Load32Disp(TargetReg(kSp), offset, rl_dest.reg.GetLow()); } else { - LoadBaseDisp(TargetReg(kSp), offset, rl_dest.reg, k64); + LoadBaseDisp(TargetReg(kSp), offset, rl_dest.reg, k64, kNotVolatile); } } } @@ -243,14 +243,11 @@ bool Mir2Lir::GenSpecialIGet(MIR* mir, const InlineMethod& special) { r_result = wide ? AllocTypedTempWide(rl_dest.fp, reg_class) : AllocTypedTemp(rl_dest.fp, reg_class); } - if (data.is_volatile) { - LoadBaseDispVolatile(reg_obj, data.field_offset, r_result, size); - // Without context sensitive analysis, we must issue the most conservative barriers. - // In this case, either a load or store may follow so we issue both barriers. - GenMemBarrier(kLoadLoad); - GenMemBarrier(kLoadStore); + if (ref) { + LoadRefDisp(reg_obj, data.field_offset, r_result, data.is_volatile ? kVolatile : kNotVolatile); } else { - LoadBaseDisp(reg_obj, data.field_offset, r_result, size); + LoadBaseDisp(reg_obj, data.field_offset, r_result, size, data.is_volatile ? kVolatile : + kNotVolatile); } if (r_result != rl_dest.reg) { if (wide) { @@ -288,14 +285,11 @@ bool Mir2Lir::GenSpecialIPut(MIR* mir, const InlineMethod& special) { RegStorage reg_obj = LoadArg(data.object_arg, kRefReg); RegisterClass reg_class = RegClassForFieldLoadStore(size, data.is_volatile); RegStorage reg_src = LoadArg(data.src_arg, reg_class, wide); - if (data.is_volatile) { - // There might have been a store before this volatile one so insert StoreStore barrier. - GenMemBarrier(kStoreStore); - StoreBaseDispVolatile(reg_obj, data.field_offset, reg_src, size); - // A load might follow the volatile store so insert a StoreLoad barrier. - GenMemBarrier(kStoreLoad); + if (ref) { + StoreRefDisp(reg_obj, data.field_offset, reg_src, data.is_volatile ? kVolatile : kNotVolatile); } else { - StoreBaseDisp(reg_obj, data.field_offset, reg_src, size); + StoreBaseDisp(reg_obj, data.field_offset, reg_src, size, data.is_volatile ? kVolatile : + kNotVolatile); } if (ref) { MarkGCCard(reg_src, reg_obj); diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index faed6ae139..cb0bb80fe6 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -663,6 +663,7 @@ class Mir2Lir : public Backend { virtual void Materialize(); virtual CompiledMethod* GetCompiledMethod(); void MarkSafepointPC(LIR* inst); + void MarkSafepointPCAfter(LIR* after); void SetupResourceMasks(LIR* lir); void SetMemRefType(LIR* lir, bool is_load, int mem_type); void AnnotateDalvikRegAccess(LIR* lir, int reg_id, bool is_load, bool is64bit); @@ -830,6 +831,7 @@ class Mir2Lir : public Backend { void GenArrayBoundsCheck(int32_t index, RegStorage length); LIR* GenNullCheck(RegStorage reg); void MarkPossibleNullPointerException(int opt_flags); + void MarkPossibleNullPointerExceptionAfter(int opt_flags, LIR* after); void MarkPossibleStackOverflowException(); void ForceImplicitNullCheck(RegStorage reg, int opt_flags); LIR* GenImmedCheck(ConditionCode c_code, RegStorage reg, int imm_val, ThrowKind kind); @@ -1007,15 +1009,20 @@ class Mir2Lir : public Backend { virtual LIR* LoadConstant(RegStorage r_dest, int value); // Natural word size. virtual LIR* LoadWordDisp(RegStorage r_base, int displacement, RegStorage r_dest) { - return LoadBaseDisp(r_base, displacement, r_dest, kWord); + return LoadBaseDisp(r_base, displacement, r_dest, kWord, kNotVolatile); } // Load 32 bits, regardless of target. virtual LIR* Load32Disp(RegStorage r_base, int displacement, RegStorage r_dest) { - return LoadBaseDisp(r_base, displacement, r_dest, k32); + return LoadBaseDisp(r_base, displacement, r_dest, k32, kNotVolatile); } // Load a reference at base + displacement and decompress into register. - virtual LIR* LoadRefDisp(RegStorage r_base, int displacement, RegStorage r_dest) { - return LoadBaseDisp(r_base, displacement, r_dest, kReference); + virtual LIR* LoadRefDisp(RegStorage r_base, int displacement, RegStorage r_dest, + VolatileKind is_volatile) { + return LoadBaseDisp(r_base, displacement, r_dest, kReference, is_volatile); + } + // Load a reference at base + index and decompress into register. + virtual LIR* LoadRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest) { + return LoadBaseIndexed(r_base, r_index, r_dest, 2, kReference); } // Load Dalvik value with 32-bit memory storage. If compressed object reference, decompress. virtual RegLocation LoadValue(RegLocation rl_src, RegisterClass op_kind); @@ -1033,15 +1040,20 @@ class Mir2Lir : public Backend { virtual void LoadValueDirectWideFixed(RegLocation rl_src, RegStorage r_dest); // Store an item of natural word size. virtual LIR* StoreWordDisp(RegStorage r_base, int displacement, RegStorage r_src) { - return StoreBaseDisp(r_base, displacement, r_src, kWord); + return StoreBaseDisp(r_base, displacement, r_src, kWord, kNotVolatile); } // Store an uncompressed reference into a compressed 32-bit container. - virtual LIR* StoreRefDisp(RegStorage r_base, int displacement, RegStorage r_src) { - return StoreBaseDisp(r_base, displacement, r_src, kReference); + virtual LIR* StoreRefDisp(RegStorage r_base, int displacement, RegStorage r_src, + VolatileKind is_volatile) { + return StoreBaseDisp(r_base, displacement, r_src, kReference, is_volatile); + } + // Store an uncompressed reference into a compressed 32-bit container by index. + virtual LIR* StoreRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src) { + return StoreBaseIndexed(r_base, r_index, r_src, 2, kReference); } // Store 32 bits, regardless of target. virtual LIR* Store32Disp(RegStorage r_base, int displacement, RegStorage r_src) { - return StoreBaseDisp(r_base, displacement, r_src, k32); + return StoreBaseDisp(r_base, displacement, r_src, k32, kNotVolatile); } /** @@ -1144,20 +1156,16 @@ class Mir2Lir : public Backend { virtual RegStorage LoadHelper(ThreadOffset<4> offset) = 0; virtual RegStorage LoadHelper(ThreadOffset<8> offset) = 0; - virtual LIR* LoadBaseDispVolatile(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size) = 0; virtual LIR* LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size) = 0; + OpSize size, VolatileKind is_volatile) = 0; virtual LIR* LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest, int scale, OpSize size) = 0; virtual LIR* LoadBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale, int displacement, RegStorage r_dest, OpSize size) = 0; virtual LIR* LoadConstantNoClobber(RegStorage r_dest, int value) = 0; virtual LIR* LoadConstantWide(RegStorage r_dest, int64_t value) = 0; - virtual LIR* StoreBaseDispVolatile(RegStorage r_base, int displacement, RegStorage r_src, - OpSize size) = 0; virtual LIR* StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, - OpSize size) = 0; + OpSize size, VolatileKind is_volatile) = 0; virtual LIR* StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale, OpSize size) = 0; virtual LIR* StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale, diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc index 5bb0ee04d4..60eebe4a25 100644 --- a/compiler/dex/quick/ralloc_util.cc +++ b/compiler/dex/quick/ralloc_util.cc @@ -735,7 +735,7 @@ void Mir2Lir::FlushRegWide(RegStorage reg) { } int v_reg = mir_graph_->SRegToVReg(info1->SReg()); ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); - StoreBaseDisp(TargetReg(kSp), VRegOffset(v_reg), reg, k64); + StoreBaseDisp(TargetReg(kSp), VRegOffset(v_reg), reg, k64, kNotVolatile); } } else { RegisterInfo* info = GetRegInfo(reg); @@ -743,7 +743,7 @@ void Mir2Lir::FlushRegWide(RegStorage reg) { info->SetIsDirty(false); int v_reg = mir_graph_->SRegToVReg(info->SReg()); ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); - StoreBaseDisp(TargetReg(kSp), VRegOffset(v_reg), reg, k64); + StoreBaseDisp(TargetReg(kSp), VRegOffset(v_reg), reg, k64, kNotVolatile); } } } @@ -755,7 +755,7 @@ void Mir2Lir::FlushReg(RegStorage reg) { info->SetIsDirty(false); int v_reg = mir_graph_->SRegToVReg(info->SReg()); ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); - StoreBaseDisp(TargetReg(kSp), VRegOffset(v_reg), reg, kWord); + StoreBaseDisp(TargetReg(kSp), VRegOffset(v_reg), reg, kWord, kNotVolatile); } } diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index 3f54798b7e..3f362f263e 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -325,11 +325,21 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, { kX86 ## opname ## RM, kRegMem, IS_LOAD | IS_TERTIARY_OP | reg_def | REG_USE1, { prefix, 0, 0x0F, opcode, 0, 0, 0, 0, false }, #opname "RM", "!0r,[!1r+!2d]" }, \ { kX86 ## opname ## RA, kRegArray, IS_LOAD | IS_QUIN_OP | reg_def | REG_USE12, { prefix, 0, 0x0F, opcode, 0, 0, 0, 0, false }, #opname "RA", "!0r,[!1r+!2r<<!3d+!4d]" } +#define EXT_0F_REX_NO_PREFIX_ENCODING_MAP(opname, opcode, reg_def) \ +{ kX86 ## opname ## RR, kRegReg, IS_BINARY_OP | reg_def | REG_USE1, { REX, 0x00, 0x0F, opcode, 0, 0, 0, 0, false }, #opname "RR", "!0r,!1r" }, \ +{ kX86 ## opname ## RM, kRegMem, IS_LOAD | IS_TERTIARY_OP | reg_def | REG_USE1, { REX, 0x00, 0x0F, opcode, 0, 0, 0, 0, false }, #opname "RM", "!0r,[!1r+!2d]" }, \ +{ kX86 ## opname ## RA, kRegArray, IS_LOAD | IS_QUIN_OP | reg_def | REG_USE12, { REX, 0x00, 0x0F, opcode, 0, 0, 0, 0, false }, #opname "RA", "!0r,[!1r+!2r<<!3d+!4d]" } + #define EXT_0F_REX_W_ENCODING_MAP(opname, prefix, opcode, reg_def) \ { kX86 ## opname ## RR, kRegReg, IS_BINARY_OP | reg_def | REG_USE1, { prefix, REX_W, 0x0F, opcode, 0, 0, 0, 0, false }, #opname "RR", "!0r,!1r" }, \ { kX86 ## opname ## RM, kRegMem, IS_LOAD | IS_TERTIARY_OP | reg_def | REG_USE1, { prefix, REX_W, 0x0F, opcode, 0, 0, 0, 0, false }, #opname "RM", "!0r,[!1r+!2d]" }, \ { kX86 ## opname ## RA, kRegArray, IS_LOAD | IS_QUIN_OP | reg_def | REG_USE12, { prefix, REX_W, 0x0F, opcode, 0, 0, 0, 0, false }, #opname "RA", "!0r,[!1r+!2r<<!3d+!4d]" } +#define EXT_0F_REX_W_NO_PREFIX_ENCODING_MAP(opname, opcode, reg_def) \ +{ kX86 ## opname ## RR, kRegReg, IS_BINARY_OP | reg_def | REG_USE1, { REX_W, 0x00, 0x0F, opcode, 0, 0, 0, 0, false }, #opname "RR", "!0r,!1r" }, \ +{ kX86 ## opname ## RM, kRegMem, IS_LOAD | IS_TERTIARY_OP | reg_def | REG_USE1, { REX_W, 0x00, 0x0F, opcode, 0, 0, 0, 0, false }, #opname "RM", "!0r,[!1r+!2d]" }, \ +{ kX86 ## opname ## RA, kRegArray, IS_LOAD | IS_QUIN_OP | reg_def | REG_USE12, { REX_W, 0x00, 0x0F, opcode, 0, 0, 0, 0, false }, #opname "RA", "!0r,[!1r+!2r<<!3d+!4d]" } + #define EXT_0F_ENCODING2_MAP(opname, prefix, opcode, opcode2, reg_def) \ { kX86 ## opname ## RR, kRegReg, IS_BINARY_OP | reg_def | REG_USE1, { prefix, 0, 0x0F, opcode, opcode2, 0, 0, 0, false }, #opname "RR", "!0r,!1r" }, \ { kX86 ## opname ## RM, kRegMem, IS_LOAD | IS_TERTIARY_OP | reg_def | REG_USE1, { prefix, 0, 0x0F, opcode, opcode2, 0, 0, 0, false }, #opname "RM", "!0r,[!1r+!2d]" }, \ @@ -413,11 +423,11 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, { kX86Fld64M, kMem, IS_LOAD | IS_UNARY_OP | REG_USE0 | USE_FP_STACK, { 0x0, 0, 0xDD, 0x00, 0, 0, 0, 0, false }, "Fld64M", "[!0r,!1d]" }, { kX86Fstp32M, kMem, IS_STORE | IS_UNARY_OP | REG_USE0 | USE_FP_STACK, { 0x0, 0, 0xD9, 0x00, 0, 3, 0, 0, false }, "Fstps32M", "[!0r,!1d]" }, { kX86Fstp64M, kMem, IS_STORE | IS_UNARY_OP | REG_USE0 | USE_FP_STACK, { 0x0, 0, 0xDD, 0x00, 0, 3, 0, 0, false }, "Fstpd64M", "[!0r,!1d]" }, - { kX86Fst32M, kMem, IS_STORE | IS_UNARY_OP | REG_USE0, { 0x0, 0, 0xD9, 0x00, 0, 2, 0, 0, false }, "Fsts32M", "[!0r,!1d]" }, - { kX86Fst64M, kMem, IS_STORE | IS_UNARY_OP | REG_USE0, { 0x0, 0, 0xDD, 0x00, 0, 2, 0, 0, false }, "Fstd64M", "[!0r,!1d]" }, + { kX86Fst32M, kMem, IS_STORE | IS_UNARY_OP | REG_USE0 | USE_FP_STACK, { 0x0, 0, 0xD9, 0x00, 0, 2, 0, 0, false }, "Fsts32M", "[!0r,!1d]" }, + { kX86Fst64M, kMem, IS_STORE | IS_UNARY_OP | REG_USE0 | USE_FP_STACK, { 0x0, 0, 0xDD, 0x00, 0, 2, 0, 0, false }, "Fstd64M", "[!0r,!1d]" }, { kX86Fprem, kNullary, NO_OPERAND | USE_FP_STACK, { 0xD9, 0, 0xF8, 0, 0, 0, 0, 0, false }, "Fprem64", "" }, { kX86Fucompp, kNullary, NO_OPERAND | USE_FP_STACK, { 0xDA, 0, 0xE9, 0, 0, 0, 0, 0, false }, "Fucompp", "" }, - { kX86Fstsw16R, kNullary, NO_OPERAND, { 0x9B, 0xDF, 0xE0, 0, 0, 0, 0, 0, false }, "Fstsw16R", "ax" }, + { kX86Fstsw16R, kNullary, NO_OPERAND | USE_FP_STACK, { 0x9B, 0xDF, 0xE0, 0, 0, 0, 0, 0, false }, "Fstsw16R", "ax" }, EXT_0F_ENCODING_MAP(Mova128, 0x66, 0x6F, REG_DEF0), { kX86Mova128MR, kMemReg, IS_STORE | IS_TERTIARY_OP | REG_USE02, { 0x66, 0, 0x0F, 0x6F, 0, 0, 0, 0, false }, "Mova128MR", "[!0r+!1d],!2r" }, @@ -481,6 +491,10 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, EXT_0F_ENCODING_MAP(Movzx16, 0x00, 0xB7, REG_DEF0), EXT_0F_ENCODING_MAP(Movsx8, 0x00, 0xBE, REG_DEF0), EXT_0F_ENCODING_MAP(Movsx16, 0x00, 0xBF, REG_DEF0), + EXT_0F_REX_NO_PREFIX_ENCODING_MAP(Movzx8q, 0xB6, REG_DEF0), + EXT_0F_REX_W_NO_PREFIX_ENCODING_MAP(Movzx16q, 0xB7, REG_DEF0), + EXT_0F_REX_NO_PREFIX_ENCODING_MAP(Movsx8q, 0xBE, REG_DEF0), + EXT_0F_REX_W_NO_PREFIX_ENCODING_MAP(Movsx16q, 0xBF, REG_DEF0), #undef EXT_0F_ENCODING_MAP { kX86Jcc8, kJcc, IS_BINARY_OP | IS_BRANCH | NEEDS_FIXUP | USES_CCODES, { 0, 0, 0x70, 0, 0, 0, 0, 0, false }, "Jcc8", "!1c !0t" }, @@ -827,7 +841,8 @@ void X86Mir2Lir::CheckValidByteRegister(const X86EncodingMap* entry, int32_t raw CHECK(strchr(entry->name, '8') != nullptr) << entry->name; } else { if (entry->skeleton.immediate_bytes != 1) { // Ignore ...I8 instructions. - if (!StartsWith(entry->name, "Movzx8") && !StartsWith(entry->name, "Movsx8")) { + if (!StartsWith(entry->name, "Movzx8") && !StartsWith(entry->name, "Movsx8") + && !StartsWith(entry->name, "Movzx8q") && !StartsWith(entry->name, "Movsx8q")) { CHECK(strchr(entry->name, '8') == nullptr) << entry->name; } } diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc index 28195aba36..425caec177 100644 --- a/compiler/dex/quick/x86/call_x86.cc +++ b/compiler/dex/quick/x86/call_x86.cc @@ -295,7 +295,8 @@ void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { setup_method_address_[0] = NewLIR1(kX86StartOfMethod, rs_rX86_ARG0.GetReg()); int displacement = SRegOffset(base_of_code_->s_reg_low); // Native pointer - must be natural word size. - setup_method_address_[1] = StoreBaseDisp(rs_rX86_SP, displacement, rs_rX86_ARG0, Gen64Bit() ? k64 : k32); + setup_method_address_[1] = StoreBaseDisp(rs_rX86_SP, displacement, rs_rX86_ARG0, + Gen64Bit() ? k64 : k32, kNotVolatile); } FreeTemp(rs_rX86_ARG0); diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index d482e58521..70382c746a 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -68,20 +68,16 @@ class X86Mir2Lir : public Mir2Lir { LIR* CheckSuspendUsingLoad() OVERRIDE; RegStorage LoadHelper(ThreadOffset<4> offset) OVERRIDE; RegStorage LoadHelper(ThreadOffset<8> offset) OVERRIDE; - LIR* LoadBaseDispVolatile(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size) OVERRIDE; LIR* LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size) OVERRIDE; + OpSize size, VolatileKind is_volatile) OVERRIDE; LIR* LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest, int scale, OpSize size) OVERRIDE; LIR* LoadBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale, int displacement, RegStorage r_dest, OpSize size) OVERRIDE; LIR* LoadConstantNoClobber(RegStorage r_dest, int value); LIR* LoadConstantWide(RegStorage r_dest, int64_t value); - LIR* StoreBaseDispVolatile(RegStorage r_base, int displacement, RegStorage r_src, - OpSize size) OVERRIDE; LIR* StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, - OpSize size) OVERRIDE; + OpSize size, VolatileKind is_volatile) OVERRIDE; LIR* StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale, OpSize size) OVERRIDE; LIR* StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale, int displacement, diff --git a/compiler/dex/quick/x86/fp_x86.cc b/compiler/dex/quick/x86/fp_x86.cc index 20bb7bf117..f854adb175 100644 --- a/compiler/dex/quick/x86/fp_x86.cc +++ b/compiler/dex/quick/x86/fp_x86.cc @@ -144,7 +144,7 @@ void X86Mir2Lir::GenLongToFP(RegLocation rl_dest, RegLocation rl_src, bool is_do } else { // It must have been register promoted if it is not a temp but is still in physical // register. Since we need it to be in memory to convert, we place it there now. - StoreBaseDisp(TargetReg(kSp), src_v_reg_offset, rl_src.reg, k64); + StoreBaseDisp(TargetReg(kSp), src_v_reg_offset, rl_src.reg, k64, kNotVolatile); } } @@ -178,7 +178,7 @@ void X86Mir2Lir::GenLongToFP(RegLocation rl_dest, RegLocation rl_src, bool is_do */ rl_result = EvalLoc(rl_dest, kFPReg, true); if (is_double) { - LoadBaseDisp(TargetReg(kSp), dest_v_reg_offset, rl_result.reg, k64); + LoadBaseDisp(TargetReg(kSp), dest_v_reg_offset, rl_result.reg, k64, kNotVolatile); StoreFinalValueWide(rl_dest, rl_result); } else { @@ -221,7 +221,7 @@ void X86Mir2Lir::GenConversion(Instruction::Code opcode, RegLocation rl_dest, LoadConstant(rl_result.reg, 0x7fffffff); NewLIR2(kX86Cvtsi2ssRR, temp_reg.GetReg(), rl_result.reg.GetReg()); NewLIR2(kX86ComissRR, rl_src.reg.GetReg(), temp_reg.GetReg()); - LIR* branch_pos_overflow = NewLIR2(kX86Jcc8, 0, kX86CondA); + LIR* branch_pos_overflow = NewLIR2(kX86Jcc8, 0, kX86CondAe); LIR* branch_na_n = NewLIR2(kX86Jcc8, 0, kX86CondP); NewLIR2(kX86Cvttss2siRR, rl_result.reg.GetReg(), rl_src.reg.GetReg()); LIR* branch_normal = NewLIR1(kX86Jmp8, 0); @@ -242,7 +242,7 @@ void X86Mir2Lir::GenConversion(Instruction::Code opcode, RegLocation rl_dest, LoadConstant(rl_result.reg, 0x7fffffff); NewLIR2(kX86Cvtsi2sdRR, temp_reg.GetReg(), rl_result.reg.GetReg()); NewLIR2(kX86ComisdRR, rl_src.reg.GetReg(), temp_reg.GetReg()); - LIR* branch_pos_overflow = NewLIR2(kX86Jcc8, 0, kX86CondA); + LIR* branch_pos_overflow = NewLIR2(kX86Jcc8, 0, kX86CondAe); LIR* branch_na_n = NewLIR2(kX86Jcc8, 0, kX86CondP); NewLIR2(kX86Cvttsd2siRR, rl_result.reg.GetReg(), rl_src.reg.GetReg()); LIR* branch_normal = NewLIR1(kX86Jmp8, 0); @@ -281,7 +281,7 @@ void X86Mir2Lir::GenConversion(Instruction::Code opcode, RegLocation rl_dest, LoadConstantWide(rl_result.reg, 0x7fffffffffffffff); NewLIR2(kX86Cvtsqi2ssRR, temp_reg.GetReg(), rl_result.reg.GetReg()); NewLIR2(kX86ComissRR, rl_src.reg.GetReg(), temp_reg.GetReg()); - LIR* branch_pos_overflow = NewLIR2(kX86Jcc8, 0, kX86CondA); + LIR* branch_pos_overflow = NewLIR2(kX86Jcc8, 0, kX86CondAe); LIR* branch_na_n = NewLIR2(kX86Jcc8, 0, kX86CondP); NewLIR2(kX86Cvttss2sqiRR, rl_result.reg.GetReg(), rl_src.reg.GetReg()); LIR* branch_normal = NewLIR1(kX86Jmp8, 0); @@ -306,7 +306,7 @@ void X86Mir2Lir::GenConversion(Instruction::Code opcode, RegLocation rl_dest, LoadConstantWide(rl_result.reg, 0x7fffffffffffffff); NewLIR2(kX86Cvtsqi2sdRR, temp_reg.GetReg(), rl_result.reg.GetReg()); NewLIR2(kX86ComisdRR, rl_src.reg.GetReg(), temp_reg.GetReg()); - LIR* branch_pos_overflow = NewLIR2(kX86Jcc8, 0, kX86CondA); + LIR* branch_pos_overflow = NewLIR2(kX86Jcc8, 0, kX86CondAe); LIR* branch_na_n = NewLIR2(kX86Jcc8, 0, kX86CondP); NewLIR2(kX86Cvttsd2sqiRR, rl_result.reg.GetReg(), rl_src.reg.GetReg()); LIR* branch_normal = NewLIR1(kX86Jmp8, 0); @@ -363,7 +363,8 @@ void X86Mir2Lir::GenRemFP(RegLocation rl_dest, RegLocation rl_src1, RegLocation } else { // It must have been register promoted if it is not a temp but is still in physical // register. Since we need it to be in memory to convert, we place it there now. - StoreBaseDisp(TargetReg(kSp), src1_v_reg_offset, rl_src1.reg, is_double ? k64 : k32); + StoreBaseDisp(TargetReg(kSp), src1_v_reg_offset, rl_src1.reg, is_double ? k64 : k32, + kNotVolatile); } } @@ -373,7 +374,8 @@ void X86Mir2Lir::GenRemFP(RegLocation rl_dest, RegLocation rl_src1, RegLocation FlushSpecificReg(reg_info); ResetDef(rl_src2.reg); } else { - StoreBaseDisp(TargetReg(kSp), src2_v_reg_offset, rl_src2.reg, is_double ? k64 : k32); + StoreBaseDisp(TargetReg(kSp), src2_v_reg_offset, rl_src2.reg, is_double ? k64 : k32, + kNotVolatile); } } @@ -433,7 +435,7 @@ void X86Mir2Lir::GenRemFP(RegLocation rl_dest, RegLocation rl_src1, RegLocation if (rl_result.location == kLocPhysReg) { rl_result = EvalLoc(rl_dest, kFPReg, true); if (is_double) { - LoadBaseDisp(TargetReg(kSp), dest_v_reg_offset, rl_result.reg, k64); + LoadBaseDisp(TargetReg(kSp), dest_v_reg_offset, rl_result.reg, k64, kNotVolatile); StoreFinalValueWide(rl_dest, rl_result); } else { Load32Disp(TargetReg(kSp), dest_v_reg_offset, rl_result.reg); @@ -568,8 +570,11 @@ void X86Mir2Lir::GenNegDouble(RegLocation rl_dest, RegLocation rl_src) { rl_src = LoadValueWide(rl_src, kCoreReg); rl_result = EvalLocWide(rl_dest, kCoreReg, true); if (Gen64Bit()) { - LoadConstantWide(rl_result.reg, 0x8000000000000000); - OpRegReg(kOpAdd, rl_result.reg, rl_src.reg); + OpRegCopy(rl_result.reg, rl_src.reg); + // Flip sign bit. + NewLIR2(kX86Rol64RI, rl_result.reg.GetReg(), 1); + NewLIR2(kX86Xor64RI, rl_result.reg.GetReg(), 1); + NewLIR2(kX86Ror64RI, rl_result.reg.GetReg(), 1); } else { OpRegRegImm(kOpAdd, rl_result.reg.GetHigh(), rl_src.reg.GetHigh(), 0x80000000); OpRegCopy(rl_result.reg, rl_src.reg); diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc index b3428133e0..fd20a81ddb 100644 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -35,14 +35,13 @@ void X86Mir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, rl_src1 = LoadValueWide(rl_src1, kCoreReg); rl_src2 = LoadValueWide(rl_src2, kCoreReg); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); - OpRegReg(kOpXor, rl_result.reg, rl_result.reg); // result = 0 - OpRegReg(kOpCmp, rl_src1.reg, rl_src2.reg); - NewLIR2(kX86Set8R, rl_result.reg.GetReg(), kX86CondNe); // result = (src1 != src2) ? 1 : result RegStorage temp_reg = AllocTemp(); - OpRegReg(kOpNeg, temp_reg, rl_result.reg); OpRegReg(kOpCmp, rl_src1.reg, rl_src2.reg); - // result = (src1 < src2) ? -result : result - OpCondRegReg(kOpCmov, kCondLt, rl_result.reg, temp_reg); + NewLIR2(kX86Set8R, rl_result.reg.GetReg(), kX86CondG); // result = (src1 > src2) ? 1 : 0 + NewLIR2(kX86Set8R, temp_reg.GetReg(), kX86CondL); // temp = (src1 >= src2) ? 0 : 1 + NewLIR2(kX86Sub8RR, rl_result.reg.GetReg(), temp_reg.GetReg()); + NewLIR2(kX86Movsx8qRR, rl_result.reg.GetReg(), rl_result.reg.GetReg()); + StoreValue(rl_dest, rl_result); FreeTemp(temp_reg); return; @@ -323,12 +322,22 @@ void X86Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) { return; } + if (Gen64Bit()) { + rl_src1 = LoadValueWide(rl_src1, kCoreReg); + rl_src2 = LoadValueWide(rl_src2, kCoreReg); + + OpRegReg(kOpCmp, rl_src1.reg, rl_src2.reg); + OpCondBranch(ccode, taken); + return; + } + FlushAllRegs(); LockCallTemps(); // Prepare for explicit register usage RegStorage r_tmp1 = RegStorage::MakeRegPair(rs_r0, rs_r1); RegStorage r_tmp2 = RegStorage::MakeRegPair(rs_r2, rs_r3); LoadValueDirectWideFixed(rl_src1, r_tmp1); LoadValueDirectWideFixed(rl_src2, r_tmp2); + // Swap operands and condition code to prevent use of zero flag. if (ccode == kCondLe || ccode == kCondGt) { // Compute (r3:r2) = (r3:r2) - (r1:r0) @@ -366,6 +375,23 @@ void X86Mir2Lir::GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1, LIR* taken = &block_label_list_[bb->taken]; rl_src1 = LoadValueWide(rl_src1, kCoreReg); bool is_equality_test = ccode == kCondEq || ccode == kCondNe; + + if (Gen64Bit()) { + if (is_equality_test && val == 0) { + // We can simplify of comparing for ==, != to 0. + NewLIR2(kX86Test64RR, rl_src1.reg.GetReg(), rl_src1.reg.GetReg()); + } else if (is_equality_test && val_hi == 0 && val_lo > 0) { + OpRegImm(kOpCmp, rl_src1.reg, val_lo); + } else { + RegStorage tmp = AllocTypedTempWide(false, kCoreReg); + LoadConstantWide(tmp, val); + OpRegReg(kOpCmp, rl_src1.reg, tmp); + FreeTemp(tmp); + } + OpCondBranch(ccode, taken); + return; + } + if (is_equality_test && val != 0) { rl_src1 = ForceTempWide(rl_src1); } @@ -373,7 +399,7 @@ void X86Mir2Lir::GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1, RegStorage high_reg = rl_src1.reg.GetHigh(); if (is_equality_test) { - // We can simpolify of comparing for ==, != to 0. + // We can simplify of comparing for ==, != to 0. if (val == 0) { if (IsTemp(low_reg)) { OpRegReg(kOpOr, low_reg, high_reg); @@ -728,7 +754,7 @@ bool X86Mir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) { RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); // Unaligned access is allowed on x86. - LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size); + LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, kNotVolatile); if (size == k64) { StoreValueWide(rl_dest, rl_result); } else { @@ -746,12 +772,12 @@ bool X86Mir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) { if (size == k64) { // Unaligned access is allowed on x86. RegLocation rl_value = LoadValueWide(rl_src_value, kCoreReg); - StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size); + StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size, kNotVolatile); } else { DCHECK(size == kSignedByte || size == kSignedHalf || size == k32); // Unaligned access is allowed on x86. RegLocation rl_value = LoadValue(rl_src_value, kCoreReg); - StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size); + StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size, kNotVolatile); } return true; } @@ -1112,7 +1138,7 @@ void X86Mir2Lir::GenImulMemImm(RegStorage dest, int sreg, int displacement, int NewLIR2(kX86Xor32RR, dest.GetReg(), dest.GetReg()); break; case 1: - LoadBaseDisp(rs_rX86_SP, displacement, dest, k32); + LoadBaseDisp(rs_rX86_SP, displacement, dest, k32, kNotVolatile); break; default: m = NewLIR4(IS_SIMM8(val) ? kX86Imul32RMI8 : kX86Imul32RMI, dest.GetReg(), @@ -1268,7 +1294,8 @@ void X86Mir2Lir::GenMulLong(Instruction::Code, RegLocation rl_dest, RegLocation if (src1_in_reg) { NewLIR2(kX86Mov32RR, rs_r1.GetReg(), rl_src1.reg.GetHighReg()); } else { - LoadBaseDisp(rs_rX86_SP, SRegOffset(rl_src1.s_reg_low) + HIWORD_OFFSET, rs_r1, k32); + LoadBaseDisp(rs_rX86_SP, SRegOffset(rl_src1.s_reg_low) + HIWORD_OFFSET, rs_r1, k32, + kNotVolatile); } if (is_square) { @@ -1291,7 +1318,8 @@ void X86Mir2Lir::GenMulLong(Instruction::Code, RegLocation rl_dest, RegLocation if (src2_in_reg) { NewLIR2(kX86Mov32RR, rs_r0.GetReg(), rl_src2.reg.GetHighReg()); } else { - LoadBaseDisp(rs_rX86_SP, SRegOffset(rl_src2.s_reg_low) + HIWORD_OFFSET, rs_r0, k32); + LoadBaseDisp(rs_rX86_SP, SRegOffset(rl_src2.s_reg_low) + HIWORD_OFFSET, rs_r0, k32, + kNotVolatile); } // EAX <- EAX * 1L (2H * 1L) @@ -1324,7 +1352,8 @@ void X86Mir2Lir::GenMulLong(Instruction::Code, RegLocation rl_dest, RegLocation if (src2_in_reg) { NewLIR2(kX86Mov32RR, rs_r0.GetReg(), rl_src2.reg.GetLowReg()); } else { - LoadBaseDisp(rs_rX86_SP, SRegOffset(rl_src2.s_reg_low) + LOWORD_OFFSET, rs_r0, k32); + LoadBaseDisp(rs_rX86_SP, SRegOffset(rl_src2.s_reg_low) + LOWORD_OFFSET, rs_r0, k32, + kNotVolatile); } // EDX:EAX <- 2L * 1L (double precision) @@ -1582,8 +1611,8 @@ void X86Mir2Lir::GenDivRemLong(Instruction::Code, RegLocation rl_dest, RegLocati LIR *minus_one_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe); // RHS is -1. - LoadConstantWide(rs_r3q, 0x8000000000000000); - NewLIR2(kX86Cmp64RR, rs_r0q.GetReg(), rs_r3q.GetReg()); + LoadConstantWide(rs_r6q, 0x8000000000000000); + NewLIR2(kX86Cmp64RR, rs_r0q.GetReg(), rs_r6q.GetReg()); LIR * minint_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe); // In 0x8000000000000000/-1 case. @@ -2174,6 +2203,7 @@ bool X86Mir2Lir::GenLongLongImm(RegLocation rl_dest, RegLocation rl_src1, if (rl_dest.location == kLocPhysReg && rl_src1.location == kLocPhysReg && !rl_dest.reg.IsFloat()) { X86OpCode x86op = GetOpcode(op, rl_dest, false, val); + OpRegCopy(rl_dest.reg, rl_src1.reg); NewLIR2(x86op, rl_dest.reg.GetReg(), val); StoreFinalValueWide(rl_dest, rl_dest); return true; @@ -2262,21 +2292,21 @@ void X86Mir2Lir::GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx, if (rl_method.location == kLocPhysReg) { if (use_declaring_class) { LoadRefDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), - check_class); + check_class, kNotVolatile); } else { LoadRefDisp(rl_method.reg, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), - check_class); - LoadRefDisp(check_class, offset_of_type, check_class); + check_class, kNotVolatile); + LoadRefDisp(check_class, offset_of_type, check_class, kNotVolatile); } } else { LoadCurrMethodDirect(check_class); if (use_declaring_class) { LoadRefDisp(check_class, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), - check_class); + check_class, kNotVolatile); } else { LoadRefDisp(check_class, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), - check_class); - LoadRefDisp(check_class, offset_of_type, check_class); + check_class, kNotVolatile); + LoadRefDisp(check_class, offset_of_type, check_class, kNotVolatile); } } @@ -2323,16 +2353,16 @@ void X86Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_k } else if (use_declaring_class) { LoadValueDirectFixed(rl_src, TargetReg(kArg0)); LoadRefDisp(TargetReg(kArg1), mirror::ArtMethod::DeclaringClassOffset().Int32Value(), - class_reg); + class_reg, kNotVolatile); } else { // Load dex cache entry into class_reg (kArg2). LoadValueDirectFixed(rl_src, TargetReg(kArg0)); LoadRefDisp(TargetReg(kArg1), mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), - class_reg); + class_reg, kNotVolatile); int32_t offset_of_type = mirror::Array::DataOffset(sizeof(mirror::HeapReference<mirror::Class*>)).Int32Value() + (sizeof(mirror::HeapReference<mirror::Class*>) * type_idx); - LoadRefDisp(class_reg, offset_of_type, class_reg); + LoadRefDisp(class_reg, offset_of_type, class_reg, kNotVolatile); if (!can_assume_type_is_in_dex_cache) { // Need to test presence of type in dex cache at runtime. LIR* hop_branch = OpCmpImmBranch(kCondNe, class_reg, 0, NULL); @@ -2365,7 +2395,8 @@ void X86Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_k /* Load object->klass_. */ DCHECK_EQ(mirror::Object::ClassOffset().Int32Value(), 0); - LoadRefDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1)); + LoadRefDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1), + kNotVolatile); /* kArg0 is ref, kArg1 is ref->klass_, kArg2 is class. */ LIR* branchover = nullptr; if (type_known_final) { @@ -2657,7 +2688,7 @@ void X86Mir2Lir::GenIntToLong(RegLocation rl_dest, RegLocation rl_src) { Mir2Lir::GenIntToLong(rl_dest, rl_src); return; } - rl_src = UpdateLoc(rl_src); + rl_src = UpdateLocTyped(rl_src, kCoreReg); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); if (rl_src.location == kLocPhysReg) { NewLIR2(kX86MovsxdRR, rl_result.reg.GetReg(), rl_src.reg.GetReg()); diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index 078dd5a73b..408a40a3fb 100644 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -1866,7 +1866,7 @@ void X86Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) { StoreValue(rl_method, rl_src); // If Method* has been promoted, explicitly flush if (rl_method.location == kLocPhysReg) { - StoreRefDisp(TargetReg(kSp), 0, TargetReg(kArg0)); + StoreRefDisp(TargetReg(kSp), 0, TargetReg(kArg0), kNotVolatile); } if (cu_->num_ins == 0) { @@ -1916,11 +1916,11 @@ void X86Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) { } if (need_flush) { if (t_loc->wide && t_loc->fp) { - StoreBaseDisp(TargetReg(kSp), SRegOffset(start_vreg + i), reg, k64); + StoreBaseDisp(TargetReg(kSp), SRegOffset(start_vreg + i), reg, k64, kNotVolatile); // Increment i to skip the next one i++; } else if (t_loc->wide && !t_loc->fp) { - StoreBaseDisp(TargetReg(kSp), SRegOffset(start_vreg + i), reg, k64); + StoreBaseDisp(TargetReg(kSp), SRegOffset(start_vreg + i), reg, k64, kNotVolatile); // Increment i to skip the next one i++; } else { @@ -2018,14 +2018,14 @@ int X86Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, loc = UpdateLocWide(loc); if (loc.location == kLocPhysReg) { ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); - StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k64); + StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k64, kNotVolatile); } next_arg += 2; } else { loc = UpdateLoc(loc); if (loc.location == kLocPhysReg) { ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); - StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k32); + StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k32, kNotVolatile); } next_arg++; } @@ -2150,7 +2150,8 @@ int X86Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, if (in_to_reg_storage_mapping.IsThereStackMapped()) { RegStorage regSingle = TargetReg(kArg2); RegStorage regWide = RegStorage::Solo64(TargetReg(kArg3).GetReg()); - for (int i = start_index; i <= last_mapped_in + regs_left_to_pass_via_stack; i++) { + for (int i = start_index; + i < last_mapped_in + size_of_the_last_mapped + regs_left_to_pass_via_stack; i++) { RegLocation rl_arg = info->args[i]; rl_arg = UpdateRawLoc(rl_arg); RegStorage reg = in_to_reg_storage_mapping.Get(i); @@ -2161,24 +2162,26 @@ int X86Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); if (rl_arg.wide) { if (rl_arg.location == kLocPhysReg) { - StoreBaseDisp(TargetReg(kSp), out_offset, rl_arg.reg, k64); + StoreBaseDisp(TargetReg(kSp), out_offset, rl_arg.reg, k64, kNotVolatile); } else { LoadValueDirectWideFixed(rl_arg, regWide); - StoreBaseDisp(TargetReg(kSp), out_offset, regWide, k64); + StoreBaseDisp(TargetReg(kSp), out_offset, regWide, k64, kNotVolatile); } - i++; } else { if (rl_arg.location == kLocPhysReg) { - StoreBaseDisp(TargetReg(kSp), out_offset, rl_arg.reg, k32); + StoreBaseDisp(TargetReg(kSp), out_offset, rl_arg.reg, k32, kNotVolatile); } else { LoadValueDirectFixed(rl_arg, regSingle); - StoreBaseDisp(TargetReg(kSp), out_offset, regSingle, k32); + StoreBaseDisp(TargetReg(kSp), out_offset, regSingle, k32, kNotVolatile); } } } call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); } + if (rl_arg.wide) { + i++; + } } } @@ -2190,13 +2193,15 @@ int X86Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, if (reg.Valid()) { if (rl_arg.wide) { LoadValueDirectWideFixed(rl_arg, reg); - i++; } else { LoadValueDirectFixed(rl_arg, reg); } call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); } + if (rl_arg.wide) { + i++; + } } call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc index 46e877f8f9..0352808a7c 100644 --- a/compiler/dex/quick/x86/utility_x86.cc +++ b/compiler/dex/quick/x86/utility_x86.cc @@ -585,7 +585,7 @@ LIR* X86Mir2Lir::LoadConstantWide(RegStorage r_dest, int64_t value) { // value. ScopedMemRefType mem_ref_type(this, ResourceMask::kLiteral); res = LoadBaseDisp(rl_method.reg, 256 /* bogus */, RegStorage::FloatSolo64(low_reg_val), - kDouble); + kDouble, kNotVolatile); res->target = data_target; res->flags.fixup = kFixupLoad; store_method_addr_used_ = true; @@ -611,8 +611,12 @@ LIR* X86Mir2Lir::LoadConstantWide(RegStorage r_dest, int64_t value) { if (val_lo < 0) { val_hi += 1; } - res = LoadConstantNoClobber(RegStorage::Solo32(r_dest.GetReg()), val_hi); - NewLIR2(kX86Sal64RI, r_dest.GetReg(), 32); + if (val_hi != 0) { + res = LoadConstantNoClobber(RegStorage::Solo32(r_dest.GetReg()), val_hi); + NewLIR2(kX86Sal64RI, r_dest.GetReg(), 32); + } else { + res = NewLIR2(kX86Xor64RR, r_dest.GetReg(), r_dest.GetReg()); + } if (val_lo != 0) { NewLIR2(kX86Add64RI, r_dest.GetReg(), val_lo); } @@ -752,17 +756,22 @@ LIR* X86Mir2Lir::LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStora return LoadBaseIndexedDisp(r_base, r_index, scale, 0, r_dest, size); } -LIR* X86Mir2Lir::LoadBaseDispVolatile(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size) { +LIR* X86Mir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, + OpSize size, VolatileKind is_volatile) { // LoadBaseDisp() will emit correct insn for atomic load on x86 // assuming r_dest is correctly prepared using RegClassForFieldLoadStore(). - return LoadBaseDisp(r_base, displacement, r_dest, size); -} -LIR* X86Mir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size) { - return LoadBaseIndexedDisp(r_base, RegStorage::InvalidReg(), 0, displacement, r_dest, - size); + LIR* load = LoadBaseIndexedDisp(r_base, RegStorage::InvalidReg(), 0, displacement, r_dest, + size); + + if (UNLIKELY(is_volatile == kVolatile)) { + // Without context sensitive analysis, we must issue the most conservative barriers. + // In this case, either a load or store may follow so we issue both barriers. + GenMemBarrier(kLoadLoad); + GenMemBarrier(kLoadStore); + } + + return load; } LIR* X86Mir2Lir::StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale, @@ -850,20 +859,28 @@ LIR* X86Mir2Lir::StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int /* store value base base + scaled index. */ LIR* X86Mir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, - int scale, OpSize size) { + int scale, OpSize size) { return StoreBaseIndexedDisp(r_base, r_index, scale, 0, r_src, size); } -LIR* X86Mir2Lir::StoreBaseDispVolatile(RegStorage r_base, int displacement, - RegStorage r_src, OpSize size) { +LIR* X86Mir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, OpSize size, + VolatileKind is_volatile) { + if (UNLIKELY(is_volatile == kVolatile)) { + // There might have been a store before this volatile one so insert StoreStore barrier. + GenMemBarrier(kStoreStore); + } + // StoreBaseDisp() will emit correct insn for atomic store on x86 // assuming r_dest is correctly prepared using RegClassForFieldLoadStore(). - return StoreBaseDisp(r_base, displacement, r_src, size); -} -LIR* X86Mir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, - RegStorage r_src, OpSize size) { - return StoreBaseIndexedDisp(r_base, RegStorage::InvalidReg(), 0, displacement, r_src, size); + LIR* store = StoreBaseIndexedDisp(r_base, RegStorage::InvalidReg(), 0, displacement, r_src, size); + + if (UNLIKELY(is_volatile == kVolatile)) { + // A load might follow the volatile store so insert a StoreLoad barrier. + GenMemBarrier(kStoreLoad); + } + + return store; } LIR* X86Mir2Lir::OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg, diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h index 28b9dca193..17c44bc2c7 100644 --- a/compiler/dex/quick/x86/x86_lir.h +++ b/compiler/dex/quick/x86/x86_lir.h @@ -609,6 +609,10 @@ enum X86OpCode { Binary0fOpCode(kX86Movzx16), // zero-extend 16-bit value Binary0fOpCode(kX86Movsx8), // sign-extend 8-bit value Binary0fOpCode(kX86Movsx16), // sign-extend 16-bit value + Binary0fOpCode(kX86Movzx8q), // zero-extend 8-bit value to quad word + Binary0fOpCode(kX86Movzx16q), // zero-extend 16-bit value to quad word + Binary0fOpCode(kX86Movsx8q), // sign-extend 8-bit value to quad word + Binary0fOpCode(kX86Movsx16q), // sign-extend 16-bit value to quad word #undef Binary0fOpCode kX86Jcc8, kX86Jcc32, // jCC rel8/32; lir operands - 0: rel, 1: CC, target assigned kX86Jmp8, kX86Jmp32, // jmp rel8/32; lir operands - 0: rel, target assigned @@ -707,6 +711,8 @@ struct X86EncodingMap { #define REX_X 0x42 // Extension of the ModR/M r/m field, SIB base field, or Opcode reg field #define REX_B 0x41 +// Extended register set +#define REX 0x40 // Mask extracting the least 3 bits of r0..r15 #define kRegNumMask32 0x07 // Value indicating that base or reg is not used diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 3e326f0633..96625c5dac 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -697,7 +697,7 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) return; } - timings->NewSplit("LoadImageClasses"); + TimingLogger::ScopedTiming t("LoadImageClasses", timings); // Make a first class to load all classes explicitly listed in the file Thread* self = Thread::Current(); ScopedObjectAccess soa(self); @@ -794,8 +794,7 @@ void CompilerDriver::FindClinitImageClassesCallback(mirror::Object* object, void void CompilerDriver::UpdateImageClasses(TimingLogger* timings) { if (IsImage()) { - timings->NewSplit("UpdateImageClasses"); - + TimingLogger::ScopedTiming t("UpdateImageClasses", timings); // Update image_classes_ with classes for objects created by <clinit> methods. Thread* self = Thread::Current(); const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter"); @@ -1606,11 +1605,11 @@ void CompilerDriver::ResolveDexFile(jobject class_loader, const DexFile& dex_fil if (IsImage()) { // For images we resolve all types, such as array, whereas for applications just those with // classdefs are resolved by ResolveClassFieldsAndMethods. - timings->NewSplit("Resolve Types"); + TimingLogger::ScopedTiming t("Resolve Types", timings); context.ForAll(0, dex_file.NumTypeIds(), ResolveType, thread_count_); } - timings->NewSplit("Resolve MethodsAndFields"); + TimingLogger::ScopedTiming t("Resolve MethodsAndFields", timings); context.ForAll(0, dex_file.NumClassDefs(), ResolveClassFieldsAndMethods, thread_count_); } @@ -1672,7 +1671,7 @@ static void VerifyClass(const ParallelCompilationManager* manager, size_t class_ void CompilerDriver::VerifyDexFile(jobject class_loader, const DexFile& dex_file, ThreadPool* thread_pool, TimingLogger* timings) { - timings->NewSplit("Verify Dex File"); + TimingLogger::ScopedTiming t("Verify Dex File", timings); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, thread_pool); context.ForAll(0, dex_file.NumClassDefs(), VerifyClass, thread_count_); @@ -1765,7 +1764,7 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile& dex_file, ThreadPool* thread_pool, TimingLogger* timings) { - timings->NewSplit("InitializeNoClinit"); + TimingLogger::ScopedTiming t("InitializeNoClinit", timings); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); ParallelCompilationManager context(class_linker, jni_class_loader, this, &dex_file, thread_pool); size_t thread_count; @@ -1877,7 +1876,7 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_file, ThreadPool* thread_pool, TimingLogger* timings) { - timings->NewSplit("Compile Dex File"); + TimingLogger::ScopedTiming t("Compile Dex File", timings); ParallelCompilationManager context(Runtime::Current()->GetClassLinker(), class_loader, this, &dex_file, thread_pool); context.ForAll(0, dex_file.NumClassDefs(), CompilerDriver::CompileClass, thread_count_); @@ -2054,7 +2053,9 @@ bool CompilerDriver::SkipCompilation(const std::string& method_name) { ProfileFile::ProfileData data; if (!profile_file_.GetProfileData(&data, method_name)) { // Not in profile, no information can be determined. - VLOG(compiler) << "not compiling " << method_name << " because it's not in the profile"; + if (kIsDebugBuild) { + VLOG(compiler) << "not compiling " << method_name << " because it's not in the profile"; + } return true; } @@ -2063,13 +2064,16 @@ bool CompilerDriver::SkipCompilation(const std::string& method_name) { // falls inside a bucket. bool compile = data.GetTopKUsedPercentage() - data.GetUsedPercent() <= compiler_options_->GetTopKProfileThreshold(); - if (compile) { - LOG(INFO) << "compiling method " << method_name << " because its usage is part of top " - << data.GetTopKUsedPercentage() << "% with a percent of " << data.GetUsedPercent() << "%" - << " (topKThreshold=" << compiler_options_->GetTopKProfileThreshold() << ")"; - } else { - VLOG(compiler) << "not compiling method " << method_name << " because it's not part of leading " - << compiler_options_->GetTopKProfileThreshold() << "% samples)"; + if (kIsDebugBuild) { + if (compile) { + LOG(INFO) << "compiling method " << method_name << " because its usage is part of top " + << data.GetTopKUsedPercentage() << "% with a percent of " << data.GetUsedPercent() << "%" + << " (topKThreshold=" << compiler_options_->GetTopKProfileThreshold() << ")"; + } else { + VLOG(compiler) << "not compiling method " << method_name + << " because it's not part of leading " << compiler_options_->GetTopKProfileThreshold() + << "% samples)"; + } } return !compile; } diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index ca956aac36..5325a68b37 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -38,12 +38,12 @@ class CompilerDriverTest : public CommonCompilerTest { protected: void CompileAll(jobject class_loader) LOCKS_EXCLUDED(Locks::mutator_lock_) { TimingLogger timings("CompilerDriverTest::CompileAll", false, false); - timings.StartSplit("CompileAll"); + TimingLogger::ScopedTiming t(__FUNCTION__, &timings); compiler_driver_->CompileAll(class_loader, Runtime::Current()->GetCompileTimeClassPath(class_loader), &timings); + t.NewTiming("MakeAllExecutable"); MakeAllExecutable(class_loader); - timings.EndSplit(); } void EnsureCompiled(jobject class_loader, const char* class_name, const char* method, diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 92be147a40..d52ec0ad5a 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -64,7 +64,7 @@ TEST_F(ImageTest, WriteRead) { jobject class_loader = NULL; ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); TimingLogger timings("ImageTest::WriteRead", false, false); - timings.StartSplit("CompileAll"); + TimingLogger::ScopedTiming t("CompileAll", &timings); if (kUsePortableCompiler) { // TODO: we disable this for portable so the test executes in a reasonable amount of time. // We shouldn't need to do this. @@ -75,6 +75,7 @@ TEST_F(ImageTest, WriteRead) { } compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings); + t.NewTiming("WriteElf"); ScopedObjectAccess soa(Thread::Current()); OatWriter oat_writer(class_linker->GetBootClassPath(), 0, 0, "", compiler_driver_.get(), &timings); @@ -84,7 +85,6 @@ TEST_F(ImageTest, WriteRead) { &oat_writer, oat_file.GetFile()); ASSERT_TRUE(success); - timings.EndSplit(); } } // Workound bug that mcld::Linker::emit closes oat_file by reopening as dup_oat. @@ -129,11 +129,7 @@ TEST_F(ImageTest, WriteRead) { runtime_.reset(); java_lang_dex_file_ = NULL; - std::string error_msg; - std::unique_ptr<const DexFile> dex(DexFile::Open(GetLibCoreDexFileName().c_str(), - GetLibCoreDexFileName().c_str(), - &error_msg)); - ASSERT_TRUE(dex.get() != nullptr) << error_msg; + std::unique_ptr<const DexFile> dex(LoadExpectSingleDexFile(GetLibCoreDexFileName().c_str())); // Remove the reservation of the memory for use to load the image. UnreserveImageSpace(); diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index ca1239f18d..6e5f19a8c5 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -263,7 +263,11 @@ void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg) { } mirror::String* string = obj->AsString(); const uint16_t* utf16_string = string->GetCharArray()->GetData() + string->GetOffset(); - for (DexCache* dex_cache : Runtime::Current()->GetClassLinker()->GetDexCaches()) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + ReaderMutexLock mu(Thread::Current(), *class_linker->DexLock()); + size_t dex_cache_count = class_linker->GetDexCacheCount(); + for (size_t i = 0; i < dex_cache_count; ++i) { + DexCache* dex_cache = class_linker->GetDexCache(i); const DexFile& dex_file = *dex_cache->GetDexFile(); const DexFile::StringId* string_id; if (UNLIKELY(string->GetLength() == 0)) { @@ -316,7 +320,10 @@ void ImageWriter::PruneNonImageClasses() { // Clear references to removed classes from the DexCaches. ArtMethod* resolution_method = runtime->GetResolutionMethod(); - for (DexCache* dex_cache : class_linker->GetDexCaches()) { + ReaderMutexLock mu(Thread::Current(), *class_linker->DexLock()); + size_t dex_cache_count = class_linker->GetDexCacheCount(); + for (size_t idx = 0; idx < dex_cache_count; ++idx) { + DexCache* dex_cache = class_linker->GetDexCache(idx); for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) { Class* klass = dex_cache->GetResolvedType(i); if (klass != NULL && !IsImageClass(klass)) { @@ -408,13 +415,27 @@ ObjectArray<Object>* ImageWriter::CreateImageRoots() const { Handle<Class> object_array_class(hs.NewHandle( class_linker->FindSystemClass(self, "[Ljava/lang/Object;"))); - // build an Object[] of all the DexCaches used in the source_space_ + // build an Object[] of all the DexCaches used in the source_space_. + // Since we can't hold the dex lock when allocating the dex_caches + // 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. + size_t dex_cache_count; + { + ReaderMutexLock mu(Thread::Current(), *class_linker->DexLock()); + dex_cache_count = class_linker->GetDexCacheCount(); + } Handle<ObjectArray<Object>> dex_caches( hs.NewHandle(ObjectArray<Object>::Alloc(self, object_array_class.Get(), - class_linker->GetDexCaches().size()))); - int i = 0; - for (DexCache* dex_cache : class_linker->GetDexCaches()) { - dex_caches->Set<false>(i++, dex_cache); + dex_cache_count))); + CHECK(dex_caches.Get() != nullptr) << "Failed to allocate a dex cache array."; + { + ReaderMutexLock mu(Thread::Current(), *class_linker->DexLock()); + CHECK_EQ(dex_cache_count, class_linker->GetDexCacheCount()) + << "The number of dex caches changed."; + for (size_t i = 0; i < dex_cache_count; ++i) { + dex_caches->Set<false>(i, class_linker->GetDexCache(i)); + } } // build an Object[] of the roots needed to restore the runtime diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index c6b9161b63..e1b6992c47 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -91,31 +91,31 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, size_oat_class_method_offsets_(0) { size_t offset; { - TimingLogger::ScopedSplit split("InitOatHeader", timings); + TimingLogger::ScopedTiming split("InitOatHeader", timings); offset = InitOatHeader(); } { - TimingLogger::ScopedSplit split("InitOatDexFiles", timings); + TimingLogger::ScopedTiming split("InitOatDexFiles", timings); offset = InitOatDexFiles(offset); } { - TimingLogger::ScopedSplit split("InitDexFiles", timings); + TimingLogger::ScopedTiming split("InitDexFiles", timings); offset = InitDexFiles(offset); } { - TimingLogger::ScopedSplit split("InitOatClasses", timings); + TimingLogger::ScopedTiming split("InitOatClasses", timings); offset = InitOatClasses(offset); } { - TimingLogger::ScopedSplit split("InitOatMaps", timings); + TimingLogger::ScopedTiming split("InitOatMaps", timings); offset = InitOatMaps(offset); } { - TimingLogger::ScopedSplit split("InitOatCode", timings); + TimingLogger::ScopedTiming split("InitOatCode", timings); offset = InitOatCode(offset); } { - TimingLogger::ScopedSplit split("InitOatCodeDexFiles", timings); + TimingLogger::ScopedTiming split("InitOatCodeDexFiles", timings); offset = InitOatCodeDexFiles(offset); } size_ = offset; @@ -800,6 +800,7 @@ size_t OatWriter::InitOatMaps(size_t offset) { size_t OatWriter::InitOatCode(size_t offset) { // calculate the offsets within OatHeader to executable code size_t old_offset = offset; + size_t adjusted_offset = offset; // required to be on a new page boundary offset = RoundUp(offset, kPageSize); oat_header_->SetExecutableOffset(offset); @@ -809,7 +810,8 @@ size_t OatWriter::InitOatCode(size_t offset) { #define DO_TRAMPOLINE(field, fn_name) \ offset = CompiledCode::AlignCode(offset, instruction_set); \ - oat_header_->Set ## fn_name ## Offset(offset); \ + adjusted_offset = offset + CompiledCode::CodeDelta(instruction_set); \ + oat_header_->Set ## fn_name ## Offset(adjusted_offset); \ field.reset(compiler_driver_->Create ## fn_name()); \ offset += field->size(); diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 521992ad3a..c3a322caee 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -93,15 +93,30 @@ static bool CanHandleCodeItem(const DexFile::CodeItem& code_item) { } template<typename T> -void HGraphBuilder::If_22t(const Instruction& instruction, int32_t dex_offset, bool is_not) { +void HGraphBuilder::If_22t(const Instruction& instruction, int32_t dex_offset) { HInstruction* first = LoadLocal(instruction.VRegA(), Primitive::kPrimInt); HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt); - current_block_->AddInstruction(new (arena_) T(first, second)); - if (is_not) { - current_block_->AddInstruction(new (arena_) HNot(current_block_->GetLastInstruction())); - } - current_block_->AddInstruction(new (arena_) HIf(current_block_->GetLastInstruction())); - HBasicBlock* target = FindBlockStartingAt(instruction.GetTargetOffset() + dex_offset); + T* comparison = new (arena_) T(first, second); + current_block_->AddInstruction(comparison); + HInstruction* ifinst = new (arena_) HIf(comparison); + current_block_->AddInstruction(ifinst); + HBasicBlock* target = FindBlockStartingAt(dex_offset + instruction.GetTargetOffset()); + DCHECK(target != nullptr); + current_block_->AddSuccessor(target); + target = FindBlockStartingAt(dex_offset + instruction.SizeInCodeUnits()); + DCHECK(target != nullptr); + current_block_->AddSuccessor(target); + current_block_ = nullptr; +} + +template<typename T> +void HGraphBuilder::If_21t(const Instruction& instruction, int32_t dex_offset) { + HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt); + T* comparison = new (arena_) T(value, GetIntConstant(0)); + current_block_->AddInstruction(comparison); + HInstruction* ifinst = new (arena_) HIf(comparison); + current_block_->AddInstruction(ifinst); + HBasicBlock* target = FindBlockStartingAt(dex_offset + instruction.GetTargetOffset()); DCHECK(target != nullptr); current_block_->AddSuccessor(target); target = FindBlockStartingAt(dex_offset + instruction.SizeInCodeUnits()); @@ -340,16 +355,38 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_ break; } + case Instruction::CONST: { + int32_t register_index = instruction.VRegA(); + HIntConstant* constant = GetIntConstant(instruction.VRegB_31i()); + UpdateLocal(register_index, constant); + break; + } + + case Instruction::CONST_HIGH16: { + int32_t register_index = instruction.VRegA(); + HIntConstant* constant = GetIntConstant(instruction.VRegB_21h() << 16); + UpdateLocal(register_index, constant); + break; + } + case Instruction::CONST_WIDE_16: { int32_t register_index = instruction.VRegA(); - HLongConstant* constant = GetLongConstant(instruction.VRegB_21s()); + // Get 16 bits of constant value, sign extended to 64 bits. + int64_t value = instruction.VRegB_21s(); + value <<= 48; + value >>= 48; + HLongConstant* constant = GetLongConstant(value); UpdateLocal(register_index, constant); break; } case Instruction::CONST_WIDE_32: { int32_t register_index = instruction.VRegA(); - HLongConstant* constant = GetLongConstant(instruction.VRegB_31i()); + // Get 32 bits of constant value, sign extended to 64 bits. + int64_t value = instruction.VRegB_31i(); + value <<= 32; + value >>= 32; + HLongConstant* constant = GetLongConstant(value); UpdateLocal(register_index, constant); break; } @@ -361,27 +398,58 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_ break; } - case Instruction::MOVE: { + case Instruction::CONST_WIDE_HIGH16: { + int32_t register_index = instruction.VRegA(); + int64_t value = static_cast<int64_t>(instruction.VRegB_21h()) << 48; + HLongConstant* constant = GetLongConstant(value); + UpdateLocal(register_index, constant); + break; + } + + // TODO: these instructions are also used to move floating point values, so what is + // the type (int or float)? + case Instruction::MOVE: + case Instruction::MOVE_FROM16: + case Instruction::MOVE_16: { HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimInt); UpdateLocal(instruction.VRegA(), value); break; } - case Instruction::RETURN_VOID: { - BuildReturn(instruction, Primitive::kPrimVoid); + // TODO: these instructions are also used to move floating point values, so what is + // the type (long or double)? + case Instruction::MOVE_WIDE: + case Instruction::MOVE_WIDE_FROM16: + case Instruction::MOVE_WIDE_16: { + HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimLong); + UpdateLocal(instruction.VRegA(), value); break; } - case Instruction::IF_EQ: { - If_22t<HEqual>(instruction, dex_offset, false); + case Instruction::MOVE_OBJECT: + case Instruction::MOVE_OBJECT_16: + case Instruction::MOVE_OBJECT_FROM16: { + HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimNot); + UpdateLocal(instruction.VRegA(), value); break; } - case Instruction::IF_NE: { - If_22t<HEqual>(instruction, dex_offset, true); + case Instruction::RETURN_VOID: { + BuildReturn(instruction, Primitive::kPrimVoid); break; } +#define IF_XX(comparison, cond) \ + case Instruction::IF_##cond: If_22t<comparison>(instruction, dex_offset); break; \ + case Instruction::IF_##cond##Z: If_21t<comparison>(instruction, dex_offset); break + + IF_XX(HEqual, EQ); + IF_XX(HNotEqual, NE); + IF_XX(HLessThan, LT); + IF_XX(HLessThanOrEqual, LE); + IF_XX(HGreaterThan, GT); + IF_XX(HGreaterThanOrEqual, GE); + case Instruction::GOTO: case Instruction::GOTO_16: case Instruction::GOTO_32: { @@ -500,10 +568,10 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_ } case Instruction::MOVE_RESULT: - case Instruction::MOVE_RESULT_WIDE: { + case Instruction::MOVE_RESULT_WIDE: + case Instruction::MOVE_RESULT_OBJECT: UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); break; - } case Instruction::NOP: break; diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 108514a632..0852a26c55 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -22,17 +22,11 @@ #include "primitive.h" #include "utils/allocation.h" #include "utils/growable_array.h" +#include "nodes.h" namespace art { -class ArenaAllocator; class Instruction; -class HBasicBlock; -class HGraph; -class HIntConstant; -class HLongConstant; -class HInstruction; -class HLocal; class HGraphBuilder : public ValueObject { public: @@ -90,7 +84,11 @@ class HGraphBuilder : public ValueObject { template<typename T> void Binop_22s(const Instruction& instruction, bool reverse); - template<typename T> void If_22t(const Instruction& instruction, int32_t dex_offset, bool is_not); + template<typename T> + void If_22t(const Instruction& instruction, int32_t dex_offset); + + template<typename T> + void If_21t(const Instruction& instruction, int32_t dex_offset); void BuildReturn(const Instruction& instruction, Primitive::Type type); diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 212a6dc370..ec3c81533f 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -34,6 +34,35 @@ arm::ArmManagedRegister Location::AsArm() const { namespace arm { + +inline Condition ARMCondition(IfCondition cond) { + switch (cond) { + case kCondEQ: return EQ; + case kCondNE: return NE; + case kCondLT: return LT; + case kCondLE: return LE; + case kCondGT: return GT; + case kCondGE: return GE; + default: + LOG(FATAL) << "Unknown if condition"; + } + return EQ; // Unreachable. +} + +inline Condition ARMOppositeCondition(IfCondition cond) { + switch (cond) { + case kCondEQ: return NE; + case kCondNE: return EQ; + case kCondLT: return GE; + case kCondLE: return GT; + case kCondGT: return LE; + case kCondGE: return LT; + default: + LOG(FATAL) << "Unknown if condition"; + } + return EQ; // Unreachable. +} + static constexpr int kNumberOfPushedRegistersAtEntry = 1; static constexpr int kCurrentMethodStackOffset = 0; @@ -419,33 +448,103 @@ void InstructionCodeGeneratorARM::VisitExit(HExit* exit) { void LocationsBuilderARM::VisitIf(HIf* if_instr) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr); - locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(0, Location::Any()); if_instr->SetLocations(locations); } void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) { - // TODO: Generate the input as a condition, instead of materializing in a register. - __ cmp(if_instr->GetLocations()->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(0)); - __ b(codegen_->GetLabelOf(if_instr->IfFalseSuccessor()), EQ); - if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfTrueSuccessor())) { - __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + HInstruction* cond = if_instr->InputAt(0); + DCHECK(cond->IsCondition()); + HCondition* condition = cond->AsCondition(); + if (condition->NeedsMaterialization()) { + // Condition has been materialized, compare the output to 0 + if (!if_instr->GetLocations()->InAt(0).IsRegister()) { + LOG(FATAL) << "Materialized condition is not in an ARM register"; + } + __ cmp(if_instr->GetLocations()->InAt(0).AsArm().AsCoreRegister(), + ShifterOperand(0)); + __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), EQ); + } else { + // Condition has not been materialized, use its inputs as the comparison and its + // condition as the branch condition. + __ cmp(condition->GetLocations()->InAt(0).AsArm().AsCoreRegister(), + ShifterOperand(condition->GetLocations()->InAt(1).AsArm().AsCoreRegister())); + __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), + ARMCondition(condition->GetCondition())); + } + if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) { + __ b(codegen_->GetLabelOf(if_instr->IfFalseSuccessor())); } } -void LocationsBuilderARM::VisitEqual(HEqual* equal) { - LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(equal); + +void LocationsBuilderARM::VisitCondition(HCondition* comp) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); locations->SetOut(Location::RequiresRegister()); - equal->SetLocations(locations); + comp->SetLocations(locations); +} + +void InstructionCodeGeneratorARM::VisitCondition(HCondition* comp) { + if (comp->NeedsMaterialization()) { + LocationSummary* locations = comp->GetLocations(); + __ cmp(locations->InAt(0).AsArm().AsCoreRegister(), + ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister())); + __ it(ARMCondition(comp->GetCondition()), kItElse); + __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(1), + ARMCondition(comp->GetCondition())); + __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(0), + ARMOppositeCondition(comp->GetCondition())); + } +} + +void LocationsBuilderARM::VisitEqual(HEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorARM::VisitEqual(HEqual* comp) { + VisitCondition(comp); +} + +void LocationsBuilderARM::VisitNotEqual(HNotEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorARM::VisitNotEqual(HNotEqual* comp) { + VisitCondition(comp); +} + +void LocationsBuilderARM::VisitLessThan(HLessThan* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorARM::VisitLessThan(HLessThan* comp) { + VisitCondition(comp); +} + +void LocationsBuilderARM::VisitLessThanOrEqual(HLessThanOrEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorARM::VisitLessThanOrEqual(HLessThanOrEqual* comp) { + VisitCondition(comp); +} + +void LocationsBuilderARM::VisitGreaterThan(HGreaterThan* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorARM::VisitGreaterThan(HGreaterThan* comp) { + VisitCondition(comp); +} + +void LocationsBuilderARM::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) { + VisitCondition(comp); } -void InstructionCodeGeneratorARM::VisitEqual(HEqual* equal) { - LocationSummary* locations = equal->GetLocations(); - __ teq(locations->InAt(0).AsArm().AsCoreRegister(), - ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister())); - __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(1), EQ); - __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(0), NE); +void InstructionCodeGeneratorARM::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) { + VisitCondition(comp); } void LocationsBuilderARM::VisitLocal(HLocal* local) { @@ -608,7 +707,8 @@ void InstructionCodeGeneratorARM::LoadCurrentMethod(Register reg) { void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) { Register temp = invoke->GetLocations()->GetTemp(0).AsArm().AsCoreRegister(); - size_t index_in_cache = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + + uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>); + size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).Int32Value() + invoke->GetIndexInDexCache() * kArmWordSize; // TODO: Implement all kinds of calls: diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index f4b12e2d38..f624f3ce90 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -34,6 +34,20 @@ x86::X86ManagedRegister Location::AsX86() const { namespace x86 { +inline Condition X86Condition(IfCondition cond) { + switch (cond) { + case kCondEQ: return kEqual; + case kCondNE: return kNotEqual; + case kCondLT: return kLess; + case kCondLE: return kLessEqual; + case kCondGT: return kGreater; + case kCondGE: return kGreaterEqual; + default: + LOG(FATAL) << "Unknown if condition"; + } + return kEqual; +} + static constexpr int kNumberOfPushedRegistersAtEntry = 1; static constexpr int kCurrentMethodStackOffset = 0; @@ -421,16 +435,32 @@ void LocationsBuilderX86::VisitIf(HIf* if_instr) { } void InstructionCodeGeneratorX86::VisitIf(HIf* if_instr) { - // TODO: Generate the input as a condition, instead of materializing in a register. - Location location = if_instr->GetLocations()->InAt(0); - if (location.IsRegister()) { - __ cmpl(location.AsX86().AsCpuRegister(), Immediate(0)); + HInstruction* cond = if_instr->InputAt(0); + DCHECK(cond->IsCondition()); + HCondition* condition = cond->AsCondition(); + if (condition->NeedsMaterialization()) { + // Materialized condition, compare against 0 + Location lhs = if_instr->GetLocations()->InAt(0); + if (lhs.IsRegister()) { + __ cmpl(lhs.AsX86().AsCpuRegister(), Immediate(0)); + } else { + __ cmpl(Address(ESP, lhs.GetStackIndex()), Immediate(0)); + } + __ j(kEqual, codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); } else { - __ cmpl(Address(ESP, location.GetStackIndex()), Immediate(0)); + Location lhs = condition->GetLocations()->InAt(0); + Location rhs = condition->GetLocations()->InAt(1); + // LHS is guaranteed to be in a register (see LocationsBuilderX86::VisitCondition). + if (rhs.IsRegister()) { + __ cmpl(lhs.AsX86().AsCpuRegister(), rhs.AsX86().AsCpuRegister()); + } else { + __ cmpl(lhs.AsX86().AsCpuRegister(), Address(ESP, rhs.GetStackIndex())); + } + __ j(X86Condition(condition->GetCondition()), + codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); } - __ j(kEqual, codegen_->GetLabelOf(if_instr->IfFalseSuccessor())); - if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfTrueSuccessor())) { - __ jmp(codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) { + __ jmp(codegen_->GetLabelOf(if_instr->IfFalseSuccessor())); } } @@ -475,24 +505,74 @@ void LocationsBuilderX86::VisitStoreLocal(HStoreLocal* store) { void InstructionCodeGeneratorX86::VisitStoreLocal(HStoreLocal* store) { } -void LocationsBuilderX86::VisitEqual(HEqual* equal) { - LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(equal); +void LocationsBuilderX86::VisitCondition(HCondition* comp) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::Any()); locations->SetOut(Location::SameAsFirstInput()); - equal->SetLocations(locations); + comp->SetLocations(locations); } -void InstructionCodeGeneratorX86::VisitEqual(HEqual* equal) { - LocationSummary* locations = equal->GetLocations(); - if (locations->InAt(1).IsRegister()) { - __ cmpl(locations->InAt(0).AsX86().AsCpuRegister(), - locations->InAt(1).AsX86().AsCpuRegister()); - } else { - __ cmpl(locations->InAt(0).AsX86().AsCpuRegister(), - Address(ESP, locations->InAt(1).GetStackIndex())); +void InstructionCodeGeneratorX86::VisitCondition(HCondition* comp) { + if (comp->NeedsMaterialization()) { + LocationSummary* locations = comp->GetLocations(); + if (locations->InAt(1).IsRegister()) { + __ cmpl(locations->InAt(0).AsX86().AsCpuRegister(), + locations->InAt(1).AsX86().AsCpuRegister()); + } else { + __ cmpl(locations->InAt(0).AsX86().AsCpuRegister(), + Address(ESP, locations->InAt(1).GetStackIndex())); + } + __ setb(X86Condition(comp->GetCondition()), locations->Out().AsX86().AsCpuRegister()); } - __ setb(kEqual, locations->Out().AsX86().AsCpuRegister()); +} + +void LocationsBuilderX86::VisitEqual(HEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorX86::VisitEqual(HEqual* comp) { + VisitCondition(comp); +} + +void LocationsBuilderX86::VisitNotEqual(HNotEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorX86::VisitNotEqual(HNotEqual* comp) { + VisitCondition(comp); +} + +void LocationsBuilderX86::VisitLessThan(HLessThan* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorX86::VisitLessThan(HLessThan* comp) { + VisitCondition(comp); +} + +void LocationsBuilderX86::VisitLessThanOrEqual(HLessThanOrEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorX86::VisitLessThanOrEqual(HLessThanOrEqual* comp) { + VisitCondition(comp); +} + +void LocationsBuilderX86::VisitGreaterThan(HGreaterThan* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorX86::VisitGreaterThan(HGreaterThan* comp) { + VisitCondition(comp); +} + +void LocationsBuilderX86::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorX86::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) { + VisitCondition(comp); } void LocationsBuilderX86::VisitIntConstant(HIntConstant* constant) { @@ -611,7 +691,8 @@ void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) { void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) { Register temp = invoke->GetLocations()->GetTemp(0).AsX86().AsCpuRegister(); - size_t index_in_cache = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + + uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>); + size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).Int32Value() + invoke->GetIndexInDexCache() * kX86WordSize; // TODO: Implement all kinds of calls: diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index ebeef9dfc1..283f1f5e57 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -35,6 +35,20 @@ x86_64::X86_64ManagedRegister Location::AsX86_64() const { namespace x86_64 { +inline Condition X86_64Condition(IfCondition cond) { + switch (cond) { + case kCondEQ: return kEqual; + case kCondNE: return kNotEqual; + case kCondLT: return kLess; + case kCondLE: return kLessEqual; + case kCondGT: return kGreater; + case kCondGE: return kGreaterEqual; + default: + LOG(FATAL) << "Unknown if condition"; + } + return kEqual; +} + // Some x86_64 instructions require a register to be available as temp. static constexpr Register TMP = R11; @@ -295,16 +309,32 @@ void InstructionCodeGeneratorX86_64::VisitExit(HExit* exit) { void LocationsBuilderX86_64::VisitIf(HIf* if_instr) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr); - locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(0, Location::Any()); if_instr->SetLocations(locations); } void InstructionCodeGeneratorX86_64::VisitIf(HIf* if_instr) { - // TODO: Generate the input as a condition, instead of materializing in a register. - __ cmpl(if_instr->GetLocations()->InAt(0).AsX86_64().AsCpuRegister(), Immediate(0)); - __ j(kEqual, codegen_->GetLabelOf(if_instr->IfFalseSuccessor())); - if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfTrueSuccessor())) { - __ jmp(codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + HInstruction* cond = if_instr->InputAt(0); + DCHECK(cond->IsCondition()); + HCondition* condition = cond->AsCondition(); + if (condition->NeedsMaterialization()) { + // Materialized condition, compare against 0. + Location lhs = if_instr->GetLocations()->InAt(0); + if (lhs.IsRegister()) { + __ cmpl(lhs.AsX86_64().AsCpuRegister(), Immediate(0)); + } else { + __ cmpl(Address(CpuRegister(RSP), lhs.GetStackIndex()), Immediate(0)); + } + __ j(kEqual, codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + } else { + Location lhs = condition->GetLocations()->InAt(0); + Location rhs = condition->GetLocations()->InAt(1); + __ cmpl(lhs.AsX86_64().AsCpuRegister(), rhs.AsX86_64().AsCpuRegister()); + __ j(X86_64Condition(condition->GetCondition()), + codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + } + if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) { + __ jmp(codegen_->GetLabelOf(if_instr->IfFalseSuccessor())); } } @@ -349,18 +379,69 @@ void LocationsBuilderX86_64::VisitStoreLocal(HStoreLocal* store) { void InstructionCodeGeneratorX86_64::VisitStoreLocal(HStoreLocal* store) { } -void LocationsBuilderX86_64::VisitEqual(HEqual* equal) { - LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(equal); +void LocationsBuilderX86_64::VisitCondition(HCondition* comp) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); locations->SetOut(Location::SameAsFirstInput()); - equal->SetLocations(locations); + comp->SetLocations(locations); +} + +void InstructionCodeGeneratorX86_64::VisitCondition(HCondition* comp) { + if (comp->NeedsMaterialization()) { + __ cmpq(comp->GetLocations()->InAt(0).AsX86_64().AsCpuRegister(), + comp->GetLocations()->InAt(1).AsX86_64().AsCpuRegister()); + __ setcc(X86_64Condition(comp->GetCondition()), + comp->GetLocations()->Out().AsX86_64().AsCpuRegister()); + } +} + +void LocationsBuilderX86_64::VisitEqual(HEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorX86_64::VisitEqual(HEqual* comp) { + VisitCondition(comp); +} + +void LocationsBuilderX86_64::VisitNotEqual(HNotEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorX86_64::VisitNotEqual(HNotEqual* comp) { + VisitCondition(comp); +} + +void LocationsBuilderX86_64::VisitLessThan(HLessThan* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorX86_64::VisitLessThan(HLessThan* comp) { + VisitCondition(comp); +} + +void LocationsBuilderX86_64::VisitLessThanOrEqual(HLessThanOrEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorX86_64::VisitLessThanOrEqual(HLessThanOrEqual* comp) { + VisitCondition(comp); +} + +void LocationsBuilderX86_64::VisitGreaterThan(HGreaterThan* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorX86_64::VisitGreaterThan(HGreaterThan* comp) { + VisitCondition(comp); +} + +void LocationsBuilderX86_64::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) { + VisitCondition(comp); } -void InstructionCodeGeneratorX86_64::VisitEqual(HEqual* equal) { - __ cmpq(equal->GetLocations()->InAt(0).AsX86_64().AsCpuRegister(), - equal->GetLocations()->InAt(1).AsX86_64().AsCpuRegister()); - __ setcc(kEqual, equal->GetLocations()->Out().AsX86_64().AsCpuRegister()); +void InstructionCodeGeneratorX86_64::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) { + VisitCondition(comp); } void LocationsBuilderX86_64::VisitIntConstant(HIntConstant* constant) { diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc index c3baf1a7b7..7ec0c84167 100644 --- a/compiler/optimizing/codegen_test.cc +++ b/compiler/optimizing/codegen_test.cc @@ -51,7 +51,8 @@ class InternalCodeAllocator : public CodeAllocator { static void Run(const InternalCodeAllocator& allocator, bool has_result, int32_t expected) { typedef int32_t (*fptr)(); CommonCompilerTest::MakeExecutable(allocator.GetMemory(), allocator.GetSize()); - int32_t result = reinterpret_cast<fptr>(allocator.GetMemory())(); + fptr f = reinterpret_cast<fptr>(allocator.GetMemory()); + int32_t result = f(); if (has_result) { CHECK_EQ(result, expected); } diff --git a/compiler/optimizing/graph_test.cc b/compiler/optimizing/graph_test.cc index 371478c9e7..c59f8366fa 100644 --- a/compiler/optimizing/graph_test.cc +++ b/compiler/optimizing/graph_test.cc @@ -30,7 +30,9 @@ static HBasicBlock* createIfBlock(HGraph* graph, ArenaAllocator* allocator) { graph->AddBlock(if_block); HInstruction* instr = new (allocator) HIntConstant(4); if_block->AddInstruction(instr); - instr = new (allocator) HIf(instr); + HInstruction* equal = new (allocator) HEqual(instr, instr); + if_block->AddInstruction(equal); + instr = new (allocator) HIf(equal); if_block->AddInstruction(instr); return if_block; } diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 2a97fadbaf..490d345826 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -445,4 +445,23 @@ void HGraphVisitor::VisitBasicBlock(HBasicBlock* block) { } } + +bool HCondition::NeedsMaterialization() const { + if (!HasOnlyOneUse()) { + return true; + } + HUseListNode<HInstruction>* uses = GetUses(); + HInstruction* user = uses->GetUser(); + if (!user->IsIf()) { + return true; + } + + // TODO: should we allow intervening instructions with no side-effect between this condition + // and the If instruction? + if (GetNext() != user) { + return true; + } + return false; +} + } // namespace art diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 143d5c9e6f..503f31d990 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -38,6 +38,15 @@ static const int kDefaultNumberOfSuccessors = 2; static const int kDefaultNumberOfPredecessors = 2; static const int kDefaultNumberOfBackEdges = 1; +enum IfCondition { + kCondEQ, + kCondNE, + kCondLT, + kCondLE, + kCondGT, + kCondGE, +}; + class HInstructionList { public: HInstructionList() : first_instruction_(nullptr), last_instruction_(nullptr) {} @@ -66,7 +75,7 @@ class HGraph : public ArenaObject { maximum_number_of_out_vregs_(0), number_of_vregs_(0), number_of_in_vregs_(0), - current_instruction_id_(0) { } + current_instruction_id_(0) {} ArenaAllocator* GetArena() const { return arena_; } const GrowableArray<HBasicBlock*>& GetBlocks() const { return blocks_; } @@ -381,7 +390,13 @@ class HBasicBlock : public ArenaObject { #define FOR_EACH_INSTRUCTION(M) \ M(Add) \ + M(Condition) \ M(Equal) \ + M(NotEqual) \ + M(LessThan) \ + M(LessThanOrEqual) \ + M(GreaterThan) \ + M(GreaterThanOrEqual) \ M(Exit) \ M(Goto) \ M(If) \ @@ -400,6 +415,7 @@ class HBasicBlock : public ArenaObject { M(StoreLocal) \ M(Sub) \ + #define FORWARD_DECLARATION(type) class H##type; FOR_EACH_INSTRUCTION(FORWARD_DECLARATION) #undef FORWARD_DECLARATION @@ -413,7 +429,7 @@ template <typename T> class HUseListNode : public ArenaObject { public: HUseListNode(T* user, size_t index, HUseListNode* tail) - : user_(user), index_(index), tail_(tail) { } + : user_(user), index_(index), tail_(tail) {} HUseListNode* GetTail() const { return tail_; } T* GetUser() const { return user_; } @@ -444,7 +460,7 @@ class HInstruction : public ArenaObject { live_interval_(nullptr), lifetime_position_(kNoLifetime) {} - virtual ~HInstruction() { } + virtual ~HInstruction() {} HInstruction* GetNext() const { return next_; } HInstruction* GetPrevious() const { return previous_; } @@ -507,6 +523,10 @@ class HInstruction : public ArenaObject { void ReplaceWith(HInstruction* instruction); + bool HasOnlyOneUse() const { + return uses_ != nullptr && uses_->GetTail() == nullptr; + } + #define INSTRUCTION_TYPE_CHECK(type) \ bool Is##type() { return (As##type() != nullptr); } \ virtual H##type* As##type() { return nullptr; } @@ -616,7 +636,7 @@ class HEnvironment : public ArenaObject { class HInputIterator : public ValueObject { public: - explicit HInputIterator(HInstruction* instruction) : instruction_(instruction), index_(0) { } + explicit HInputIterator(HInstruction* instruction) : instruction_(instruction), index_(0) {} bool Done() const { return index_ == instruction_->InputCount(); } HInstruction* Current() const { return instruction_->InputAt(index_); } @@ -676,7 +696,7 @@ class HBackwardInstructionIterator : public ValueObject { template<typename T, intptr_t N> class EmbeddedArray { public: - EmbeddedArray() : elements_() { } + EmbeddedArray() : elements_() {} intptr_t GetLength() const { return N; } @@ -721,8 +741,8 @@ class EmbeddedArray<T, 0> { template<intptr_t N> class HTemplateInstruction: public HInstruction { public: - HTemplateInstruction<N>() : inputs_() { } - virtual ~HTemplateInstruction() { } + HTemplateInstruction<N>() : inputs_() {} + virtual ~HTemplateInstruction() {} virtual size_t InputCount() const { return N; } virtual HInstruction* InputAt(size_t i) const { return inputs_[i]; } @@ -738,6 +758,18 @@ class HTemplateInstruction: public HInstruction { friend class SsaBuilder; }; +template<intptr_t N> +class HExpression: public HTemplateInstruction<N> { + public: + explicit HExpression<N>(Primitive::Type type) : type_(type) {} + virtual ~HExpression() {} + + virtual Primitive::Type GetType() const { return type_; } + + private: + const Primitive::Type type_; +}; + // Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow // instruction that branches to the exit block. class HReturnVoid : public HTemplateInstruction<0> { @@ -800,6 +832,7 @@ class HGoto : public HTemplateInstruction<0> { DISALLOW_COPY_AND_ASSIGN(HGoto); }; + // Conditional branch. A block ending with an HIf instruction must have // two successors. class HIf : public HTemplateInstruction<1> { @@ -820,53 +853,143 @@ class HIf : public HTemplateInstruction<1> { DECLARE_INSTRUCTION(If); + virtual bool IsIfInstruction() const { return true; } + private: DISALLOW_COPY_AND_ASSIGN(HIf); }; -class HBinaryOperation : public HTemplateInstruction<2> { +class HBinaryOperation : public HExpression<2> { public: HBinaryOperation(Primitive::Type result_type, HInstruction* left, - HInstruction* right) : result_type_(result_type) { + HInstruction* right) : HExpression(result_type) { SetRawInputAt(0, left); SetRawInputAt(1, right); } HInstruction* GetLeft() const { return InputAt(0); } HInstruction* GetRight() const { return InputAt(1); } - Primitive::Type GetResultType() const { return result_type_; } + Primitive::Type GetResultType() const { return GetType(); } virtual bool IsCommutative() { return false; } - virtual Primitive::Type GetType() const { return GetResultType(); } private: - const Primitive::Type result_type_; - DISALLOW_COPY_AND_ASSIGN(HBinaryOperation); }; - -// Instruction to check if two inputs are equal to each other. -class HEqual : public HBinaryOperation { +class HCondition : public HBinaryOperation { public: - HEqual(HInstruction* first, HInstruction* second) + HCondition(HInstruction* first, HInstruction* second) : HBinaryOperation(Primitive::kPrimBoolean, first, second) {} virtual bool IsCommutative() { return true; } + bool NeedsMaterialization() const; - virtual Primitive::Type GetType() const { return Primitive::kPrimBoolean; } + DECLARE_INSTRUCTION(Condition); + + virtual IfCondition GetCondition() const = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(HCondition); +}; + +// Instruction to check if two inputs are equal to each other. +class HEqual : public HCondition { + public: + HEqual(HInstruction* first, HInstruction* second) + : HCondition(first, second) {} DECLARE_INSTRUCTION(Equal); + virtual IfCondition GetCondition() const { + return kCondEQ; + } + private: DISALLOW_COPY_AND_ASSIGN(HEqual); }; +class HNotEqual : public HCondition { + public: + HNotEqual(HInstruction* first, HInstruction* second) + : HCondition(first, second) {} + + DECLARE_INSTRUCTION(NotEqual); + + virtual IfCondition GetCondition() const { + return kCondNE; + } + + private: + DISALLOW_COPY_AND_ASSIGN(HNotEqual); +}; + +class HLessThan : public HCondition { + public: + HLessThan(HInstruction* first, HInstruction* second) + : HCondition(first, second) {} + + DECLARE_INSTRUCTION(LessThan); + + virtual IfCondition GetCondition() const { + return kCondLT; + } + + private: + DISALLOW_COPY_AND_ASSIGN(HLessThan); +}; + +class HLessThanOrEqual : public HCondition { + public: + HLessThanOrEqual(HInstruction* first, HInstruction* second) + : HCondition(first, second) {} + + DECLARE_INSTRUCTION(LessThanOrEqual); + + virtual IfCondition GetCondition() const { + return kCondLE; + } + + private: + DISALLOW_COPY_AND_ASSIGN(HLessThanOrEqual); +}; + +class HGreaterThan : public HCondition { + public: + HGreaterThan(HInstruction* first, HInstruction* second) + : HCondition(first, second) {} + + DECLARE_INSTRUCTION(GreaterThan); + + virtual IfCondition GetCondition() const { + return kCondGT; + } + + private: + DISALLOW_COPY_AND_ASSIGN(HGreaterThan); +}; + +class HGreaterThanOrEqual : public HCondition { + public: + HGreaterThanOrEqual(HInstruction* first, HInstruction* second) + : HCondition(first, second) {} + + DECLARE_INSTRUCTION(GreaterThanOrEqual); + + virtual IfCondition GetCondition() const { + return kCondGE; + } + + private: + DISALLOW_COPY_AND_ASSIGN(HGreaterThanOrEqual); +}; + + // A local in the graph. Corresponds to a Dex register. class HLocal : public HTemplateInstruction<0> { public: - explicit HLocal(uint16_t reg_number) : reg_number_(reg_number) { } + explicit HLocal(uint16_t reg_number) : reg_number_(reg_number) {} DECLARE_INSTRUCTION(Local); @@ -880,21 +1003,17 @@ class HLocal : public HTemplateInstruction<0> { }; // Load a given local. The local is an input of this instruction. -class HLoadLocal : public HTemplateInstruction<1> { +class HLoadLocal : public HExpression<1> { public: - explicit HLoadLocal(HLocal* local, Primitive::Type type) : type_(type) { + explicit HLoadLocal(HLocal* local, Primitive::Type type) : HExpression(type) { SetRawInputAt(0, local); } - virtual Primitive::Type GetType() const { return type_; } - HLocal* GetLocal() const { return reinterpret_cast<HLocal*>(InputAt(0)); } DECLARE_INSTRUCTION(LoadLocal); private: - const Primitive::Type type_; - DISALLOW_COPY_AND_ASSIGN(HLoadLocal); }; @@ -917,12 +1036,11 @@ class HStoreLocal : public HTemplateInstruction<2> { // Constants of the type int. Those can be from Dex instructions, or // synthesized (for example with the if-eqz instruction). -class HIntConstant : public HTemplateInstruction<0> { +class HIntConstant : public HExpression<0> { public: - explicit HIntConstant(int32_t value) : value_(value) { } + explicit HIntConstant(int32_t value) : HExpression(Primitive::kPrimInt), value_(value) {} int32_t GetValue() const { return value_; } - virtual Primitive::Type GetType() const { return Primitive::kPrimInt; } DECLARE_INSTRUCTION(IntConstant); @@ -932,9 +1050,9 @@ class HIntConstant : public HTemplateInstruction<0> { DISALLOW_COPY_AND_ASSIGN(HIntConstant); }; -class HLongConstant : public HTemplateInstruction<0> { +class HLongConstant : public HExpression<0> { public: - explicit HLongConstant(int64_t value) : value_(value) { } + explicit HLongConstant(int64_t value) : HExpression(Primitive::kPrimLong), value_(value) {} int64_t GetValue() const { return value_; } @@ -1008,15 +1126,14 @@ class HInvokeStatic : public HInvoke { DISALLOW_COPY_AND_ASSIGN(HInvokeStatic); }; -class HNewInstance : public HTemplateInstruction<0> { +class HNewInstance : public HExpression<0> { public: - HNewInstance(uint32_t dex_pc, uint16_t type_index) : dex_pc_(dex_pc), type_index_(type_index) {} + HNewInstance(uint32_t dex_pc, uint16_t type_index) : HExpression(Primitive::kPrimNot), + dex_pc_(dex_pc), type_index_(type_index) {} uint32_t GetDexPc() const { return dex_pc_; } uint16_t GetTypeIndex() const { return type_index_; } - virtual Primitive::Type GetType() const { return Primitive::kPrimNot; } - // Calls runtime so needs an environment. virtual bool NeedsEnvironment() const { return true; } @@ -1057,15 +1174,13 @@ class HSub : public HBinaryOperation { // The value of a parameter in this method. Its location depends on // the calling convention. -class HParameterValue : public HTemplateInstruction<0> { +class HParameterValue : public HExpression<0> { public: HParameterValue(uint8_t index, Primitive::Type parameter_type) - : index_(index), parameter_type_(parameter_type) {} + : HExpression(parameter_type), index_(index) {} uint8_t GetIndex() const { return index_; } - virtual Primitive::Type GetType() const { return parameter_type_; } - DECLARE_INSTRUCTION(ParameterValue); private: @@ -1073,19 +1188,15 @@ class HParameterValue : public HTemplateInstruction<0> { // than HGraph::number_of_in_vregs_; const uint8_t index_; - const Primitive::Type parameter_type_; - DISALLOW_COPY_AND_ASSIGN(HParameterValue); }; -class HNot : public HTemplateInstruction<1> { +class HNot : public HExpression<1> { public: - explicit HNot(HInstruction* input) { + explicit HNot(HInstruction* input) : HExpression(Primitive::kPrimBoolean) { SetRawInputAt(0, input); } - virtual Primitive::Type GetType() const { return Primitive::kPrimBoolean; } - DECLARE_INSTRUCTION(Not); private: @@ -1210,10 +1321,10 @@ class HParallelMove : public HTemplateInstruction<0> { class HGraphVisitor : public ValueObject { public: - explicit HGraphVisitor(HGraph* graph) : graph_(graph) { } - virtual ~HGraphVisitor() { } + explicit HGraphVisitor(HGraph* graph) : graph_(graph) {} + virtual ~HGraphVisitor() {} - virtual void VisitInstruction(HInstruction* instruction) { } + virtual void VisitInstruction(HInstruction* instruction) {} virtual void VisitBasicBlock(HBasicBlock* block); void VisitInsertionOrder(); diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index 348e9d4921..1f4cb41582 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -100,6 +100,9 @@ void RegisterAllocator::BlockRegister(Location location, interval->AddRange(start, end); } +// TODO: make the register allocator understand instructions like HCondition +// that may not need to be materialized. It doesn't need to allocate any +// registers for it. void RegisterAllocator::AllocateRegistersInternal() { number_of_registers_ = processing_core_registers_ ? codegen_->GetNumberOfCoreRegisters() diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc index bfabc5ad27..a7283ab329 100644 --- a/compiler/optimizing/register_allocator_test.cc +++ b/compiler/optimizing/register_allocator_test.cc @@ -318,4 +318,42 @@ TEST(RegisterAllocatorTest, Loop3) { ASSERT_EQ(phi_interval->GetRegister(), ret->InputAt(0)->GetLiveInterval()->GetRegister()); } +TEST(RegisterAllocatorTest, FirstRegisterUse) { + const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( + Instruction::CONST_4 | 0 | 0, + Instruction::ADD_INT_LIT8 | 1 << 8, 1 << 8, + Instruction::ADD_INT_LIT8 | 0 << 8, 1 << 8, + Instruction::ADD_INT_LIT8 | 1 << 8, 1 << 8 | 1, + Instruction::RETURN_VOID); + + ArenaPool pool; + ArenaAllocator allocator(&pool); + HGraph* graph = BuildSSAGraph(data, &allocator); + CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, kArm); + SsaLivenessAnalysis liveness(*graph, codegen); + liveness.Analyze(); + + HAdd* first_add = graph->GetBlocks().Get(1)->GetFirstInstruction()->AsAdd(); + HAdd* last_add = graph->GetBlocks().Get(1)->GetLastInstruction()->GetPrevious()->AsAdd(); + ASSERT_EQ(last_add->InputAt(0), first_add); + LiveInterval* interval = first_add->GetLiveInterval(); + ASSERT_EQ(interval->GetEnd(), last_add->GetLifetimePosition() + 1); + ASSERT_TRUE(interval->GetNextSibling() == nullptr); + + // We need a register for the output of the instruction. + ASSERT_EQ(interval->FirstRegisterUse(), first_add->GetLifetimePosition()); + + // Split at the next instruction. + interval = interval->SplitAt(first_add->GetLifetimePosition() + 2); + // The user of the split is the last add. + ASSERT_EQ(interval->FirstRegisterUse(), last_add->GetLifetimePosition() + 1); + + // Split before the last add. + LiveInterval* new_interval = interval->SplitAt(last_add->GetLifetimePosition() - 1); + // Ensure the current interval has no register use... + ASSERT_EQ(interval->FirstRegisterUse(), kNoLifetime); + // And the new interval has it for the last add. + ASSERT_EQ(new_interval->FirstRegisterUse(), last_add->GetLifetimePosition() + 1); +} + } // namespace art diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index fc3eb660d5..83035b52f6 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -289,21 +289,23 @@ class LiveInterval : public ArenaObject { size_t FirstRegisterUseAfter(size_t position) const { if (position == GetStart() && defined_by_ != nullptr) { - Location location = defined_by_->GetLocations()->Out(); + LocationSummary* locations = defined_by_->GetLocations(); + Location location = locations->Out(); // This interval is the first interval of the instruction. If the output // of the instruction requires a register, we return the position of that instruction // as the first register use. if (location.IsUnallocated()) { if ((location.GetPolicy() == Location::kRequiresRegister) || (location.GetPolicy() == Location::kSameAsFirstInput - && defined_by_->GetLocations()->InAt(0).GetPolicy() == Location::kRequiresRegister)) { + && locations->InAt(0).GetPolicy() == Location::kRequiresRegister)) { return position; } } } UsePosition* use = first_use_; - while (use != nullptr) { + size_t end = GetEnd(); + while (use != nullptr && use->GetPosition() <= end) { size_t use_position = use->GetPosition(); if (use_position >= position && !use->GetIsEnvironment()) { Location location = use->GetUser()->GetLocations()->InAt(use->GetInputIndex()); diff --git a/compiler/trampolines/trampoline_compiler.cc b/compiler/trampolines/trampoline_compiler.cc index ac84d6ae40..d5225c1f73 100644 --- a/compiler/trampolines/trampoline_compiler.cc +++ b/compiler/trampolines/trampoline_compiler.cc @@ -30,11 +30,7 @@ namespace art { namespace arm { static const std::vector<uint8_t>* CreateTrampoline(EntryPointCallingConvention abi, ThreadOffset<4> offset) { - // NOTE: the assembler used here is ARM, not Thumb. This is because the address - // returned by this function is a pointer and for thumb we would have to set the - // bottom bit. It doesn't matter since the instructions generated are the same - // size anyway. - std::unique_ptr<ArmAssembler> assembler(static_cast<ArmAssembler*>(Assembler::Create(kArm))); + std::unique_ptr<ArmAssembler> assembler(static_cast<ArmAssembler*>(Assembler::Create(kThumb2))); switch (abi) { case kInterpreterAbi: // Thread* is first argument (R0) in interpreter ABI. diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc index b607a1db3a..8a34928a59 100644 --- a/compiler/utils/arm/assembler_arm.cc +++ b/compiler/utils/arm/assembler_arm.cc @@ -111,43 +111,38 @@ uint32_t ShifterOperand::encodingArm() const { } } -uint32_t ShifterOperand::encodingThumb(int version) const { - CHECK(version == 1 || version == 2); - if (version == 1) { - LOG(FATAL) << "Invalid of use encodingThumb with version 1"; - } else { - switch (type_) { - case kImmediate: - return immed_; - case kRegister: - if (is_shift_) { - // Shifted immediate or register. - if (rs_ == kNoRegister) { - // Immediate shift. - if (shift_ == RRX) { - // RRX is encoded as an ROR with imm 0. - return ROR << 4 | static_cast<uint32_t>(rm_); - } else { - uint32_t imm3 = immed_ >> 2; - uint32_t imm2 = immed_ & 0b11; - - return imm3 << 12 | imm2 << 6 | shift_ << 4 | - static_cast<uint32_t>(rm_); - } +uint32_t ShifterOperand::encodingThumb() const { + switch (type_) { + case kImmediate: + return immed_; + case kRegister: + if (is_shift_) { + // Shifted immediate or register. + if (rs_ == kNoRegister) { + // Immediate shift. + if (shift_ == RRX) { + // RRX is encoded as an ROR with imm 0. + return ROR << 4 | static_cast<uint32_t>(rm_); } else { - LOG(FATAL) << "No register-shifted register instruction available in thumb"; - return 0; + uint32_t imm3 = immed_ >> 2; + uint32_t imm2 = immed_ & 0b11; + + return imm3 << 12 | imm2 << 6 | shift_ << 4 | + static_cast<uint32_t>(rm_); } } else { - // Simple register - return static_cast<uint32_t>(rm_); + LOG(FATAL) << "No register-shifted register instruction available in thumb"; + return 0; } - break; - default: - // Can't get here. - LOG(FATAL) << "Invalid shifter operand for thumb"; - return 0; - } + } else { + // Simple register + return static_cast<uint32_t>(rm_); + } + break; + default: + // Can't get here. + LOG(FATAL) << "Invalid shifter operand for thumb"; + return 0; } return 0; } @@ -187,51 +182,78 @@ bool ShifterOperand::CanHoldThumb(Register rd, Register rn, Opcode opcode, uint32_t Address::encodingArm() const { CHECK(IsAbsoluteUint(12, offset_)); uint32_t encoding; - if (offset_ < 0) { - encoding = (am_ ^ (1 << kUShift)) | -offset_; // Flip U to adjust sign. + if (is_immed_offset_) { + if (offset_ < 0) { + encoding = (am_ ^ (1 << kUShift)) | -offset_; // Flip U to adjust sign. + } else { + encoding = am_ | offset_; + } } else { - encoding = am_ | offset_; + uint32_t imm5 = offset_; + uint32_t shift = shift_; + if (shift == RRX) { + imm5 = 0; + shift = ROR; + } + encoding = am_ | static_cast<uint32_t>(rm_) | shift << 5 | offset_ << 7 | B25; } encoding |= static_cast<uint32_t>(rn_) << kRnShift; return encoding; } -uint32_t Address::encodingThumb(int version) const { - CHECK(version == 1 || version == 2); +uint32_t Address::encodingThumb(bool is_32bit) const { uint32_t encoding = 0; - if (version == 2) { - encoding = static_cast<uint32_t>(rn_) << 16; - // Check for the T3/T4 encoding. - // PUW must Offset for T3 - // Convert ARM PU0W to PUW - // The Mode is in ARM encoding format which is: - // |P|U|0|W| - // we need this in thumb2 mode: - // |P|U|W| - - uint32_t am = am_; - int32_t offset = offset_; - if (offset < 0) { - am ^= 1 << kUShift; - offset = -offset; - } - if (offset_ < 0 || (offset >= 0 && offset < 256 && + if (is_immed_offset_) { + encoding = static_cast<uint32_t>(rn_) << 16; + // Check for the T3/T4 encoding. + // PUW must Offset for T3 + // Convert ARM PU0W to PUW + // The Mode is in ARM encoding format which is: + // |P|U|0|W| + // we need this in thumb2 mode: + // |P|U|W| + + uint32_t am = am_; + int32_t offset = offset_; + if (offset < 0) { + am ^= 1 << kUShift; + offset = -offset; + } + if (offset_ < 0 || (offset >= 0 && offset < 256 && am_ != Mode::Offset)) { - // T4 encoding. - uint32_t PUW = am >> 21; // Move down to bottom of word. - PUW = (PUW >> 1) | (PUW & 1); // Bits 3, 2 and 0. - // If P is 0 then W must be 1 (Different from ARM). - if ((PUW & 0b100) == 0) { - PUW |= 0b1; - } - encoding |= B11 | PUW << 8 | offset; - } else { - // T3 encoding (also sets op1 to 0b01). - encoding |= B23 | offset_; + // T4 encoding. + uint32_t PUW = am >> 21; // Move down to bottom of word. + PUW = (PUW >> 1) | (PUW & 1); // Bits 3, 2 and 0. + // If P is 0 then W must be 1 (Different from ARM). + if ((PUW & 0b100) == 0) { + PUW |= 0b1; } + encoding |= B11 | PUW << 8 | offset; + } else { + // T3 encoding (also sets op1 to 0b01). + encoding |= B23 | offset_; + } } else { - LOG(FATAL) << "Invalid use of encodingThumb for version 1"; + // Register offset, possibly shifted. + // Need to choose between encoding T1 (16 bit) or T2. + // Only Offset mode is supported. Shift must be LSL and the count + // is only 2 bits. + CHECK_EQ(shift_, LSL); + CHECK_LE(offset_, 4); + CHECK_EQ(am_, Offset); + bool is_t2 = is_32bit; + if (ArmAssembler::IsHighRegister(rn_) || ArmAssembler::IsHighRegister(rm_)) { + is_t2 = true; + } else if (offset_ != 0) { + is_t2 = true; + } + if (is_t2) { + encoding = static_cast<uint32_t>(rn_) << 16 | static_cast<uint32_t>(rm_) | + offset_ << 4; + } else { + encoding = static_cast<uint32_t>(rn_) << 3 | static_cast<uint32_t>(rm_) << 6; + } } return encoding; } diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h index 7b662e1500..be19174093 100644 --- a/compiler/utils/arm/assembler_arm.h +++ b/compiler/utils/arm/assembler_arm.h @@ -68,7 +68,7 @@ class ShifterOperand { } uint32_t encodingArm() const; - uint32_t encodingThumb(int version) const; + uint32_t encodingThumb() const; bool IsEmpty() const { return type_ == kUnknown; @@ -196,8 +196,26 @@ class Address { NegPostIndex = (0|0|0) << 21 // negative post-indexed with writeback }; - explicit Address(Register rn, int32_t offset = 0, Mode am = Offset) : rn_(rn), offset_(offset), - am_(am) { + Address(Register rn, int32_t offset = 0, Mode am = Offset) : rn_(rn), rm_(R0), + offset_(offset), + am_(am), is_immed_offset_(true), shift_(LSL) { + } + + Address(Register rn, Register rm, Mode am = Offset) : rn_(rn), rm_(rm), offset_(0), + am_(am), is_immed_offset_(false), shift_(LSL) { + CHECK_NE(rm, PC); + } + + Address(Register rn, Register rm, Shift shift, uint32_t count, Mode am = Offset) : + rn_(rn), rm_(rm), offset_(count), + am_(am), is_immed_offset_(false), shift_(shift) { + CHECK_NE(rm, PC); + } + + // LDR(literal) - pc relative load. + explicit Address(int32_t offset) : + rn_(PC), rm_(R0), offset_(offset), + am_(Offset), is_immed_offset_(false), shift_(LSL) { } static bool CanHoldLoadOffsetArm(LoadOperandType type, int offset); @@ -207,7 +225,7 @@ class Address { static bool CanHoldStoreOffsetThumb(StoreOperandType type, int offset); uint32_t encodingArm() const; - uint32_t encodingThumb(int version) const; + uint32_t encodingThumb(bool is_32bit) const; uint32_t encoding3() const; uint32_t vencoding() const; @@ -218,6 +236,10 @@ class Address { return rn_; } + Register GetRegisterOffset() const { + return rm_; + } + int32_t GetOffset() const { return offset_; } @@ -226,10 +248,26 @@ class Address { return am_; } + bool IsImmediate() const { + return is_immed_offset_; + } + + Shift GetShift() const { + return shift_; + } + + int32_t GetShiftCount() const { + CHECK(!is_immed_offset_); + return offset_; + } + private: Register rn_; - int32_t offset_; + Register rm_; + int32_t offset_; // Used as shift amount for register offset. Mode am_; + bool is_immed_offset_; + Shift shift_; }; // Instruction encoding bits. @@ -544,11 +582,25 @@ class ArmAssembler : public Assembler { // Convenience shift instructions. Use mov instruction with shifter operand // for variants setting the status flags or using a register shift count. - virtual void Lsl(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL) = 0; - virtual void Lsr(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL) = 0; - virtual void Asr(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL) = 0; - virtual void Ror(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL) = 0; - virtual void Rrx(Register rd, Register rm, Condition cond = AL) = 0; + virtual void Lsl(Register rd, Register rm, uint32_t shift_imm, bool setcc = false, + Condition cond = AL) = 0; + virtual void Lsr(Register rd, Register rm, uint32_t shift_imm, bool setcc = false, + Condition cond = AL) = 0; + virtual void Asr(Register rd, Register rm, uint32_t shift_imm, bool setcc = false, + Condition cond = AL) = 0; + virtual void Ror(Register rd, Register rm, uint32_t shift_imm, bool setcc = false, + Condition cond = AL) = 0; + virtual void Rrx(Register rd, Register rm, bool setcc = false, + Condition cond = AL) = 0; + + virtual void Lsl(Register rd, Register rm, Register rn, bool setcc = false, + Condition cond = AL) = 0; + virtual void Lsr(Register rd, Register rm, Register rn, bool setcc = false, + Condition cond = AL) = 0; + virtual void Asr(Register rd, Register rm, Register rn, bool setcc = false, + Condition cond = AL) = 0; + virtual void Ror(Register rd, Register rm, Register rn, bool setcc = false, + Condition cond = AL) = 0; static bool IsInstructionForExceptionHandling(uword pc); @@ -673,6 +725,14 @@ class ArmAssembler : public Assembler { static uint32_t ModifiedImmediate(uint32_t value); + static bool IsLowRegister(Register r) { + return r < R8; + } + + static bool IsHighRegister(Register r) { + return r >= R8; + } + protected: // Returns whether or not the given register is used for passing parameters. static int RegisterCompare(const Register* reg1, const Register* reg2) { diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc index b2bb20fa2d..267bba8521 100644 --- a/compiler/utils/arm/assembler_arm32.cc +++ b/compiler/utils/arm/assembler_arm32.cc @@ -541,20 +541,40 @@ void Arm32Assembler::EmitType5(Condition cond, int offset, bool link) { void Arm32Assembler::EmitMemOp(Condition cond, - bool load, - bool byte, - Register rd, - const Address& ad) { + bool load, + bool byte, + Register rd, + const Address& ad) { CHECK_NE(rd, kNoRegister); CHECK_NE(cond, kNoCondition); const Address& addr = static_cast<const Address&>(ad); - int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | - B26 | - (load ? L : 0) | - (byte ? B : 0) | - (static_cast<int32_t>(rd) << kRdShift) | - addr.encodingArm(); + int32_t encoding = 0; + if (!ad.IsImmediate() && ad.GetRegisterOffset() == PC) { + // PC relative LDR(literal) + int32_t offset = ad.GetOffset(); + int32_t u = B23; + if (offset < 0) { + offset = -offset; + u = 0; + } + CHECK_LT(offset, (1 << 12)); + encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B26 | B24 | u | B20 | + (load ? L : 0) | + (byte ? B : 0) | + (static_cast<int32_t>(rd) << kRdShift) | + 0xf << 16 | + (offset & 0xfff); + + } else { + encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B26 | + (load ? L : 0) | + (byte ? B : 0) | + (static_cast<int32_t>(rd) << kRdShift) | + addr.encodingArm(); + } Emit(encoding); } @@ -1020,39 +1040,98 @@ void Arm32Assembler::EmitVFPds(Condition cond, int32_t opcode, void Arm32Assembler::Lsl(Register rd, Register rm, uint32_t shift_imm, - Condition cond) { + bool setcc, Condition cond) { CHECK_NE(shift_imm, 0u); // Do not use Lsl if no shift is wanted. - mov(rd, ShifterOperand(rm, LSL, shift_imm), cond); + if (setcc) { + movs(rd, ShifterOperand(rm, LSL, shift_imm), cond); + } else { + mov(rd, ShifterOperand(rm, LSL, shift_imm), cond); + } } void Arm32Assembler::Lsr(Register rd, Register rm, uint32_t shift_imm, - Condition cond) { + bool setcc, Condition cond) { CHECK_NE(shift_imm, 0u); // Do not use Lsr if no shift is wanted. if (shift_imm == 32) shift_imm = 0; // Comply to UAL syntax. - mov(rd, ShifterOperand(rm, LSR, shift_imm), cond); + if (setcc) { + movs(rd, ShifterOperand(rm, LSR, shift_imm), cond); + } else { + mov(rd, ShifterOperand(rm, LSR, shift_imm), cond); + } } void Arm32Assembler::Asr(Register rd, Register rm, uint32_t shift_imm, - Condition cond) { + bool setcc, Condition cond) { CHECK_NE(shift_imm, 0u); // Do not use Asr if no shift is wanted. if (shift_imm == 32) shift_imm = 0; // Comply to UAL syntax. - mov(rd, ShifterOperand(rm, ASR, shift_imm), cond); + if (setcc) { + movs(rd, ShifterOperand(rm, ASR, shift_imm), cond); + } else { + mov(rd, ShifterOperand(rm, ASR, shift_imm), cond); + } } void Arm32Assembler::Ror(Register rd, Register rm, uint32_t shift_imm, - Condition cond) { + bool setcc, Condition cond) { CHECK_NE(shift_imm, 0u); // Use Rrx instruction. - mov(rd, ShifterOperand(rm, ROR, shift_imm), cond); + if (setcc) { + movs(rd, ShifterOperand(rm, ROR, shift_imm), cond); + } else { + mov(rd, ShifterOperand(rm, ROR, shift_imm), cond); + } +} + +void Arm32Assembler::Rrx(Register rd, Register rm, bool setcc, Condition cond) { + if (setcc) { + movs(rd, ShifterOperand(rm, ROR, 0), cond); + } else { + mov(rd, ShifterOperand(rm, ROR, 0), cond); + } +} + + +void Arm32Assembler::Lsl(Register rd, Register rm, Register rn, + bool setcc, Condition cond) { + if (setcc) { + movs(rd, ShifterOperand(rm, LSL, rn), cond); + } else { + mov(rd, ShifterOperand(rm, LSL, rn), cond); + } +} + + +void Arm32Assembler::Lsr(Register rd, Register rm, Register rn, + bool setcc, Condition cond) { + if (setcc) { + movs(rd, ShifterOperand(rm, LSR, rn), cond); + } else { + mov(rd, ShifterOperand(rm, LSR, rn), cond); + } } -void Arm32Assembler::Rrx(Register rd, Register rm, Condition cond) { - mov(rd, ShifterOperand(rm, ROR, 0), cond); + +void Arm32Assembler::Asr(Register rd, Register rm, Register rn, + bool setcc, Condition cond) { + if (setcc) { + movs(rd, ShifterOperand(rm, ASR, rn), cond); + } else { + mov(rd, ShifterOperand(rm, ASR, rn), cond); + } } +void Arm32Assembler::Ror(Register rd, Register rm, Register rn, + bool setcc, Condition cond) { + if (setcc) { + movs(rd, ShifterOperand(rm, ROR, rn), cond); + } else { + mov(rd, ShifterOperand(rm, ROR, rn), cond); + } +} + void Arm32Assembler::vmstat(Condition cond) { // VMRS APSR_nzcv, FPSCR CHECK_NE(cond, kNoCondition); int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h index 7a0fce2e6a..7f9094d664 100644 --- a/compiler/utils/arm/assembler_arm32.h +++ b/compiler/utils/arm/assembler_arm32.h @@ -197,11 +197,25 @@ class Arm32Assembler FINAL : public ArmAssembler { void bl(Label* label, Condition cond = AL); void blx(Register rm, Condition cond = AL) OVERRIDE; void bx(Register rm, Condition cond = AL) OVERRIDE; - void Lsl(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL); - void Lsr(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL); - void Asr(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL); - void Ror(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL); - void Rrx(Register rd, Register rm, Condition cond = AL); + void Lsl(Register rd, Register rm, uint32_t shift_imm, bool setcc = false, + Condition cond = AL) OVERRIDE; + void Lsr(Register rd, Register rm, uint32_t shift_imm, bool setcc = false, + Condition cond = AL) OVERRIDE; + void Asr(Register rd, Register rm, uint32_t shift_imm, bool setcc = false, + Condition cond = AL) OVERRIDE; + void Ror(Register rd, Register rm, uint32_t shift_imm, bool setcc = false, + Condition cond = AL) OVERRIDE; + void Rrx(Register rd, Register rm, bool setcc = false, + Condition cond = AL) OVERRIDE; + + void Lsl(Register rd, Register rm, Register rn, bool setcc = false, + Condition cond = AL) OVERRIDE; + void Lsr(Register rd, Register rm, Register rn, bool setcc = false, + Condition cond = AL) OVERRIDE; + void Asr(Register rd, Register rm, Register rn, bool setcc = false, + Condition cond = AL) OVERRIDE; + void Ror(Register rd, Register rm, Register rn, bool setcc = false, + Condition cond = AL) OVERRIDE; void Push(Register rd, Condition cond = AL) OVERRIDE; void Pop(Register rd, Condition cond = AL) OVERRIDE; diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc index 703d68e0b3..604f59e08f 100644 --- a/compiler/utils/arm/assembler_thumb2.cc +++ b/compiler/utils/arm/assembler_thumb2.cc @@ -780,7 +780,7 @@ void Thumb2Assembler::Emit32BitDataProcessing(Condition cond, imm8; } else { // Modified immediate. - uint32_t imm = ModifiedImmediate(so.encodingThumb(2)); + uint32_t imm = ModifiedImmediate(so.encodingThumb()); if (imm == kInvalidModifiedImmediate) { LOG(FATAL) << "Immediate value cannot fit in thumb2 modified immediate"; } @@ -798,7 +798,7 @@ void Thumb2Assembler::Emit32BitDataProcessing(Condition cond, set_cc << 20 | rn << 16 | rd << 8 | - so.encodingThumb(2); + so.encodingThumb(); } Emit32(encoding); } @@ -1080,6 +1080,82 @@ void Thumb2Assembler::EmitDataProcessing(Condition cond, } } +void Thumb2Assembler::EmitShift(Register rd, Register rm, Shift shift, uint8_t amount, bool setcc) { + CHECK_LT(amount, (1 << 5)); + if (IsHighRegister(rd) || IsHighRegister(rm) || shift == ROR || shift == RRX) { + uint16_t opcode = 0; + switch (shift) { + case LSL: opcode = 0b00; break; + case LSR: opcode = 0b01; break; + case ASR: opcode = 0b10; break; + case ROR: opcode = 0b11; break; + case RRX: opcode = 0b11; amount = 0; break; + default: + LOG(FATAL) << "Unsupported thumb2 shift opcode"; + } + // 32 bit. + int32_t encoding = B31 | B30 | B29 | B27 | B25 | B22 | + 0xf << 16 | (setcc ? B20 : 0); + uint32_t imm3 = amount >> 2; + uint32_t imm2 = amount & 0b11; + encoding |= imm3 << 12 | imm2 << 6 | static_cast<int16_t>(rm) | + static_cast<int16_t>(rd) << 8 | opcode << 4; + Emit32(encoding); + } else { + // 16 bit shift + uint16_t opcode = 0; + switch (shift) { + case LSL: opcode = 0b00; break; + case LSR: opcode = 0b01; break; + case ASR: opcode = 0b10; break; + default: + LOG(FATAL) << "Unsupported thumb2 shift opcode"; + } + int16_t encoding = opcode << 11 | amount << 6 | static_cast<int16_t>(rm) << 3 | + static_cast<int16_t>(rd); + Emit16(encoding); + } +} + +void Thumb2Assembler::EmitShift(Register rd, Register rn, Shift shift, Register rm, bool setcc) { + CHECK_NE(shift, RRX); + bool must_be_32bit = false; + if (IsHighRegister(rd) || IsHighRegister(rm) || IsHighRegister(rn) || rd != rn) { + must_be_32bit = true; + } + + if (must_be_32bit) { + uint16_t opcode = 0; + switch (shift) { + case LSL: opcode = 0b00; break; + case LSR: opcode = 0b01; break; + case ASR: opcode = 0b10; break; + case ROR: opcode = 0b11; break; + default: + LOG(FATAL) << "Unsupported thumb2 shift opcode"; + } + // 32 bit. + int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | + 0xf << 12 | (setcc ? B20 : 0); + encoding |= static_cast<int16_t>(rn) << 16 | static_cast<int16_t>(rm) | + static_cast<int16_t>(rd) << 8 | opcode << 21; + Emit32(encoding); + } else { + uint16_t opcode = 0; + switch (shift) { + case LSL: opcode = 0b0010; break; + case LSR: opcode = 0b0011; break; + case ASR: opcode = 0b0100; break; + default: + LOG(FATAL) << "Unsupported thumb2 shift opcode"; + } + int16_t encoding = B14 | opcode << 6 | static_cast<int16_t>(rm) << 3 | + static_cast<int16_t>(rd); + Emit16(encoding); + } +} + + void Thumb2Assembler::Branch::Emit(AssemblerBuffer* buffer) const { bool link = type_ == kUnconditionalLinkX || type_ == kUnconditionalLink; @@ -1171,7 +1247,7 @@ void Thumb2Assembler::EmitLoadStore(Condition cond, } Register rn = ad.GetRegister(); - if (IsHighRegister(rn) && rn != SP) { + if (IsHighRegister(rn) && rn != SP && rn != PC) { must_be_32bit = true; } @@ -1179,87 +1255,132 @@ void Thumb2Assembler::EmitLoadStore(Condition cond, must_be_32bit = true; } - int32_t offset = ad.GetOffset(); - - // The 16 bit SP relative instruction can only have a 10 bit offset. - if (rn == SP && offset > 1024) { - must_be_32bit = true; - } + if (ad.IsImmediate()) { + // Immediate offset + int32_t offset = ad.GetOffset(); - if (byte) { - // 5 bit offset, no shift. - if (offset > 32) { + // The 16 bit SP relative instruction can only have a 10 bit offset. + if (rn == SP && offset >= (1 << 10)) { must_be_32bit = true; } - } else if (half) { - // 6 bit offset, shifted by 1. - if (offset > 64) { - must_be_32bit = true; - } - } else { - // 7 bit offset, shifted by 2. - if (offset > 128) { - must_be_32bit = true; - } - } - - if (must_be_32bit) { - int32_t encoding = B31 | B30 | B29 | B28 | B27 | - (load ? B20 : 0) | - (is_signed ? B24 : 0) | - static_cast<uint32_t>(rd) << 12 | - ad.encodingThumb(2) | - (byte ? 0 : half ? B21 : B22); - Emit32(encoding); - } else { - // 16 bit thumb1. - uint8_t opA = 0; - bool sp_relative = false; if (byte) { - opA = 0b0111; + // 5 bit offset, no shift. + if (offset >= (1 << 5)) { + must_be_32bit = true; + } } else if (half) { - opA = 0b1000; + // 6 bit offset, shifted by 1. + if (offset >= (1 << 6)) { + must_be_32bit = true; + } } else { - if (rn == SP) { - opA = 0b1001; - sp_relative = true; - } else { - opA = 0b0110; + // 7 bit offset, shifted by 2. + if (offset >= (1 << 7)) { + must_be_32bit = true; } } - int16_t encoding = opA << 12 | - (load ? B11 : 0); - - CHECK_GE(offset, 0); - if (sp_relative) { - // SP relative, 10 bit offset. - CHECK_LT(offset, 1024); - CHECK_EQ((offset & 0b11), 0); - encoding |= rd << 8 | offset >> 2; + + if (must_be_32bit) { + int32_t encoding = B31 | B30 | B29 | B28 | B27 | + (load ? B20 : 0) | + (is_signed ? B24 : 0) | + static_cast<uint32_t>(rd) << 12 | + ad.encodingThumb(true) | + (byte ? 0 : half ? B21 : B22); + Emit32(encoding); } else { - // No SP relative. The offset is shifted right depending on - // the size of the load/store. - encoding |= static_cast<uint32_t>(rd); + // 16 bit thumb1. + uint8_t opA = 0; + bool sp_relative = false; if (byte) { - // 5 bit offset, no shift. - CHECK_LT(offset, 32); + opA = 0b0111; } else if (half) { - // 6 bit offset, shifted by 1. - CHECK_LT(offset, 64); - CHECK_EQ((offset & 0b1), 0); - offset >>= 1; + opA = 0b1000; } else { - // 7 bit offset, shifted by 2. - CHECK_LT(offset, 128); + if (rn == SP) { + opA = 0b1001; + sp_relative = true; + } else { + opA = 0b0110; + } + } + int16_t encoding = opA << 12 | + (load ? B11 : 0); + + CHECK_GE(offset, 0); + if (sp_relative) { + // SP relative, 10 bit offset. + CHECK_LT(offset, (1 << 10)); CHECK_EQ((offset & 0b11), 0); - offset >>= 2; + encoding |= rd << 8 | offset >> 2; + } else { + // No SP relative. The offset is shifted right depending on + // the size of the load/store. + encoding |= static_cast<uint32_t>(rd); + + if (byte) { + // 5 bit offset, no shift. + CHECK_LT(offset, (1 << 5)); + } else if (half) { + // 6 bit offset, shifted by 1. + CHECK_LT(offset, (1 << 6)); + CHECK_EQ((offset & 0b1), 0); + offset >>= 1; + } else { + // 7 bit offset, shifted by 2. + CHECK_LT(offset, (1 << 7)); + CHECK_EQ((offset & 0b11), 0); + offset >>= 2; + } + encoding |= rn << 3 | offset << 6; } - encoding |= rn << 3 | offset << 6; + + Emit16(encoding); } + } else { + // Register shift. + if (ad.GetRegister() == PC) { + // PC relative literal encoding. + int32_t offset = ad.GetOffset(); + if (must_be_32bit || offset < 0 || offset >= (1 << 10) || !load) { + int32_t up = B23; + if (offset < 0) { + offset = -offset; + up = 0; + } + CHECK_LT(offset, (1 << 12)); + int32_t encoding = 0x1f << 27 | 0xf << 16 | B22 | (load ? B20 : 0) | + offset | up | + static_cast<uint32_t>(rd) << 12; + Emit32(encoding); + } else { + // 16 bit literal load. + CHECK_GE(offset, 0); + CHECK_LT(offset, (1 << 10)); + int32_t encoding = B14 | (load ? B11 : 0) | static_cast<uint32_t>(rd) << 8 | offset >> 2; + Emit16(encoding); + } + } else { + if (ad.GetShiftCount() != 0) { + // If there is a shift count this must be 32 bit. + must_be_32bit = true; + } else if (IsHighRegister(ad.GetRegisterOffset())) { + must_be_32bit = true; + } - Emit16(encoding); + if (must_be_32bit) { + int32_t encoding = 0x1f << 27 | B22 | (load ? B20 : 0) | static_cast<uint32_t>(rd) << 12 | + ad.encodingThumb(true); + Emit32(encoding); + } else { + // 16 bit register offset. + int32_t encoding = B14 | B12 | (load ? B11 : 0) | static_cast<uint32_t>(rd) | + ad.encodingThumb(false); + Emit16(encoding); + } + } } } @@ -2011,37 +2132,70 @@ void Thumb2Assembler::EmitBranches() { void Thumb2Assembler::Lsl(Register rd, Register rm, uint32_t shift_imm, - Condition cond) { + bool setcc, Condition cond) { CHECK_NE(shift_imm, 0u); // Do not use Lsl if no shift is wanted. - mov(rd, ShifterOperand(rm, LSL, shift_imm), cond); + CheckCondition(cond); + EmitShift(rd, rm, LSL, shift_imm, setcc); } void Thumb2Assembler::Lsr(Register rd, Register rm, uint32_t shift_imm, - Condition cond) { + bool setcc, Condition cond) { CHECK_NE(shift_imm, 0u); // Do not use Lsr if no shift is wanted. if (shift_imm == 32) shift_imm = 0; // Comply to UAL syntax. - mov(rd, ShifterOperand(rm, LSR, shift_imm), cond); + CheckCondition(cond); + EmitShift(rd, rm, LSR, shift_imm, setcc); } void Thumb2Assembler::Asr(Register rd, Register rm, uint32_t shift_imm, - Condition cond) { + bool setcc, Condition cond) { CHECK_NE(shift_imm, 0u); // Do not use Asr if no shift is wanted. if (shift_imm == 32) shift_imm = 0; // Comply to UAL syntax. - mov(rd, ShifterOperand(rm, ASR, shift_imm), cond); + CheckCondition(cond); + EmitShift(rd, rm, ASR, shift_imm, setcc); } void Thumb2Assembler::Ror(Register rd, Register rm, uint32_t shift_imm, - Condition cond) { + bool setcc, Condition cond) { CHECK_NE(shift_imm, 0u); // Use Rrx instruction. - mov(rd, ShifterOperand(rm, ROR, shift_imm), cond); + CheckCondition(cond); + EmitShift(rd, rm, ROR, shift_imm, setcc); +} + + +void Thumb2Assembler::Rrx(Register rd, Register rm, bool setcc, Condition cond) { + CheckCondition(cond); + EmitShift(rd, rm, RRX, rm, setcc); +} + + +void Thumb2Assembler::Lsl(Register rd, Register rm, Register rn, + bool setcc, Condition cond) { + CheckCondition(cond); + EmitShift(rd, rm, LSL, rn, setcc); +} + + +void Thumb2Assembler::Lsr(Register rd, Register rm, Register rn, + bool setcc, Condition cond) { + CheckCondition(cond); + EmitShift(rd, rm, LSR, rn, setcc); } -void Thumb2Assembler::Rrx(Register rd, Register rm, Condition cond) { - mov(rd, ShifterOperand(rm, ROR, 0), cond); +void Thumb2Assembler::Asr(Register rd, Register rm, Register rn, + bool setcc, Condition cond) { + CheckCondition(cond); + EmitShift(rd, rm, ASR, rn, setcc); +} + + +void Thumb2Assembler::Ror(Register rd, Register rm, Register rn, + bool setcc, Condition cond) { + CheckCondition(cond); + EmitShift(rd, rm, ROR, rn, setcc); } diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h index 60b9384e56..5f24e4e0e1 100644 --- a/compiler/utils/arm/assembler_thumb2.h +++ b/compiler/utils/arm/assembler_thumb2.h @@ -221,11 +221,25 @@ class Thumb2Assembler FINAL : public ArmAssembler { void blx(Register rm, Condition cond = AL) OVERRIDE; void bx(Register rm, Condition cond = AL) OVERRIDE; - void Lsl(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL); - void Lsr(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL); - void Asr(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL); - void Ror(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL); - void Rrx(Register rd, Register rm, Condition cond = AL); + void Lsl(Register rd, Register rm, uint32_t shift_imm, bool setcc = false, + Condition cond = AL) OVERRIDE; + void Lsr(Register rd, Register rm, uint32_t shift_imm, bool setcc = false, + Condition cond = AL) OVERRIDE; + void Asr(Register rd, Register rm, uint32_t shift_imm, bool setcc = false, + Condition cond = AL) OVERRIDE; + void Ror(Register rd, Register rm, uint32_t shift_imm, bool setcc = false, + Condition cond = AL) OVERRIDE; + void Rrx(Register rd, Register rm, bool setcc = false, + Condition cond = AL) OVERRIDE; + + void Lsl(Register rd, Register rm, Register rn, bool setcc = false, + Condition cond = AL) OVERRIDE; + void Lsr(Register rd, Register rm, Register rn, bool setcc = false, + Condition cond = AL) OVERRIDE; + void Asr(Register rd, Register rm, Register rn, bool setcc = false, + Condition cond = AL) OVERRIDE; + void Ror(Register rd, Register rm, Register rn, bool setcc = false, + Condition cond = AL) OVERRIDE; void Push(Register rd, Condition cond = AL) OVERRIDE; void Pop(Register rd, Condition cond = AL) OVERRIDE; @@ -395,14 +409,8 @@ class Thumb2Assembler FINAL : public ArmAssembler { static int DecodeBranchOffset(int32_t inst); int32_t EncodeTstOffset(int offset, int32_t inst); int DecodeTstOffset(int32_t inst); - - bool IsLowRegister(Register r) { - return r < R8; - } - - bool IsHighRegister(Register r) { - return r >= R8; - } + void EmitShift(Register rd, Register rm, Shift shift, uint8_t amount, bool setcc = false); + void EmitShift(Register rd, Register rn, Shift shift, Register rm, bool setcc = false); bool force_32bit_; // Force the assembler to use 32 bit thumb2 instructions. diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc index 55fbed1e44..68cb6569b1 100644 --- a/compiler/utils/assembler_thumb_test.cc +++ b/compiler/utils/assembler_thumb_test.cc @@ -28,6 +28,15 @@ namespace arm { #include "assembler_thumb_test_expected.cc.inc" #ifndef HAVE_ANDROID_OS +// This controls whether the results are printed to the +// screen or compared against the expected output. +// To generate new expected output, set this to true and +// copy the output into the .cc.inc file in the form +// of the other results. +// +// When this is false, the results are not printed to the +// output, but are compared against the expected results +// in the .cc.inc file. static constexpr bool kPrintResults = false; #endif @@ -38,6 +47,19 @@ void SetAndroidData() { } } +int CompareIgnoringSpace(const char* s1, const char* s2) { + while (*s1 != '\0') { + while (isspace(*s1)) ++s1; + while (isspace(*s2)) ++s2; + if (*s1 == '\0' || *s1 != *s2) { + break; + } + ++s1; + ++s2; + } + return *s1 - *s2; +} + std::string GetAndroidToolsDir() { std::string root; const char* android_build_top = getenv("ANDROID_BUILD_TOP"); @@ -180,7 +202,10 @@ void dump(std::vector<uint8_t>& code, const char* testname) { if (s == nullptr) { break; } - ASSERT_EQ(strcmp(results->second[lineindex], testline), 0); + if (CompareIgnoringSpace(results->second[lineindex], testline) != 0) { + LOG(FATAL) << "Output is not as expected at line: " << lineindex + << results->second[lineindex] << "/" << testline; + } ++lineindex; } // Check that we are at the end. @@ -1222,6 +1247,152 @@ TEST(Thumb2AssemblerTest, MixedBranch32) { delete assembler; } +TEST(Thumb2AssemblerTest, Shifts) { + arm::Thumb2Assembler* assembler = static_cast<arm::Thumb2Assembler*>(Assembler::Create(kThumb2)); + + // 16 bit + __ Lsl(R0, R1, 5); + __ Lsr(R0, R1, 5); + __ Asr(R0, R1, 5); + + __ Lsl(R0, R0, R1); + __ Lsr(R0, R0, R1); + __ Asr(R0, R0, R1); + + // 32 bit due to high registers. + __ Lsl(R8, R1, 5); + __ Lsr(R0, R8, 5); + __ Asr(R8, R1, 5); + __ Ror(R0, R8, 5); + + // 32 bit due to different Rd and Rn. + __ Lsl(R0, R1, R2); + __ Lsr(R0, R1, R2); + __ Asr(R0, R1, R2); + __ Ror(R0, R1, R2); + + // 32 bit due to use of high registers. + __ Lsl(R8, R1, R2); + __ Lsr(R0, R8, R2); + __ Asr(R0, R1, R8); + + // S bit (all 32 bit) + + // 32 bit due to high registers. + __ Lsl(R8, R1, 5, true); + __ Lsr(R0, R8, 5, true); + __ Asr(R8, R1, 5, true); + __ Ror(R0, R8, 5, true); + + // 32 bit due to different Rd and Rn. + __ Lsl(R0, R1, R2, true); + __ Lsr(R0, R1, R2, true); + __ Asr(R0, R1, R2, true); + __ Ror(R0, R1, R2, true); + + // 32 bit due to use of high registers. + __ Lsl(R8, R1, R2, true); + __ Lsr(R0, R8, R2, true); + __ Asr(R0, R1, R8, true); + + size_t cs = __ CodeSize(); + std::vector<uint8_t> managed_code(cs); + MemoryRegion code(&managed_code[0], managed_code.size()); + __ FinalizeInstructions(code); + dump(managed_code, "Shifts"); + delete assembler; +} + +TEST(Thumb2AssemblerTest, LoadStoreRegOffset) { + arm::Thumb2Assembler* assembler = static_cast<arm::Thumb2Assembler*>(Assembler::Create(kThumb2)); + + // 16 bit. + __ ldr(R0, Address(R1, R2)); + __ str(R0, Address(R1, R2)); + + // 32 bit due to shift. + __ ldr(R0, Address(R1, R2, LSL, 1)); + __ str(R0, Address(R1, R2, LSL, 1)); + + __ ldr(R0, Address(R1, R2, LSL, 3)); + __ str(R0, Address(R1, R2, LSL, 3)); + + // 32 bit due to high register use. + __ ldr(R8, Address(R1, R2)); + __ str(R8, Address(R1, R2)); + + __ ldr(R1, Address(R8, R2)); + __ str(R2, Address(R8, R2)); + + __ ldr(R0, Address(R1, R8)); + __ str(R0, Address(R1, R8)); + + size_t cs = __ CodeSize(); + std::vector<uint8_t> managed_code(cs); + MemoryRegion code(&managed_code[0], managed_code.size()); + __ FinalizeInstructions(code); + dump(managed_code, "LoadStoreRegOffset"); + delete assembler; +} + +TEST(Thumb2AssemblerTest, LoadStoreLiteral) { + arm::Thumb2Assembler* assembler = static_cast<arm::Thumb2Assembler*>(Assembler::Create(kThumb2)); + + __ ldr(R0, Address(4)); + __ str(R0, Address(4)); + + __ ldr(R0, Address(-8)); + __ str(R0, Address(-8)); + + // Limits. + __ ldr(R0, Address(0x3ff)); // 10 bits (16 bit). + __ ldr(R0, Address(0x7ff)); // 11 bits (32 bit). + __ str(R0, Address(0x3ff)); // 32 bit (no 16 bit str(literal)). + __ str(R0, Address(0x7ff)); // 11 bits (32 bit). + + size_t cs = __ CodeSize(); + std::vector<uint8_t> managed_code(cs); + MemoryRegion code(&managed_code[0], managed_code.size()); + __ FinalizeInstructions(code); + dump(managed_code, "LoadStoreLiteral"); + delete assembler; +} + +TEST(Thumb2AssemblerTest, LoadStoreLimits) { + arm::Thumb2Assembler* assembler = static_cast<arm::Thumb2Assembler*>(Assembler::Create(kThumb2)); + + __ ldr(R0, Address(R4, 124)); // 16 bit. + __ ldr(R0, Address(R4, 128)); // 32 bit. + + __ ldrb(R0, Address(R4, 31)); // 16 bit. + __ ldrb(R0, Address(R4, 32)); // 32 bit. + + __ ldrh(R0, Address(R4, 62)); // 16 bit. + __ ldrh(R0, Address(R4, 64)); // 32 bit. + + __ ldrsb(R0, Address(R4, 31)); // 32 bit. + __ ldrsb(R0, Address(R4, 32)); // 32 bit. + + __ ldrsh(R0, Address(R4, 62)); // 32 bit. + __ ldrsh(R0, Address(R4, 64)); // 32 bit. + + __ str(R0, Address(R4, 124)); // 16 bit. + __ str(R0, Address(R4, 128)); // 32 bit. + + __ strb(R0, Address(R4, 31)); // 16 bit. + __ strb(R0, Address(R4, 32)); // 32 bit. + + __ strh(R0, Address(R4, 62)); // 16 bit. + __ strh(R0, Address(R4, 64)); // 32 bit. + + size_t cs = __ CodeSize(); + std::vector<uint8_t> managed_code(cs); + MemoryRegion code(&managed_code[0], managed_code.size()); + __ FinalizeInstructions(code); + dump(managed_code, "LoadStoreLimits"); + delete assembler; +} + #undef __ } // namespace arm } // namespace art diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc index c5f22268b3..3943e3713b 100644 --- a/compiler/utils/assembler_thumb_test_expected.cc.inc +++ b/compiler/utils/assembler_thumb_test_expected.cc.inc @@ -4742,6 +4742,82 @@ const char* MixedBranch32Results[] = { " 80a: 0011 movs r1, r2\n", nullptr }; +const char* ShiftsResults[] = { + " 0: 0148 lsls r0, r1, #5\n", + " 2: 0948 lsrs r0, r1, #5\n", + " 4: 1148 asrs r0, r1, #5\n", + " 6: 4088 lsls r0, r1\n", + " 8: 40c8 lsrs r0, r1\n", + " a: 4108 asrs r0, r1\n", + " c: ea4f 1841 mov.w r8, r1, lsl #5\n", + " 10: ea4f 1058 mov.w r0, r8, lsr #5\n", + " 14: ea4f 1861 mov.w r8, r1, asr #5\n", + " 18: ea4f 1078 mov.w r0, r8, ror #5\n", + " 1c: fa01 f002 lsl.w r0, r1, r2\n", + " 20: fa21 f002 lsr.w r0, r1, r2\n", + " 24: fa41 f002 asr.w r0, r1, r2\n", + " 28: fa61 f002 ror.w r0, r1, r2\n", + " 2c: fa01 f802 lsl.w r8, r1, r2\n", + " 30: fa28 f002 lsr.w r0, r8, r2\n", + " 34: fa41 f008 asr.w r0, r1, r8\n", + " 38: ea5f 1841 movs.w r8, r1, lsl #5\n", + " 3c: ea5f 1058 movs.w r0, r8, lsr #5\n", + " 40: ea5f 1861 movs.w r8, r1, asr #5\n", + " 44: ea5f 1078 movs.w r0, r8, ror #5\n", + " 48: fa11 f002 lsls.w r0, r1, r2\n", + " 4c: fa31 f002 lsrs.w r0, r1, r2\n", + " 50: fa51 f002 asrs.w r0, r1, r2\n", + " 54: fa71 f002 rors.w r0, r1, r2\n", + " 58: fa11 f802 lsls.w r8, r1, r2\n", + " 5c: fa38 f002 lsrs.w r0, r8, r2\n", + " 60: fa51 f008 asrs.w r0, r1, r8\n", + nullptr +}; +const char* LoadStoreRegOffsetResults[] = { + " 0: 5888 ldr r0, [r1, r2]\n", + " 2: 5088 str r0, [r1, r2]\n", + " 4: f851 0012 ldr.w r0, [r1, r2, lsl #1]\n", + " 8: f841 0012 str.w r0, [r1, r2, lsl #1]\n", + " c: f851 0032 ldr.w r0, [r1, r2, lsl #3]\n", + " 10: f841 0032 str.w r0, [r1, r2, lsl #3]\n", + " 14: f851 8002 ldr.w r8, [r1, r2]\n", + " 18: f841 8002 str.w r8, [r1, r2]\n", + " 1c: f858 1002 ldr.w r1, [r8, r2]\n", + " 20: f848 2002 str.w r2, [r8, r2]\n", + " 24: f851 0008 ldr.w r0, [r1, r8]\n", + " 28: f841 0008 str.w r0, [r1, r8]\n", + nullptr +}; +const char* LoadStoreLiteralResults[] = { + " 0: 4801 ldr r0, [pc, #4] ; (8 <LoadStoreLiteral+0x8>)\n", + " 2: f8cf 0004 str.w r0, [pc, #4] ; 8 <LoadStoreLiteral+0x8>\n", + " 6: f85f 0008 ldr.w r0, [pc, #-8] ; 0 <LoadStoreLiteral>\n", + " a: f84f 0008 str.w r0, [pc, #-8] ; 4 <LoadStoreLiteral+0x4>\n", + " e: 48ff ldr r0, [pc, #1020] ; (40c <LoadStoreLiteral+0x40c>)\n", + " 10: f8df 07ff ldr.w r0, [pc, #2047] ; 813 <LoadStoreLiteral+0x813>\n", + " 14: f8cf 03ff str.w r0, [pc, #1023] ; 417 <LoadStoreLiteral+0x417>\n", + " 18: f8cf 07ff str.w r0, [pc, #2047] ; 81b <LoadStoreLiteral+0x81b>\n", + nullptr +}; +const char* LoadStoreLimitsResults[] = { + " 0: 6fe0 ldr r0, [r4, #124] ; 0x7c\n", + " 2: f8d4 0080 ldr.w r0, [r4, #128] ; 0x80\n", + " 6: 7fe0 ldrb r0, [r4, #31]\n", + " 8: f894 0020 ldrb.w r0, [r4, #32]\n", + " c: 8fe0 ldrh r0, [r4, #62] ; 0x3e\n", + " e: f8b4 0040 ldrh.w r0, [r4, #64] ; 0x40\n", + " 12: f994 001f ldrsb.w r0, [r4, #31]\n", + " 16: f994 0020 ldrsb.w r0, [r4, #32]\n", + " 1a: f9b4 003e ldrsh.w r0, [r4, #62] ; 0x3e\n", + " 1e: f9b4 0040 ldrsh.w r0, [r4, #64] ; 0x40\n", + " 22: 67e0 str r0, [r4, #124] ; 0x7c\n", + " 24: f8c4 0080 str.w r0, [r4, #128] ; 0x80\n", + " 28: 77e0 strb r0, [r4, #31]\n", + " 2a: f884 0020 strb.w r0, [r4, #32]\n", + " 2e: 87e0 strh r0, [r4, #62] ; 0x3e\n", + " 30: f8a4 0040 strh.w r0, [r4, #64] ; 0x40\n", + nullptr +}; std::map<std::string, const char**> test_results; void setup_results() { test_results["SimpleMov"] = SimpleMovResults; @@ -4785,4 +4861,8 @@ void setup_results() { test_results["CompareAndBranchRelocation16"] = CompareAndBranchRelocation16Results; test_results["CompareAndBranchRelocation32"] = CompareAndBranchRelocation32Results; test_results["MixedBranch32"] = MixedBranch32Results; + test_results["Shifts"] = ShiftsResults; + test_results["LoadStoreRegOffset"] = LoadStoreRegOffsetResults; + test_results["LoadStoreLiteral"] = LoadStoreLiteralResults; + test_results["LoadStoreLimits"] = LoadStoreLimitsResults; } diff --git a/compiler/utils/scoped_arena_containers.h b/compiler/utils/scoped_arena_containers.h index 5deb6614d5..67285658e2 100644 --- a/compiler/utils/scoped_arena_containers.h +++ b/compiler/utils/scoped_arena_containers.h @@ -17,8 +17,10 @@ #ifndef ART_COMPILER_UTILS_SCOPED_ARENA_CONTAINERS_H_ #define ART_COMPILER_UTILS_SCOPED_ARENA_CONTAINERS_H_ -#include <vector> +#include <deque> +#include <queue> #include <set> +#include <vector> #include "utils/scoped_arena_allocator.h" #include "safe_map.h" @@ -26,6 +28,12 @@ namespace art { template <typename T> +using ScopedArenaDeque = std::deque<T, ScopedArenaAllocatorAdapter<T>>; + +template <typename T> +using ScopedArenaQueue = std::queue<T, ScopedArenaDeque<T>>; + +template <typename T> using ScopedArenaVector = std::vector<T, ScopedArenaAllocatorAdapter<T>>; template <typename T, typename Comparator = std::less<T>> diff --git a/dalvikvm/Android.mk b/dalvikvm/Android.mk index 31fcd176ae..5d838c0031 100644 --- a/dalvikvm/Android.mk +++ b/dalvikvm/Android.mk @@ -33,7 +33,7 @@ LOCAL_MODULE_STEM_64 := dalvikvm64 include external/libcxx/libcxx.mk include $(BUILD_EXECUTABLE) -# create symlink for the primary version target. +# Create symlink for the primary version target. include $(BUILD_SYSTEM)/executable_prefer_symlink.mk ART_TARGET_EXECUTABLES += $(TARGET_OUT_EXECUTABLES)/$(LOCAL_MODULE) @@ -50,6 +50,13 @@ LOCAL_SHARED_LIBRARIES := libnativehelper LOCAL_LDFLAGS := -ldl -lpthread LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_IS_HOST_MODULE := true +LOCAL_MULTILIB := both +LOCAL_MODULE_STEM_32 := dalvikvm32 +LOCAL_MODULE_STEM_64 := dalvikvm64 include external/libcxx/libcxx.mk include $(BUILD_HOST_EXECUTABLE) -ART_HOST_EXECUTABLES += $(HOST_OUT_EXECUTABLES)/$(LOCAL_MODULE) + +ART_HOST_EXECUTABLES += $(HOST_OUT_EXECUTABLES)/$(LOCAL_MODULE)32 +ifneq ($(HOST_PREFER_32_BIT),true) + ART_HOST_EXECUTABLES += $(HOST_OUT_EXECUTABLES)/$(LOCAL_MODULE)64 +endif
\ No newline at end of file diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index d7b34dc47d..de2960c56c 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -367,12 +367,12 @@ class Dex2Oat { driver->CompileAll(class_loader, dex_files, &timings); - timings.NewSplit("dex2oat OatWriter"); + TimingLogger::ScopedTiming t2("dex2oat OatWriter", &timings); std::string image_file_location; uint32_t image_file_location_oat_checksum = 0; uintptr_t image_file_location_oat_data_begin = 0; if (!driver->IsImage()) { - TimingLogger::ScopedSplit split("Loading image checksum", &timings); + TimingLogger::ScopedTiming t3("Loading image checksum", &timings); gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace(); image_file_location_oat_checksum = image_space->GetImageHeader().GetOatChecksum(); image_file_location_oat_data_begin = @@ -380,14 +380,13 @@ class Dex2Oat { image_file_location = image_space->GetImageFilename(); } - OatWriter oat_writer(dex_files, - image_file_location_oat_checksum, + OatWriter oat_writer(dex_files, image_file_location_oat_checksum, image_file_location_oat_data_begin, image_file_location, driver.get(), &timings); - TimingLogger::ScopedSplit split("Writing ELF", &timings); + t2.NewTiming("Writing ELF"); if (!driver->WriteElf(android_root, is_host, dex_files, &oat_writer, oat_file)) { LOG(ERROR) << "Failed to write ELF file " << oat_file->GetPath(); return nullptr; @@ -479,11 +478,8 @@ class Dex2Oat { continue; } std::string error_msg; - const DexFile* dex_file = DexFile::Open(parsed[i].c_str(), parsed[i].c_str(), &error_msg); - if (dex_file == nullptr) { + if (!DexFile::Open(parsed[i].c_str(), parsed[i].c_str(), &error_msg, &dex_files)) { LOG(WARNING) << "Failed to open dex file '" << parsed[i] << "': " << error_msg; - } else { - dex_files.push_back(dex_file); } } } @@ -537,12 +533,9 @@ static size_t OpenDexFiles(const std::vector<const char*>& dex_filenames, LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'"; continue; } - const DexFile* dex_file = DexFile::Open(dex_filename, dex_location, &error_msg); - if (dex_file == nullptr) { + if (!DexFile::Open(dex_filename, dex_location, &error_msg, &dex_files)) { LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg; ++failure_count; - } else { - dex_files.push_back(dex_file); } ATRACE_END(); } @@ -748,6 +741,7 @@ void CheckExplicitCheckOptions(InstructionSet isa, bool* explicit_null_checks, bool* explicit_so_checks, bool* explicit_suspend_checks) { switch (isa) { case kArm: + case kThumb2: break; // All checks implemented, leave as is. default: // No checks implemented, reset all to explicit checks. @@ -1039,8 +1033,8 @@ static int dex2oat(int argc, char** argv) { } else { Usage("--implicit-checks passed non-recognized value %s", val.c_str()); } - has_explicit_checks_options = true; } + has_explicit_checks_options = true; } else { Usage("Unknown argument %s", option.data()); } @@ -1076,7 +1070,7 @@ static int dex2oat(int argc, char** argv) { bool image = (!image_filename.empty()); if (!image && boot_image_filename.empty()) { - boot_image_filename += GetAndroidRoot(); + boot_image_filename += android_root; boot_image_filename += "/framework/boot.art"; } std::string boot_image_option; @@ -1136,10 +1130,8 @@ static int dex2oat(int argc, char** argv) { } if (compiler_filter_string == nullptr) { - if ((instruction_set == kX86_64 && image) || - instruction_set == kArm64 || - instruction_set == kMips) { - // TODO: implement/fix compilers for these architectures. + if (instruction_set == kMips) { + // TODO: fix compiler for Mips. compiler_filter_string = "interpret-only"; } else if (image) { compiler_filter_string = "speed"; @@ -1172,6 +1164,7 @@ static int dex2oat(int argc, char** argv) { CheckExplicitCheckOptions(instruction_set, &explicit_null_checks, &explicit_so_checks, &explicit_suspend_checks); + LOG(INFO) << "init compiler options for explicit null: " << explicit_null_checks; CompilerOptions compiler_options(compiler_filter, huge_method_threshold, large_method_threshold, @@ -1213,7 +1206,7 @@ static int dex2oat(int argc, char** argv) { return EXIT_FAILURE; } - timings.StartSplit("dex2oat Setup"); + timings.StartTiming("dex2oat Setup"); LOG(INFO) << CommandLine(); Runtime::Options runtime_options; @@ -1258,7 +1251,17 @@ static int dex2oat(int argc, char** argv) { // TODO: Not sure whether it's a good idea to allow anything else but the runtime option in // this case at all, as we'll have to throw away produced code for a mismatch. if (!has_explicit_checks_options) { - if (instruction_set == kRuntimeISA) { + bool cross_compiling = true; + switch (kRuntimeISA) { + case kArm: + case kThumb2: + cross_compiling = instruction_set != kArm && instruction_set != kThumb2; + break; + default: + cross_compiling = instruction_set != kRuntimeISA; + break; + } + if (!cross_compiling) { Runtime* runtime = Runtime::Current(); compiler_options.SetExplicitNullChecks(runtime->ExplicitNullChecks()); compiler_options.SetExplicitStackOverflowChecks(runtime->ExplicitStackOverflowChecks()); @@ -1310,13 +1313,11 @@ static int dex2oat(int argc, char** argv) { << error_msg; return EXIT_FAILURE; } - const DexFile* dex_file = DexFile::Open(*zip_archive.get(), zip_location, &error_msg); - if (dex_file == nullptr) { + if (!DexFile::OpenFromZip(*zip_archive.get(), zip_location, &error_msg, &dex_files)) { LOG(ERROR) << "Failed to open dex from file descriptor for zip file '" << zip_location << "': " << error_msg; return EXIT_FAILURE; } - dex_files.push_back(dex_file); ATRACE_END(); } else { size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, dex_files); @@ -1438,7 +1439,7 @@ static int dex2oat(int argc, char** argv) { // Elf32_Phdr.p_vaddr values by the desired base address. // if (image) { - timings.NewSplit("dex2oat ImageWriter"); + TimingLogger::ScopedTiming t("dex2oat ImageWriter", &timings); bool image_creation_success = dex2oat->CreateImageFile(image_filename, image_base, oat_unstripped, @@ -1451,6 +1452,7 @@ static int dex2oat(int argc, char** argv) { } if (is_host) { + timings.EndTiming(); if (dump_timing || (dump_slow_timing && timings.GetTotalNs() > MsToNs(1000))) { LOG(INFO) << Dumpable<TimingLogger>(timings); } @@ -1463,7 +1465,7 @@ static int dex2oat(int argc, char** argv) { // If we don't want to strip in place, copy from unstripped location to stripped location. // We need to strip after image creation because FixupElf needs to use .strtab. if (oat_unstripped != oat_stripped) { - timings.NewSplit("dex2oat OatFile copy"); + TimingLogger::ScopedTiming t("dex2oat OatFile copy", &timings); oat_file.reset(); std::unique_ptr<File> in(OS::OpenFileForReading(oat_unstripped.c_str())); std::unique_ptr<File> out(OS::CreateEmptyFile(oat_stripped.c_str())); @@ -1498,7 +1500,7 @@ static int dex2oat(int argc, char** argv) { } #endif // ART_USE_PORTABLE_COMPILER - timings.EndSplit(); + timings.EndTiming(); if (dump_timing || (dump_slow_timing && timings.GetTotalNs() > MsToNs(1000))) { LOG(INFO) << Dumpable<TimingLogger>(timings); diff --git a/disassembler/Android.mk b/disassembler/Android.mk index feacbde2e8..a0abc9e1f0 100644 --- a/disassembler/Android.mk +++ b/disassembler/Android.mk @@ -16,7 +16,7 @@ LOCAL_PATH := $(call my-dir) -include art/build/Android.common.mk +include art/build/Android.common_build.mk LIBART_DISASSEMBLER_SRC_FILES := \ disassembler.cc \ @@ -80,7 +80,7 @@ define build-libart-disassembler LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime - LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk + LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk include external/libcxx/libcxx.mk ifeq ($$(art_target_or_host),target) diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc index 4e4a512713..1f565e504a 100644 --- a/disassembler/disassembler_arm.cc +++ b/disassembler/disassembler_arm.cc @@ -269,18 +269,34 @@ void DisassemblerArm::DumpArm(std::ostream& os, const uint8_t* instr_ptr) { uint32_t op = (instruction >> 21) & 0xf; opcode = kDataProcessingOperations[op]; bool implicit_s = ((op & ~3) == 8); // TST, TEQ, CMP, and CMN. - if (implicit_s) { - // Rd is unused (and not shown), and we don't show the 's' suffix either. - } else { + bool is_mov = op == 0b1101 || op == 0b1111; + if (is_mov) { + // Show only Rd and Rm. if (s) { - suffixes += 's'; - } - args << ArmRegister(instruction, 12) << ", "; - } - if (i) { - args << ArmRegister(instruction, 16) << ", " << ShiftedImmediate(instruction); + suffixes += 's'; + } + args << ArmRegister(instruction, 12) << ", "; + if (i) { + args << ShiftedImmediate(instruction); + } else { + // TODO: Shifted register. + args << ArmRegister(instruction, 16) << ", " << ArmRegister(instruction, 0); + } } else { - args << Rm(instruction); + if (implicit_s) { + // Rd is unused (and not shown), and we don't show the 's' suffix either. + } else { + if (s) { + suffixes += 's'; + } + args << ArmRegister(instruction, 12) << ", "; + } + if (i) { + args << ArmRegister(instruction, 16) << ", " << ShiftedImmediate(instruction); + } else { + // TODO: Shifted register. + args << ArmRegister(instruction, 16) << ", " << ArmRegister(instruction, 0); + } } } break; @@ -1291,7 +1307,7 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) int32_t imm32 = (imm8 << 24) >> 24; // sign-extend imm8 if (Rn.r == 13 && P == 1 && U == 0 && W == 1 && imm32 == 4) { opcode << "push"; - args << Rt; + args << "{" << Rt << "}"; } else if (Rn.r == 15 || (P == 0 && W == 0)) { opcode << "UNDEFINED"; } else { @@ -1443,10 +1459,33 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) } args << "]"; } else { - // LDRT Rt, [Rn, #imm8] - 111 11 00 00 101 nnnn tttt 1110iiiiiiii - uint32_t imm8 = instr & 0xFF; - opcode << "ldrt"; - args << Rt << ", [" << Rn << ", #" << imm8 << "]"; + bool p = (instr & (1 << 10)) != 0; + bool w = (instr & (1 << 8)) != 0; + bool u = (instr & (1 << 9)) != 0; + if (p && u && !w) { + // LDRT Rt, [Rn, #imm8] - 111 11 00 00 101 nnnn tttt 1110iiiiiiii + uint32_t imm8 = instr & 0xFF; + opcode << "ldrt"; + args << Rt << ", [" << Rn << ", #" << imm8 << "]"; + } else if (Rn.r == 13 && !p && u && w && (instr & 0xff) == 4) { + // POP + opcode << "pop"; + args << "{" << Rt << "}"; + } else { + bool wback = !p || w; + uint32_t offset = (instr & 0xff); + opcode << "ldr.w"; + args << Rt << ","; + if (p && !wback) { + args << "[" << Rn << ", #" << offset << "]"; + } else if (p && wback) { + args << "[" << Rn << ", #" << offset << "]!"; + } else if (!p && wback) { + args << "[" << Rn << "], #" << offset; + } else { + LOG(FATAL) << p << " " << w; + } + } } break; } diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc index e6a6860626..b012bc1cc1 100644 --- a/disassembler/disassembler_x86.cc +++ b/disassembler/disassembler_x86.cc @@ -56,10 +56,16 @@ static const char* gReg64Names[] = { "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" }; +// 64-bit opcode REX modifier. +constexpr uint8_t REX_W = 0b1000; +constexpr uint8_t REX_R = 0b0100; +constexpr uint8_t REX_X = 0b0010; +constexpr uint8_t REX_B = 0b0001; + static void DumpReg0(std::ostream& os, uint8_t rex, size_t reg, bool byte_operand, uint8_t size_override) { DCHECK_LT(reg, (rex == 0) ? 8u : 16u); - bool rex_w = (rex & 0b1000) != 0; + bool rex_w = (rex & REX_W) != 0; if (byte_operand) { os << ((rex == 0) ? gReg8Names[reg] : gExtReg8Names[reg]); } else if (rex_w) { @@ -86,14 +92,14 @@ static void DumpAnyReg(std::ostream& os, uint8_t rex, size_t reg, static void DumpReg(std::ostream& os, uint8_t rex, uint8_t reg, bool byte_operand, uint8_t size_override, RegFile reg_file) { - bool rex_r = (rex & 0b0100) != 0; + bool rex_r = (rex & REX_R) != 0; size_t reg_num = rex_r ? (reg + 8) : reg; DumpAnyReg(os, rex, reg_num, byte_operand, size_override, reg_file); } static void DumpRmReg(std::ostream& os, uint8_t rex, uint8_t reg, bool byte_operand, uint8_t size_override, RegFile reg_file) { - bool rex_b = (rex & 0b0001) != 0; + bool rex_b = (rex & REX_B) != 0; size_t reg_num = rex_b ? (reg + 8) : reg; DumpAnyReg(os, rex, reg_num, byte_operand, size_override, reg_file); } @@ -107,19 +113,19 @@ static void DumpAddrReg(std::ostream& os, uint8_t rex, uint8_t reg) { } static void DumpBaseReg(std::ostream& os, uint8_t rex, uint8_t reg) { - bool rex_b = (rex & 0b0001) != 0; + bool rex_b = (rex & REX_B) != 0; size_t reg_num = rex_b ? (reg + 8) : reg; DumpAddrReg(os, rex, reg_num); } static void DumpIndexReg(std::ostream& os, uint8_t rex, uint8_t reg) { - bool rex_x = (rex & 0b0010) != 0; + bool rex_x = (rex & REX_X) != 0; uint8_t reg_num = rex_x ? (reg + 8) : reg; DumpAddrReg(os, rex, reg_num); } static void DumpOpcodeReg(std::ostream& os, uint8_t rex, uint8_t reg) { - bool rex_b = (rex & 0b0001) != 0; + bool rex_b = (rex & REX_B) != 0; size_t reg_num = rex_b ? (reg + 8) : reg; DumpReg0(os, rex, reg_num, false, 0); } @@ -896,6 +902,7 @@ DISASSEMBLER_ENTRY(cmp, case 0xB0: case 0xB1: case 0xB2: case 0xB3: case 0xB4: case 0xB5: case 0xB6: case 0xB7: opcode << "mov"; immediate_bytes = 1; + byte_operand = true; reg_in_opcode = true; break; case 0xB8: case 0xB9: case 0xBA: case 0xBB: case 0xBC: case 0xBD: case 0xBE: case 0xBF: @@ -916,6 +923,15 @@ DISASSEMBLER_ENTRY(cmp, byte_operand = (*instr == 0xC0); break; case 0xC3: opcode << "ret"; break; + case 0xC6: + static const char* c6_opcodes[] = {"mov", "unknown-c6", "unknown-c6", "unknown-c6", "unknown-c6", "unknown-c6", "unknown-c6", "unknown-c6"}; + modrm_opcodes = c6_opcodes; + store = true; + immediate_bytes = 1; + has_modrm = true; + reg_is_opcode = true; + byte_operand = true; + break; case 0xC7: static const char* c7_opcodes[] = {"mov", "unknown-c7", "unknown-c7", "unknown-c7", "unknown-c7", "unknown-c7", "unknown-c7", "unknown-c7"}; modrm_opcodes = c7_opcodes; @@ -1064,6 +1080,16 @@ DISASSEMBLER_ENTRY(cmp, if (reg_is_opcode && modrm_opcodes != NULL) { opcode << modrm_opcodes[reg_or_opcode]; } + + // Add opcode suffixes to indicate size. + if (byte_operand) { + opcode << 'b'; + } else if ((rex & REX_W) != 0) { + opcode << 'q'; + } else if (prefix[2] == 0x66) { + opcode << 'w'; + } + if (load) { if (!reg_is_opcode) { DumpReg(args, rex, reg_or_opcode, byte_operand, prefix[2], dst_reg_file); diff --git a/oatdump/Android.mk b/oatdump/Android.mk index ecf6a0b868..c35ff857c4 100644 --- a/oatdump/Android.mk +++ b/oatdump/Android.mk @@ -16,11 +16,11 @@ LOCAL_PATH := $(call my-dir) +include art/build/Android.executable.mk + OATDUMP_SRC_FILES := \ oatdump.cc -include art/build/Android.executable.mk - ifeq ($(ART_BUILD_TARGET_NDEBUG),true) $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libart-disassembler,art/disassembler,target,ndebug)) endif @@ -34,3 +34,62 @@ endif ifeq ($(ART_BUILD_HOST_DEBUG),true) $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libartd-disassembler,art/disassembler,host,debug)) endif + +######################################################################## +# oatdump targets + +ART_DUMP_OAT_PATH ?= $(OUT_DIR) + +OATDUMP := $(HOST_OUT_EXECUTABLES)/oatdump$(HOST_EXECUTABLE_SUFFIX) +OATDUMPD := $(HOST_OUT_EXECUTABLES)/oatdumpd$(HOST_EXECUTABLE_SUFFIX) +# TODO: for now, override with debug version for better error reporting +OATDUMP := $(OATDUMPD) + +.PHONY: dump-oat +dump-oat: dump-oat-core dump-oat-boot + +.PHONY: dump-oat-core +dump-oat-core: dump-oat-core-host dump-oat-core-target + +.PHONY: dump-oat-core-host +ifeq ($(ART_BUILD_HOST),true) +dump-oat-core-host: $(HOST_CORE_IMG_OUT) $(OATDUMP) + $(OATDUMP) --image=$(HOST_CORE_IMG_LOCATION) --output=$(ART_DUMP_OAT_PATH)/core.host.oatdump.txt + @echo Output in $(ART_DUMP_OAT_PATH)/core.host.oatdump.txt +endif + +.PHONY: dump-oat-core-target +ifeq ($(ART_BUILD_TARGET),true) +dump-oat-core-target: $(TARGET_CORE_IMG_OUT) $(OATDUMP) + $(OATDUMP) --image=$(TARGET_CORE_IMG_LOCATION) \ + --output=$(ART_DUMP_OAT_PATH)/core.target.oatdump.txt --instruction-set=$(TARGET_ARCH) + @echo Output in $(ART_DUMP_OAT_PATH)/core.target.oatdump.txt +endif + +.PHONY: dump-oat-boot-$(TARGET_ARCH) +ifeq ($(ART_BUILD_TARGET_NDEBUG),true) +dump-oat-boot-$(TARGET_ARCH): $(DEFAULT_DEX_PREOPT_BUILT_IMAGE_FILENAME) $(OATDUMP) + $(OATDUMP) --image=$(DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION) \ + --output=$(ART_DUMP_OAT_PATH)/boot.$(TARGET_ARCH).oatdump.txt --instruction-set=$(TARGET_ARCH) + @echo Output in $(ART_DUMP_OAT_PATH)/boot.$(TARGET_ARCH).oatdump.txt +endif + +ifdef TARGET_2ND_ARCH +dump-oat-boot-$(TARGET_2ND_ARCH): $(2ND_DEFAULT_DEX_PREOPT_BUILT_IMAGE_FILENAME) $(OATDUMP) + $(OATDUMP) --image=$(2ND_DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION) \ + --output=$(ART_DUMP_OAT_PATH)/boot.$(TARGET_2ND_ARCH).oatdump.txt --instruction-set=$(TARGET_2ND_ARCH) + @echo Output in $(ART_DUMP_OAT_PATH)/boot.$(TARGET_2ND_ARCH).oatdump.txt +endif + +.PHONY: dump-oat-boot +dump-oat-boot: dump-oat-boot-$(TARGET_ARCH) +ifdef TARGET_2ND_ARCH +dump-oat-boot: dump-oat-boot-$(TARGET_2ND_ARCH) +endif + +.PHONY: dump-oat-Calculator +ifeq ($(ART_BUILD_TARGET_NDEBUG),true) +dump-oat-Calculator: $(TARGET_OUT_APPS)/Calculator.odex $(DEFAULT_DEX_PREOPT_BUILT_IMAGE) $(OATDUMP) + $(OATDUMP) --oat-file=$< --output=$(ART_DUMP_OAT_PATH)/Calculator.oatdump.txt + @echo Output in $(ART_DUMP_OAT_PATH)/Calculator.oatdump.txt +endif diff --git a/runtime/Android.mk b/runtime/Android.mk index c40ae7a8fe..c6ac9cab68 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -16,7 +16,7 @@ LOCAL_PATH := $(call my-dir) -include art/build/Android.common.mk +include art/build/Android.common_build.mk LIBART_COMMON_SRC_FILES := \ atomic.cc.arm \ @@ -54,6 +54,7 @@ LIBART_COMMON_SRC_FILES := \ gc/collector/concurrent_copying.cc \ gc/collector/garbage_collector.cc \ gc/collector/immune_region.cc \ + gc/collector/mark_compact.cc \ gc/collector/mark_sweep.cc \ gc/collector/partial_mark_sweep.cc \ gc/collector/semi_space.cc \ @@ -267,10 +268,6 @@ ifeq ($(TARGET_ARCH),mips64) $(info TODOMips64: $(LOCAL_PATH)/Android.mk Add mips64 specific runtime files) endif # TARGET_ARCH != mips64 -ifeq (,$(filter $(TARGET_ARCH),$(ART_SUPPORTED_ARCH))) -$(warning unsupported TARGET_ARCH=$(TARGET_ARCH)) -endif - LIBART_HOST_SRC_FILES := \ $(LIBART_COMMON_SRC_FILES) \ base/logging_linux.cc \ @@ -334,8 +331,8 @@ define build-libart art_target_or_host := $(1) art_ndebug_or_debug := $(2) - include $(CLEAR_VARS) - LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) + include $$(CLEAR_VARS) + LOCAL_CPP_EXTENSION := $$(ART_CPP_EXTENSION) ifeq ($$(art_ndebug_or_debug),ndebug) LOCAL_MODULE := libart else # debug @@ -346,13 +343,13 @@ define build-libart LOCAL_MODULE_CLASS := SHARED_LIBRARIES ifeq ($$(art_target_or_host),target) - LOCAL_SRC_FILES := $(LIBART_TARGET_SRC_FILES) - $(foreach arch,$(ART_SUPPORTED_ARCH), - LOCAL_SRC_FILES_$(arch) := $$(LIBART_TARGET_SRC_FILES_$(arch))) + LOCAL_SRC_FILES := $$(LIBART_TARGET_SRC_FILES) + $$(foreach arch,$$(ART_TARGET_SUPPORTED_ARCH), \ + $$(eval LOCAL_SRC_FILES_$$(arch) := $$$$(LIBART_TARGET_SRC_FILES_$$(arch)))) else # host - LOCAL_SRC_FILES := $(LIBART_HOST_SRC_FILES) - LOCAL_SRC_FILES_32 := $(LIBART_HOST_SRC_FILES_32) - LOCAL_SRC_FILES_64 := $(LIBART_HOST_SRC_FILES_64) + LOCAL_SRC_FILES := $$(LIBART_HOST_SRC_FILES) + LOCAL_SRC_FILES_32 := $$(LIBART_HOST_SRC_FILES_32) + LOCAL_SRC_FILES_64 := $$(LIBART_HOST_SRC_FILES_64) LOCAL_IS_HOST_MODULE := true endif @@ -367,43 +364,43 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT LOCAL_GENERATED_SOURCES += $$(ENUM_OPERATOR_OUT_GEN) - LOCAL_CFLAGS := $(LIBART_CFLAGS) - LOCAL_LDFLAGS := $(LIBART_LDFLAGS) + LOCAL_CFLAGS := $$(LIBART_CFLAGS) + LOCAL_LDFLAGS := $$(LIBART_LDFLAGS) ifeq ($$(art_target_or_host),target) - LOCAL_LDFLAGS += $(LIBART_TARGET_LDFLAGS) + LOCAL_LDFLAGS += $$(LIBART_TARGET_LDFLAGS) else - LOCAL_LDFLAGS += $(LIBART_HOST_LDFLAGS) + LOCAL_LDFLAGS += $$(LIBART_HOST_LDFLAGS) endif - $(foreach arch,$(ART_SUPPORTED_ARCH), - LOCAL_LDFLAGS_$(arch) := $$(LIBART_TARGET_LDFLAGS_$(arch))) + $$(foreach arch,$$(ART_TARGET_SUPPORTED_ARCH), \ + $$(eval LOCAL_LDFLAGS_$$(arch) := $$(LIBART_TARGET_LDFLAGS_$$(arch)))) # Clang usage ifeq ($$(art_target_or_host),target) - $(call set-target-local-clang-vars) - $(call set-target-local-cflags-vars,$(2)) + $$(eval $$(call set-target-local-clang-vars)) + $$(eval $$(call set-target-local-cflags-vars,$(2))) # TODO: Loop with ifeq, ART_TARGET_CLANG - ifneq ($$(ART_TARGET_CLANG_$(TARGET_ARCH)),true) - LOCAL_SRC_FILES_$(TARGET_ARCH) += $(LIBART_GCC_ONLY_SRC_FILES) + ifneq ($$(ART_TARGET_CLANG_$$(TARGET_ARCH)),true) + LOCAL_SRC_FILES_$$(TARGET_ARCH) += $$(LIBART_GCC_ONLY_SRC_FILES) endif - ifneq ($$(ART_TARGET_CLANG_$(TARGET_2ND_ARCH)),true) - LOCAL_SRC_FILES_$(TARGET_2ND_ARCH) += $(LIBART_GCC_ONLY_SRC_FILES) + ifneq ($$(ART_TARGET_CLANG_$$(TARGET_2ND_ARCH)),true) + LOCAL_SRC_FILES_$$(TARGET_2ND_ARCH) += $$(LIBART_GCC_ONLY_SRC_FILES) endif else # host - LOCAL_CLANG := $(ART_HOST_CLANG) - ifeq ($(ART_HOST_CLANG),false) - LOCAL_SRC_FILES += $(LIBART_GCC_ONLY_SRC_FILES) + LOCAL_CLANG := $$(ART_HOST_CLANG) + ifeq ($$(ART_HOST_CLANG),false) + LOCAL_SRC_FILES += $$(LIBART_GCC_ONLY_SRC_FILES) endif - LOCAL_CFLAGS += $(ART_HOST_CFLAGS) + LOCAL_CFLAGS += $$(ART_HOST_CFLAGS) ifeq ($$(art_ndebug_or_debug),debug) - LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS) - LOCAL_LDLIBS += $(ART_HOST_DEBUG_LDLIBS) + LOCAL_CFLAGS += $$(ART_HOST_DEBUG_CFLAGS) + LOCAL_LDLIBS += $$(ART_HOST_DEBUG_LDLIBS) LOCAL_STATIC_LIBRARIES := libgtest_host else - LOCAL_CFLAGS += $(ART_HOST_NON_DEBUG_CFLAGS) + LOCAL_CFLAGS += $$(ART_HOST_NON_DEBUG_CFLAGS) endif endif - LOCAL_C_INCLUDES += $(ART_C_INCLUDES) + LOCAL_C_INCLUDES += $$(ART_C_INCLUDES) LOCAL_C_INCLUDES += art/sigchainlib LOCAL_SHARED_LIBRARIES += liblog libnativehelper @@ -415,23 +412,24 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT else # host LOCAL_STATIC_LIBRARIES += libcutils libziparchive-host libz libutils LOCAL_LDLIBS += -ldl -lpthread - ifeq ($(HOST_OS),linux) + ifeq ($$(HOST_OS),linux) LOCAL_LDLIBS += -lrt endif + LOCAL_MULTILIB := both endif - ifeq ($(ART_USE_PORTABLE_COMPILER),true) - include $(LLVM_GEN_INTRINSICS_MK) + ifeq ($$(ART_USE_PORTABLE_COMPILER),true) + include $$(LLVM_GEN_INTRINSICS_MK) ifeq ($$(art_target_or_host),target) - include $(LLVM_DEVICE_BUILD_MK) + include $$(LLVM_DEVICE_BUILD_MK) else # host - include $(LLVM_HOST_BUILD_MK) + include $$(LLVM_HOST_BUILD_MK) endif endif - LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk - LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk + LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk +# LOCAL_ADDITIONAL_DEPENDENCIES += $$(LOCAL_PATH)/Android.mk ifeq ($$(art_target_or_host),target) - LOCAL_MODULE_TARGET_ARCH := $(ART_SUPPORTED_ARCH) + LOCAL_MODULE_TARGET_ARCH := $$(ART_TARGET_SUPPORTED_ARCH) endif ifeq ($$(art_target_or_host),target) @@ -440,10 +438,17 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT # produce meaningful name resolution. LOCAL_STRIP_MODULE := keep_symbols endif - include $(BUILD_SHARED_LIBRARY) + include $$(BUILD_SHARED_LIBRARY) else # host - include $(BUILD_HOST_SHARED_LIBRARY) + include $$(BUILD_HOST_SHARED_LIBRARY) endif + + # Clear locally defined variables. + GENERATED_SRC_DIR := + ENUM_OPERATOR_OUT_CC_FILES := + ENUM_OPERATOR_OUT_GEN := + art_target_or_host := + art_ndebug_or_debug := endef # We always build dex2oat and dependencies, even if the host build is otherwise disabled, since @@ -456,9 +461,28 @@ ifeq ($(ART_BUILD_DEBUG),true) endif ifeq ($(ART_BUILD_TARGET_NDEBUG),true) +# $(error $(call build-libart,target,ndebug)) $(eval $(call build-libart,target,ndebug)) endif ifeq ($(ART_BUILD_TARGET_DEBUG),true) $(eval $(call build-libart,target,debug)) endif +# Clear locally defined variables. +LOCAL_PATH := +LIBART_COMMON_SRC_FILES := +LIBART_GCC_ONLY_SRC_FILES := +LIBART_TARGET_LDFLAGS := +LIBART_HOST_LDFLAGS := +LIBART_TARGET_SRC_FILES := +LIBART_TARGET_SRC_FILES_arm := +LIBART_TARGET_SRC_FILES_arm64 := +LIBART_TARGET_SRC_FILES_x86 := +LIBART_TARGET_SRC_FILES_x86_64 := +LIBART_TARGET_SRC_FILES_mips := +LIBART_HOST_SRC_FILES := +LIBART_HOST_SRC_FILES_32 := +LIBART_HOST_SRC_FILES_64 := +LIBART_ENUM_OPERATOR_OUT_HEADER_FILES := +LIBART_CFLAGS := +build-libart :=
\ No newline at end of file diff --git a/runtime/arch/arm64/memcmp.S b/runtime/arch/arm64/memcmp.S new file mode 100644 index 0000000000..d73fb67ab9 --- /dev/null +++ b/runtime/arch/arm64/memcmp.S @@ -0,0 +1,144 @@ +/* + * 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. + */ + +/* Assumptions: + * + * ARMv8-a, AArch64 + */ + +#include <private/bionic_asm.h> + +/* Parameters and result. */ +#define src1 x0 +#define src2 x1 +#define limit x2 +#define result x0 + +/* Internal variables. */ +#define data1 x3 +#define data1w w3 +#define data2 x4 +#define data2w w4 +#define has_nul x5 +#define diff x6 +#define endloop x7 +#define tmp1 x8 +#define tmp2 x9 +#define tmp3 x10 +#define pos x11 +#define limit_wd x12 +#define mask x13 + +ENTRY(memcmp) + cbz limit, .Lret0 + eor tmp1, src1, src2 + tst tmp1, #7 + b.ne .Lmisaligned8 + ands tmp1, src1, #7 + b.ne .Lmutual_align + add limit_wd, limit, #7 + lsr limit_wd, limit_wd, #3 + /* Start of performance-critical section -- one 64B cache line. */ +.Lloop_aligned: + ldr data1, [src1], #8 + ldr data2, [src2], #8 +.Lstart_realigned: + subs limit_wd, limit_wd, #1 + eor diff, data1, data2 /* Non-zero if differences found. */ + csinv endloop, diff, xzr, ne /* Last Dword or differences. */ + cbz endloop, .Lloop_aligned + /* End of performance-critical section -- one 64B cache line. */ + + /* Not reached the limit, must have found a diff. */ + cbnz limit_wd, .Lnot_limit + + /* Limit % 8 == 0 => all bytes significant. */ + ands limit, limit, #7 + b.eq .Lnot_limit + + lsl limit, limit, #3 /* Bits -> bytes. */ + mov mask, #~0 +#ifdef __AARCH64EB__ + lsr mask, mask, limit +#else + lsl mask, mask, limit +#endif + bic data1, data1, mask + bic data2, data2, mask + + orr diff, diff, mask +.Lnot_limit: + +#ifndef __AARCH64EB__ + rev diff, diff + rev data1, data1 + rev data2, data2 +#endif + /* The MS-non-zero bit of DIFF marks either the first bit + that is different, or the end of the significant data. + Shifting left now will bring the critical information into the + top bits. */ + clz pos, diff + lsl data1, data1, pos + lsl data2, data2, pos + /* But we need to zero-extend (char is unsigned) the value and then + perform a signed 32-bit subtraction. */ + lsr data1, data1, #56 + sub result, data1, data2, lsr #56 + ret + +.Lmutual_align: + /* Sources are mutually aligned, but are not currently at an + alignment boundary. Round down the addresses and then mask off + the bytes that precede the start point. */ + bic src1, src1, #7 + bic src2, src2, #7 + add limit, limit, tmp1 /* Adjust the limit for the extra. */ + lsl tmp1, tmp1, #3 /* Bytes beyond alignment -> bits. */ + ldr data1, [src1], #8 + neg tmp1, tmp1 /* Bits to alignment -64. */ + ldr data2, [src2], #8 + mov tmp2, #~0 +#ifdef __AARCH64EB__ + /* Big-endian. Early bytes are at MSB. */ + lsl tmp2, tmp2, tmp1 /* Shift (tmp1 & 63). */ +#else + /* Little-endian. Early bytes are at LSB. */ + lsr tmp2, tmp2, tmp1 /* Shift (tmp1 & 63). */ +#endif + add limit_wd, limit, #7 + orr data1, data1, tmp2 + orr data2, data2, tmp2 + lsr limit_wd, limit_wd, #3 + b .Lstart_realigned + +.Lret0: + mov result, #0 + ret + + .p2align 6 +.Lmisaligned8: + sub limit, limit, #1 +1: + /* Perhaps we can do better than this. */ + ldrb data1w, [src1], #1 + ldrb data2w, [src2], #1 + subs limit, limit, #1 + ccmp data1w, data2w, #0, cs /* NZCV = 0b0000. */ + b.eq 1b + sub result, data1, data2 + ret +END(memcmp) diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index 22b8cca4d4..a31c08b8c2 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -258,7 +258,7 @@ class StubTest : public CommonRuntimeTest { "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31", "memory"); // clobber. -#elif defined(__x86_64__) +#elif defined(__x86_64__) && !defined(__APPLE__) // Note: Uses the native convention // TODO: Set the thread? __asm__ __volatile__( @@ -483,7 +483,7 @@ class StubTest : public CommonRuntimeTest { "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31", "memory"); // clobber. -#elif defined(__x86_64__) +#elif defined(__x86_64__) && !defined(__APPLE__) // Note: Uses the native convention // TODO: Set the thread? __asm__ __volatile__( @@ -518,7 +518,7 @@ class StubTest : public CommonRuntimeTest { // Method with 32b arg0, 64b arg1 size_t Invoke3UWithReferrer(size_t arg0, uint64_t arg1, uintptr_t code, Thread* self, mirror::ArtMethod* referrer) { -#if defined(__x86_64__) || defined(__aarch64__) +#if (defined(__x86_64__) && !defined(__APPLE__)) || defined(__aarch64__) // Just pass through. return Invoke3WithReferrer(arg0, arg1, 0U, code, self, referrer); #else @@ -533,7 +533,7 @@ class StubTest : public CommonRuntimeTest { // Method with 32b arg0, 32b arg1, 64b arg2 size_t Invoke3UUWithReferrer(uint32_t arg0, uint32_t arg1, uint64_t arg2, uintptr_t code, Thread* self, mirror::ArtMethod* referrer) { -#if defined(__x86_64__) || defined(__aarch64__) +#if (defined(__x86_64__) && !defined(__APPLE__)) || defined(__aarch64__) // Just pass through. return Invoke3WithReferrer(arg0, arg1, arg2, code, self, referrer); #else @@ -547,12 +547,12 @@ class StubTest : public CommonRuntimeTest { }; -#if defined(__i386__) || defined(__x86_64__) +#if defined(__i386__) || (defined(__x86_64__) && !defined(__APPLE__)) extern "C" void art_quick_memcpy(void); #endif TEST_F(StubTest, Memcpy) { -#if defined(__i386__) || defined(__x86_64__) +#if defined(__i386__) || (defined(__x86_64__) && !defined(__APPLE__)) Thread* self = Thread::Current(); uint32_t orig[20]; @@ -588,12 +588,12 @@ TEST_F(StubTest, Memcpy) { #endif } -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) extern "C" void art_quick_lock_object(void); #endif TEST_F(StubTest, LockObject) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) static constexpr size_t kThinLockLoops = 100; Thread* self = Thread::Current(); @@ -664,14 +664,14 @@ class RandGen { }; -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) extern "C" void art_quick_lock_object(void); extern "C" void art_quick_unlock_object(void); #endif // NO_THREAD_SAFETY_ANALYSIS as we do not want to grab exclusive mutator lock for MonitorInfo. static void TestUnlockObject(StubTest* test) NO_THREAD_SAFETY_ANALYSIS { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) static constexpr size_t kThinLockLoops = 100; Thread* self = Thread::Current(); @@ -817,12 +817,12 @@ TEST_F(StubTest, UnlockObject) { TestUnlockObject(this); } -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) extern "C" void art_quick_check_cast(void); #endif TEST_F(StubTest, CheckCast) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) Thread* self = Thread::Current(); // Find some classes. ScopedObjectAccess soa(self); @@ -867,7 +867,7 @@ TEST_F(StubTest, CheckCast) { } -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) extern "C" void art_quick_aput_obj_with_null_and_bound_check(void); // Do not check non-checked ones, we'd need handlers and stuff... #endif @@ -875,7 +875,7 @@ extern "C" void art_quick_aput_obj_with_null_and_bound_check(void); TEST_F(StubTest, APutObj) { TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING(); -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) Thread* self = Thread::Current(); // Create an object ScopedObjectAccess soa(self); @@ -1003,7 +1003,7 @@ TEST_F(StubTest, APutObj) { TEST_F(StubTest, AllocObject) { TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING(); -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) // TODO: Check the "Unresolved" allocation stubs Thread* self = Thread::Current(); @@ -1125,7 +1125,7 @@ TEST_F(StubTest, AllocObject) { TEST_F(StubTest, AllocObjectArray) { TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING(); -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) // TODO: Check the "Unresolved" allocation stubs Thread* self = Thread::Current(); @@ -1204,14 +1204,14 @@ TEST_F(StubTest, AllocObjectArray) { } -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) extern "C" void art_quick_string_compareto(void); #endif TEST_F(StubTest, StringCompareTo) { TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING(); -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) // TODO: Check the "Unresolved" allocation stubs Thread* self = Thread::Current(); @@ -1301,7 +1301,7 @@ TEST_F(StubTest, StringCompareTo) { } -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) extern "C" void art_quick_set32_static(void); extern "C" void art_quick_get32_static(void); #endif @@ -1309,7 +1309,7 @@ extern "C" void art_quick_get32_static(void); static void GetSet32Static(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f, Thread* self, mirror::ArtMethod* referrer, StubTest* test) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) constexpr size_t num_values = 7; uint32_t values[num_values] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF }; @@ -1337,7 +1337,7 @@ static void GetSet32Static(Handle<mirror::Object>* obj, Handle<mirror::ArtField> } -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) extern "C" void art_quick_set32_instance(void); extern "C" void art_quick_get32_instance(void); #endif @@ -1345,7 +1345,7 @@ extern "C" void art_quick_get32_instance(void); static void GetSet32Instance(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f, Thread* self, mirror::ArtMethod* referrer, StubTest* test) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) constexpr size_t num_values = 7; uint32_t values[num_values] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF }; @@ -1379,7 +1379,7 @@ static void GetSet32Instance(Handle<mirror::Object>* obj, Handle<mirror::ArtFiel } -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) extern "C" void art_quick_set_obj_static(void); extern "C" void art_quick_get_obj_static(void); @@ -1406,7 +1406,7 @@ static void set_and_check_static(uint32_t f_idx, mirror::Object* val, Thread* se static void GetSetObjStatic(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f, Thread* self, mirror::ArtMethod* referrer, StubTest* test) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) set_and_check_static((*f)->GetDexFieldIndex(), nullptr, self, referrer, test); // Allocate a string object for simplicity. @@ -1422,7 +1422,7 @@ static void GetSetObjStatic(Handle<mirror::Object>* obj, Handle<mirror::ArtField } -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) extern "C" void art_quick_set_obj_instance(void); extern "C" void art_quick_get_obj_instance(void); @@ -1453,7 +1453,7 @@ static void set_and_check_instance(Handle<mirror::ArtField>* f, mirror::Object* static void GetSetObjInstance(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f, Thread* self, mirror::ArtMethod* referrer, StubTest* test) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) set_and_check_instance(f, obj->Get(), nullptr, self, referrer, test); // Allocate a string object for simplicity. @@ -1471,7 +1471,7 @@ static void GetSetObjInstance(Handle<mirror::Object>* obj, Handle<mirror::ArtFie // TODO: Complete these tests for 32b architectures. -#if defined(__x86_64__) || defined(__aarch64__) +#if (defined(__x86_64__) && !defined(__APPLE__)) || defined(__aarch64__) extern "C" void art_quick_set64_static(void); extern "C" void art_quick_get64_static(void); #endif @@ -1479,7 +1479,7 @@ extern "C" void art_quick_get64_static(void); static void GetSet64Static(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f, Thread* self, mirror::ArtMethod* referrer, StubTest* test) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if defined(__x86_64__) || defined(__aarch64__) +#if (defined(__x86_64__) && !defined(__APPLE__)) || defined(__aarch64__) constexpr size_t num_values = 8; uint64_t values[num_values] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF, 0xFFFFFFFFFFFF }; @@ -1506,7 +1506,7 @@ static void GetSet64Static(Handle<mirror::Object>* obj, Handle<mirror::ArtField> } -#if defined(__x86_64__) || defined(__aarch64__) +#if (defined(__x86_64__) && !defined(__APPLE__)) || defined(__aarch64__) extern "C" void art_quick_set64_instance(void); extern "C" void art_quick_get64_instance(void); #endif @@ -1514,7 +1514,7 @@ extern "C" void art_quick_get64_instance(void); static void GetSet64Instance(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f, Thread* self, mirror::ArtMethod* referrer, StubTest* test) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if defined(__x86_64__) || defined(__aarch64__) +#if (defined(__x86_64__) && !defined(__APPLE__)) || defined(__aarch64__) constexpr size_t num_values = 8; uint64_t values[num_values] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF, 0xFFFFFFFFFFFF }; @@ -1678,12 +1678,12 @@ TEST_F(StubTest, Fields64) { TestFields(self, this, Primitive::Type::kPrimLong); } -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) extern "C" void art_quick_imt_conflict_trampoline(void); #endif TEST_F(StubTest, IMT) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING(); Thread* self = Thread::Current(); diff --git a/runtime/arch/x86_64/asm_support_x86_64.S b/runtime/arch/x86_64/asm_support_x86_64.S index 34c8b82166..70c71c24f2 100644 --- a/runtime/arch/x86_64/asm_support_x86_64.S +++ b/runtime/arch/x86_64/asm_support_x86_64.S @@ -19,7 +19,7 @@ #include "asm_support_x86_64.h" -#if defined(__clang__) && (__clang_major__ < 4) && (__clang_minor__ < 5) +#if defined(__APPLE__) || (defined(__clang__) && (__clang_major__ < 4) && (__clang_minor__ < 5)) // Clang's as(1) doesn't let you name macro parameters prior to 3.5. #define MACRO0(macro_name) .macro macro_name #define MACRO1(macro_name, macro_arg1) .macro macro_name @@ -27,13 +27,12 @@ #define MACRO3(macro_name, macro_arg1, macro_args2, macro_args3) .macro macro_name #define END_MACRO .endmacro - // Clang's as(1) uses $0, $1, and so on for macro arguments prior to 3.5. + // Clang's as(1) uses $0, $1, and so on for macro arguments. + #define RAW_VAR(name,index) $index #define VAR(name,index) SYMBOL($index) - #define PLT_VAR(name, index) SYMBOL($index)@PLT + #define PLT_VAR(name, index) PLT_SYMBOL($index) #define REG_VAR(name,index) %$index #define CALL_MACRO(name,index) $index - #define FUNCTION_TYPE(name,index) .type $index, @function - #define SIZE(name,index) .size $index, .-$index // The use of $x for arguments mean that literals need to be represented with $$x in macros. #define LITERAL(value) $value @@ -52,17 +51,27 @@ // no special meaning to $, so literals are still just $x. The use of altmacro means % is a // special character meaning care needs to be taken when passing registers as macro arguments. .altmacro + #define RAW_VAR(name,index) name& #define VAR(name,index) name& #define PLT_VAR(name, index) name&@PLT #define REG_VAR(name,index) %name #define CALL_MACRO(name,index) name& - #define FUNCTION_TYPE(name,index) .type name&, @function - #define SIZE(name,index) .size name, .-name #define LITERAL(value) $value #define MACRO_LITERAL(value) $value #endif +#if defined(__APPLE__) + #define FUNCTION_TYPE(name,index) + #define SIZE(name,index) +#elif defined(__clang__) && (__clang_major__ < 4) && (__clang_minor__ < 5) + #define FUNCTION_TYPE(name,index) .type $index, @function + #define SIZE(name,index) .size $index, .-$index +#else + #define FUNCTION_TYPE(name,index) .type name&, @function + #define SIZE(name,index) .size name, .-name +#endif + // CFI support. #if !defined(__APPLE__) #define CFI_STARTPROC .cfi_startproc @@ -86,9 +95,14 @@ // Symbols. #if !defined(__APPLE__) #define SYMBOL(name) name - #define PLT_SYMBOL(name) name ## @PLT + #if defined(__clang__) && (__clang_major__ < 4) && (__clang_minor__ < 5) + // TODO: Disabled for old clang 3.3, this leads to text relocations and there should be a + // better fix. + #define PLT_SYMBOL(name) name // ## @PLT + #else + #define PLT_SYMBOL(name) name ## @PLT + #endif #else - // Mac OS' symbols have an _ prefix. #define SYMBOL(name) _ ## name #define PLT_SYMBOL(name) _ ## name #endif @@ -103,8 +117,10 @@ MACRO1(DEFINE_FUNCTION, c_name) .globl VAR(c_name, 0) ALIGN_FUNCTION_ENTRY VAR(c_name, 0): +#if !defined(__APPLE__) // Have a local entrypoint that's not globl VAR(c_name, 0)_local: +#endif CFI_STARTPROC // Ensure we get a sane starting CFA. CFI_DEF_CFA(rsp, 8) diff --git a/runtime/arch/x86_64/context_x86_64.cc b/runtime/arch/x86_64/context_x86_64.cc index 0ccbd279f5..e1f47ee3d4 100644 --- a/runtime/arch/x86_64/context_x86_64.cc +++ b/runtime/arch/x86_64/context_x86_64.cc @@ -59,8 +59,8 @@ void X86_64Context::FillCalleeSaves(const StackVisitor& fr) { size_t j = 2; // Offset j to skip return address spill. for (size_t i = 0; i < kNumberOfFloatRegisters; ++i) { if (((frame_info.FpSpillMask() >> i) & 1) != 0) { - fprs_[i] = fr.CalleeSaveAddress(spill_count + fp_spill_count - j, - frame_info.FrameSizeInBytes()); + fprs_[i] = reinterpret_cast<uint64_t*>( + fr.CalleeSaveAddress(spill_count + fp_spill_count - j, frame_info.FrameSizeInBytes())); j++; } } @@ -93,7 +93,7 @@ bool X86_64Context::SetGPR(uint32_t reg, uintptr_t value) { bool X86_64Context::SetFPR(uint32_t reg, uintptr_t value) { CHECK_LT(reg, static_cast<uint32_t>(kNumberOfFloatRegisters)); - CHECK_NE(fprs_[reg], &gZero); + CHECK_NE(fprs_[reg], reinterpret_cast<const uint64_t*>(&gZero)); if (fprs_[reg] != nullptr) { *fprs_[reg] = value; return true; diff --git a/runtime/arch/x86_64/entrypoints_init_x86_64.cc b/runtime/arch/x86_64/entrypoints_init_x86_64.cc index 92aabeeb14..b6f51f741c 100644 --- a/runtime/arch/x86_64/entrypoints_init_x86_64.cc +++ b/runtime/arch/x86_64/entrypoints_init_x86_64.cc @@ -112,6 +112,9 @@ extern void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints); void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) { +#if defined(__APPLE__) + UNIMPLEMENTED(FATAL); +#else // Interpreter ipoints->pInterpreterToInterpreterBridge = artInterpreterToInterpreterBridge; ipoints->pInterpreterToCompiledCodeBridge = artInterpreterToCompiledCodeBridge; @@ -216,6 +219,7 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method; qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception; qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow; +#endif // __APPLE__ }; } // namespace art diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index c9220c87ba..668fb882c7 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -23,6 +23,10 @@ * Runtime::CreateCalleeSaveMethod(kSaveAll) */ MACRO0(SETUP_SAVE_ALL_CALLEE_SAVE_FRAME) +#if defined(__APPLE__) + int3 + int3 +#else // R10 := Runtime::Current() movq _ZN3art7Runtime9instance_E@GOTPCREL(%rip), %r10 movq (%r10), %r10 @@ -45,6 +49,7 @@ MACRO0(SETUP_SAVE_ALL_CALLEE_SAVE_FRAME) #if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVE != 6*8 + 8 + 8) #error "SAVE_ALL_CALLEE_SAVE_FRAME(X86_64) size not as expected." #endif +#endif // __APPLE__ END_MACRO /* @@ -52,6 +57,10 @@ END_MACRO * Runtime::CreateCalleeSaveMethod(kRefsOnly) */ MACRO0(SETUP_REF_ONLY_CALLEE_SAVE_FRAME) +#if defined(__APPLE__) + int3 + int3 +#else // R10 := Runtime::Current() movq _ZN3art7Runtime9instance_E@GOTPCREL(%rip), %r10 movq (%r10), %r10 @@ -74,6 +83,7 @@ MACRO0(SETUP_REF_ONLY_CALLEE_SAVE_FRAME) #if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 6*8 + 8 + 8) #error "REFS_ONLY_CALLEE_SAVE_FRAME(X86_64) size not as expected." #endif +#endif // __APPLE__ END_MACRO MACRO0(RESTORE_REF_ONLY_CALLEE_SAVE_FRAME) @@ -93,6 +103,10 @@ END_MACRO * Runtime::CreateCalleeSaveMethod(kRefsAndArgs) */ MACRO0(SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME) +#if defined(__APPLE__) + int3 + int3 +#else // R10 := Runtime::Current() movq _ZN3art7Runtime9instance_E@GOTPCREL(%rip), %r10 movq (%r10), %r10 @@ -130,6 +144,7 @@ MACRO0(SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME) #if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 11*8 + 80 + 8) #error "REFS_AND_ARGS_CALLEE_SAVE_FRAME(X86_64) size not as expected." #endif +#endif // __APPLE__ END_MACRO MACRO0(RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME) @@ -366,6 +381,10 @@ END_MACRO * r9 = char* shorty */ DEFINE_FUNCTION art_quick_invoke_stub +#if defined(__APPLE__) + int3 + int3 +#else // Set up argument XMM registers. leaq 1(%r9), %r10 // R10 := shorty + 1 ; ie skip return arg character. leaq 4(%rsi), %r11 // R11 := arg_array + 4 ; ie skip this pointer. @@ -431,6 +450,7 @@ DEFINE_FUNCTION art_quick_invoke_stub .Lreturn_float_quick: movss %xmm0, (%r8) // Store the floating point result. ret +#endif // __APPLE__ END_FUNCTION art_quick_invoke_stub /* @@ -445,6 +465,10 @@ END_FUNCTION art_quick_invoke_stub * r9 = char* shorty */ DEFINE_FUNCTION art_quick_invoke_static_stub +#if defined(__APPLE__) + int3 + int3 +#else // Set up argument XMM registers. leaq 1(%r9), %r10 // R10 := shorty + 1 ; ie skip return arg character movq %rsi, %r11 // R11 := arg_array @@ -509,6 +533,7 @@ DEFINE_FUNCTION art_quick_invoke_static_stub .Lreturn_float_quick2: movss %xmm0, (%r8) // Store the floating point result. ret +#endif // __APPLE__ END_FUNCTION art_quick_invoke_static_stub MACRO3(NO_ARG_DOWNCALL, c_name, cxx_name, return_macro) @@ -559,6 +584,45 @@ MACRO3(THREE_ARG_DOWNCALL, c_name, cxx_name, return_macro) END_FUNCTION VAR(c_name, 0) END_MACRO +MACRO3(ONE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) + DEFINE_FUNCTION VAR(c_name, 0) + movl 8(%rsp), %esi // pass referrer + SETUP_REF_ONLY_CALLEE_SAVE_FRAME + // arg0 is in rdi + movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current() + movq %rsp, %rcx // pass SP + call PLT_VAR(cxx_name, 1) // cxx_name(arg0, referrer, Thread*, SP) + RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + CALL_MACRO(return_macro, 2) + END_FUNCTION VAR(c_name, 0) +END_MACRO + +MACRO3(TWO_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) + DEFINE_FUNCTION VAR(c_name, 0) + movl 8(%rsp), %edx // pass referrer + SETUP_REF_ONLY_CALLEE_SAVE_FRAME + // arg0 and arg1 are in rdi/rsi + movq %gs:THREAD_SELF_OFFSET, %rcx // pass Thread::Current() + movq %rsp, %r8 // pass SP + call PLT_VAR(cxx_name, 1) // (arg0, arg1, referrer, Thread*, SP) + RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + CALL_MACRO(return_macro, 2) + END_FUNCTION VAR(c_name, 0) +END_MACRO + +MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) + DEFINE_FUNCTION VAR(c_name, 0) + movl 8(%rsp), %ecx // pass referrer + SETUP_REF_ONLY_CALLEE_SAVE_FRAME + // arg0, arg1, and arg2 are in rdi/rsi/rdx + movq %gs:THREAD_SELF_OFFSET, %r8 // pass Thread::Current() + movq %rsp, %r9 // pass SP + call PLT_VAR(cxx_name, 1) // cxx_name(arg0, arg1, arg2, referrer, Thread*, SP) + RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + CALL_MACRO(return_macro, 2) // return or deliver exception + END_FUNCTION VAR(c_name, 0) +END_MACRO + MACRO0(RETURN_IF_RESULT_IS_NON_ZERO) testq %rax, %rax // rax == 0 ? jz 1f // if rax == 0 goto 1 @@ -783,14 +847,23 @@ END_FUNCTION art_quick_check_cast * rdi(edi) = array, rsi(esi) = index, rdx(edx) = value */ DEFINE_FUNCTION art_quick_aput_obj_with_null_and_bound_check +#if defined(__APPLE__) + int3 + int3 +#else testl %edi, %edi // testq %rdi, %rdi jnz art_quick_aput_obj_with_bound_check_local jmp art_quick_throw_null_pointer_exception_local +#endif // __APPLE__ END_FUNCTION art_quick_aput_obj_with_null_and_bound_check DEFINE_FUNCTION art_quick_aput_obj_with_bound_check +#if defined(__APPLE__) + int3 + int3 +#else movl ARRAY_LENGTH_OFFSET(%edi), %ecx // movl ARRAY_LENGTH_OFFSET(%rdi), %ecx // This zero-extends, so value(%rcx)=value(%ecx) cmpl %ecx, %esi @@ -800,6 +873,7 @@ DEFINE_FUNCTION art_quick_aput_obj_with_bound_check mov %ecx, %esi // mov %rcx, %rsi jmp art_quick_throw_array_bounds_local +#endif // __APPLE__ END_FUNCTION art_quick_aput_obj_with_bound_check @@ -894,47 +968,6 @@ UNIMPLEMENTED art_quick_lshl UNIMPLEMENTED art_quick_lshr UNIMPLEMENTED art_quick_lushr - -MACRO3(ONE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) - DEFINE_FUNCTION VAR(c_name, 0) - movl 8(%rsp), %esi // pass referrer - SETUP_REF_ONLY_CALLEE_SAVE_FRAME - // arg0 is in rdi - movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current() - movq %rsp, %rcx // pass SP - call PLT_VAR(cxx_name, 1) // cxx_name(arg0, referrer, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address - CALL_MACRO(return_macro, 2) - END_FUNCTION VAR(c_name, 0) -END_MACRO - -MACRO3(TWO_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) - DEFINE_FUNCTION VAR(c_name, 0) - movl 8(%rsp), %edx // pass referrer - SETUP_REF_ONLY_CALLEE_SAVE_FRAME - // arg0 and arg1 are in rdi/rsi - movq %gs:THREAD_SELF_OFFSET, %rcx // pass Thread::Current() - movq %rsp, %r8 // pass SP - call PLT_VAR(cxx_name, 1) // (arg0, arg1, referrer, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address - CALL_MACRO(return_macro, 2) - END_FUNCTION VAR(c_name, 0) -END_MACRO - -MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) - DEFINE_FUNCTION VAR(c_name, 0) - movl 8(%rsp), %ecx // pass referrer - SETUP_REF_ONLY_CALLEE_SAVE_FRAME - // arg0, arg1, and arg2 are in rdi/rsi/rdx - movq %gs:THREAD_SELF_OFFSET, %r8 // pass Thread::Current() - movq %rsp, %r9 // pass SP - call PLT_VAR(cxx_name, 1) // cxx_name(arg0, arg1, arg2, referrer, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address - CALL_MACRO(return_macro, 2) // return or deliver exception - END_FUNCTION VAR(c_name, 0) -END_MACRO - - THREE_ARG_REF_DOWNCALL art_quick_set32_instance, artSet32InstanceFromCode, RETURN_IF_EAX_ZERO THREE_ARG_DOWNCALL art_quick_set64_instance, artSet64InstanceFromCode, RETURN_IF_EAX_ZERO THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCode, RETURN_IF_EAX_ZERO @@ -1006,10 +1039,15 @@ END_FUNCTION art_quick_proxy_invoke_handler * rax is a hidden argument that holds the target method's dex method index. */ DEFINE_FUNCTION art_quick_imt_conflict_trampoline +#if defined(__APPLE__) + int3 + int3 +#else movl 8(%rsp), %edi // load caller Method* movl METHOD_DEX_CACHE_METHODS_OFFSET(%rdi), %edi // load dex_cache_resolved_methods movl OBJECT_ARRAY_DATA_OFFSET(%rdi, %rax, 4), %edi // load the target method jmp art_quick_invoke_interface_trampoline_local +#endif // __APPLE__ END_FUNCTION art_quick_imt_conflict_trampoline DEFINE_FUNCTION art_quick_resolution_trampoline @@ -1294,6 +1332,10 @@ END_FUNCTION art_quick_to_interpreter_bridge * Routine that intercepts method calls and returns. */ DEFINE_FUNCTION art_quick_instrumentation_entry +#if defined(__APPLE__) + int3 + int3 +#else SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME movq %rdi, %r12 // Preserve method pointer in a callee-save. @@ -1313,6 +1355,7 @@ DEFINE_FUNCTION art_quick_instrumentation_entry RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME jmp *%rax // Tail call to intended method. +#endif // __APPLE__ END_FUNCTION art_quick_instrumentation_entry DEFINE_FUNCTION art_quick_instrumentation_exit diff --git a/runtime/arch/x86_64/thread_x86_64.cc b/runtime/arch/x86_64/thread_x86_64.cc index b7a5c43839..6dff2b4a55 100644 --- a/runtime/arch/x86_64/thread_x86_64.cc +++ b/runtime/arch/x86_64/thread_x86_64.cc @@ -21,18 +21,28 @@ #include "thread-inl.h" #include "thread_list.h" +#if defined(__linux__) #include <asm/prctl.h> #include <sys/prctl.h> #include <sys/syscall.h> +#endif namespace art { +#if defined(__linux__) static void arch_prctl(int code, void* val) { syscall(__NR_arch_prctl, code, val); } +#endif + void Thread::InitCpu() { MutexLock mu(nullptr, *Locks::modify_ldt_lock_); + +#if defined(__linux__) arch_prctl(ARCH_SET_GS, this); +#else + UNIMPLEMENTED(FATAL) << "Need to set GS"; +#endif // Allow easy indirection back to Thread*. tlsPtr_.self = this; diff --git a/runtime/base/histogram-inl.h b/runtime/base/histogram-inl.h index 7c0999992e..4c18ce405c 100644 --- a/runtime/base/histogram-inl.h +++ b/runtime/base/histogram-inl.h @@ -164,18 +164,18 @@ inline void Histogram<Value>::PrintBins(std::ostream& os, const CumulativeData& template <class Value> inline void Histogram<Value>::PrintConfidenceIntervals(std::ostream &os, double interval, const CumulativeData& data) const { + static constexpr size_t kFractionalDigits = 3; DCHECK_GT(interval, 0); DCHECK_LT(interval, 1.0); - - double per_0 = (1.0 - interval) / 2.0; - double per_1 = per_0 + interval; - TimeUnit unit = GetAppropriateTimeUnit(Mean() * kAdjust); - os << Name() << ":\tSum: "; - os << PrettyDuration(Sum() * kAdjust) << " "; - os << (interval * 100) << "% C.I. " << FormatDuration(Percentile(per_0, data) * kAdjust, unit); - os << "-" << FormatDuration(Percentile(per_1, data) * kAdjust, unit) << " "; - os << "Avg: " << FormatDuration(Mean() * kAdjust, unit) << " Max: "; - os << FormatDuration(Max() * kAdjust, unit) << "\n"; + const double per_0 = (1.0 - interval) / 2.0; + const double per_1 = per_0 + interval; + const TimeUnit unit = GetAppropriateTimeUnit(Mean() * kAdjust); + os << Name() << ":\tSum: " << PrettyDuration(Sum() * kAdjust) << " " + << (interval * 100) << "% C.I. " << FormatDuration(Percentile(per_0, data) * kAdjust, unit, + kFractionalDigits) + << "-" << FormatDuration(Percentile(per_1, data) * kAdjust, unit, kFractionalDigits) << " " + << "Avg: " << FormatDuration(Mean() * kAdjust, unit, kFractionalDigits) << " Max: " + << FormatDuration(Max() * kAdjust, unit, kFractionalDigits) << "\n"; } template <class Value> diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc index c0bce840e4..351de3d48f 100644 --- a/runtime/base/scoped_flock.cc +++ b/runtime/base/scoped_flock.cc @@ -63,6 +63,10 @@ File* ScopedFlock::GetFile() { return file_.get(); } +bool ScopedFlock::HasFile() { + return file_.get() != nullptr; +} + ScopedFlock::ScopedFlock() { } ScopedFlock::~ScopedFlock() { diff --git a/runtime/base/scoped_flock.h b/runtime/base/scoped_flock.h index 26b4eb0c2e..f8ed805bea 100644 --- a/runtime/base/scoped_flock.h +++ b/runtime/base/scoped_flock.h @@ -40,6 +40,10 @@ class ScopedFlock { // Returns the (locked) file associated with this instance. File* GetFile(); + + // Returns whether a file is held. + bool HasFile(); + ~ScopedFlock(); private: std::unique_ptr<File> file_; diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc index a1550028e9..b6a2aaf33b 100644 --- a/runtime/base/timing_logger.cc +++ b/runtime/base/timing_logger.cc @@ -33,6 +33,8 @@ namespace art { constexpr size_t CumulativeLogger::kLowMemoryBucketCount; constexpr size_t CumulativeLogger::kDefaultBucketCount; +constexpr size_t TimingLogger::kIndexNotFound; + CumulativeLogger::CumulativeLogger(const std::string& name) : name_(name), lock_name_("CumulativeLoggerLock" + name), @@ -66,10 +68,12 @@ void CumulativeLogger::Reset() { void CumulativeLogger::AddLogger(const TimingLogger &logger) { MutexLock mu(Thread::Current(), lock_); - for (const TimingLogger::SplitTiming& split : logger.GetSplits()) { - uint64_t split_time = split.first; - const char* split_name = split.second; - AddPair(split_name, split_time); + TimingLogger::TimingData timing_data(logger.CalculateTimingData()); + const std::vector<TimingLogger::Timing>& timings = logger.GetTimings(); + for (size_t i = 0; i < timings.size(); ++i) { + if (timings[i].IsStartTiming()) { + AddPair(timings[i].GetName(), timing_data.GetExclusiveTime(i)); + } } ++iterations_; } @@ -124,166 +128,125 @@ void CumulativeLogger::DumpHistogram(std::ostream &os) const { } TimingLogger::TimingLogger(const char* name, bool precise, bool verbose) - : name_(name), precise_(precise), verbose_(verbose), current_split_(NULL) { + : name_(name), precise_(precise), verbose_(verbose) { } void TimingLogger::Reset() { - current_split_ = NULL; - splits_.clear(); + timings_.clear(); } -void TimingLogger::StartSplit(const char* new_split_label) { - DCHECK(new_split_label != nullptr) << "Starting split with null label."; - TimingLogger::ScopedSplit* explicit_scoped_split = - new TimingLogger::ScopedSplit(new_split_label, this); - explicit_scoped_split->explicit_ = true; +void TimingLogger::StartTiming(const char* label) { + DCHECK(label != nullptr); + timings_.push_back(Timing(NanoTime(), label)); + ATRACE_BEGIN(label); } -void TimingLogger::EndSplit() { - CHECK(current_split_ != nullptr) << "Ending a non-existent split."; - DCHECK(current_split_->label_ != nullptr); - DCHECK(current_split_->explicit_ == true) - << "Explicitly ending scoped split: " << current_split_->label_; - delete current_split_; - // TODO: current_split_ = nullptr; +void TimingLogger::EndTiming() { + timings_.push_back(Timing(NanoTime(), nullptr)); + ATRACE_END(); } -// Ends the current split and starts the one given by the label. -void TimingLogger::NewSplit(const char* new_split_label) { - if (current_split_ == nullptr) { - StartSplit(new_split_label); - } else { - DCHECK(new_split_label != nullptr) << "New split (" << new_split_label << ") with null label."; - current_split_->TailInsertSplit(new_split_label); +uint64_t TimingLogger::GetTotalNs() const { + if (timings_.size() < 2) { + return 0; } + return timings_.back().GetTime() - timings_.front().GetTime(); } -uint64_t TimingLogger::GetTotalNs() const { - uint64_t total_ns = 0; - for (const TimingLogger::SplitTiming& split : splits_) { - total_ns += split.first; +size_t TimingLogger::FindTimingIndex(const char* name, size_t start_idx) const { + DCHECK_LT(start_idx, timings_.size()); + for (size_t i = start_idx; i < timings_.size(); ++i) { + if (timings_[i].IsStartTiming() && strcmp(timings_[i].GetName(), name) == 0) { + return i; + } } - return total_ns; + return kIndexNotFound; +} + +TimingLogger::TimingData TimingLogger::CalculateTimingData() const { + TimingLogger::TimingData ret; + ret.data_.resize(timings_.size()); + std::vector<size_t> open_stack; + for (size_t i = 0; i < timings_.size(); ++i) { + if (timings_[i].IsEndTiming()) { + CHECK(!open_stack.empty()) << "No starting split for ending split at index " << i; + size_t open_idx = open_stack.back(); + uint64_t time = timings_[i].GetTime() - timings_[open_idx].GetTime(); + ret.data_[open_idx].exclusive_time += time; + DCHECK_EQ(ret.data_[open_idx].total_time, 0U); + ret.data_[open_idx].total_time += time; + // Each open split has exactly one end. + open_stack.pop_back(); + // If there is a parent node, subtract from the exclusive time. + if (!open_stack.empty()) { + // Note this may go negative, but will work due to 2s complement when we add the value + // total time value later. + ret.data_[open_stack.back()].exclusive_time -= time; + } + } else { + open_stack.push_back(i); + } + } + CHECK(open_stack.empty()) << "Missing ending for timing " + << timings_[open_stack.back()].GetName() << " at index " << open_stack.back(); + return ret; // No need to fear, C++11 move semantics are here. } -void TimingLogger::Dump(std::ostream &os) const { +void TimingLogger::Dump(std::ostream &os, const char* indent_string) const { + static constexpr size_t kFractionalDigits = 3; + TimingLogger::TimingData timing_data(CalculateTimingData()); uint64_t longest_split = 0; - uint64_t total_ns = 0; - for (const SplitTiming& split : splits_) { - uint64_t split_time = split.first; - longest_split = std::max(longest_split, split_time); - total_ns += split_time; + for (size_t i = 0; i < timings_.size(); ++i) { + longest_split = std::max(longest_split, timing_data.GetTotalTime(i)); } // Compute which type of unit we will use for printing the timings. TimeUnit tu = GetAppropriateTimeUnit(longest_split); uint64_t divisor = GetNsToTimeUnitDivisor(tu); + uint64_t mod_fraction = divisor >= 1000 ? divisor / 1000 : 1; // Print formatted splits. - for (const SplitTiming& split : splits_) { - uint64_t split_time = split.first; - if (!precise_ && divisor >= 1000) { - // Make the fractional part 0. - split_time -= split_time % (divisor / 1000); + size_t tab_count = 1; + os << name_ << " [Exclusive time] [Total time]\n"; + for (size_t i = 0; i < timings_.size(); ++i) { + if (timings_[i].IsStartTiming()) { + uint64_t exclusive_time = timing_data.GetExclusiveTime(i); + uint64_t total_time = timing_data.GetTotalTime(i); + if (!precise_) { + // Make the fractional part 0. + exclusive_time -= exclusive_time % mod_fraction; + total_time -= total_time % mod_fraction; + } + for (size_t j = 0; j < tab_count; ++j) { + os << indent_string; + } + os << FormatDuration(exclusive_time, tu, kFractionalDigits); + // If they are the same, just print one value to prevent spam. + if (exclusive_time != total_time) { + os << "/" << FormatDuration(total_time, tu, kFractionalDigits); + } + os << " " << timings_[i].GetName() << "\n"; + ++tab_count; + } else { + --tab_count; } - os << name_ << ": " << std::setw(8) << FormatDuration(split_time, tu) << " " - << split.second << "\n"; } - os << name_ << ": end, " << NsToMs(total_ns) << " ms\n"; + os << name_ << ": end, " << PrettyDuration(GetTotalNs()) << "\n"; } -TimingLogger::ScopedSplit::ScopedSplit(const char* label, TimingLogger* timing_logger) { - DCHECK(label != NULL) << "New scoped split (" << label << ") with null label."; - CHECK(timing_logger != NULL) << "New scoped split (" << label << ") without TimingLogger."; - timing_logger_ = timing_logger; - label_ = label; - running_ns_ = 0; - explicit_ = false; - - // Stash away the current split and pause it. - enclosing_split_ = timing_logger->current_split_; - if (enclosing_split_ != NULL) { - enclosing_split_->Pause(); - } - - timing_logger_->current_split_ = this; - - ATRACE_BEGIN(label_); - - start_ns_ = NanoTime(); - if (timing_logger_->verbose_) { - LOG(INFO) << "Begin: " << label_; - } -} - -TimingLogger::ScopedSplit::~ScopedSplit() { - uint64_t current_time = NanoTime(); - uint64_t split_time = current_time - start_ns_; - running_ns_ += split_time; - ATRACE_END(); - - if (timing_logger_->verbose_) { - LOG(INFO) << "End: " << label_ << " " << PrettyDuration(split_time); - } - - // If one or more enclosed explicitly started splits are not terminated we can - // either fail or "unwind" the stack of splits in the timing logger to 'this' - // (by deleting the intervening scoped splits). This implements the latter. - TimingLogger::ScopedSplit* current = timing_logger_->current_split_; - while ((current != NULL) && (current != this)) { - delete current; - current = timing_logger_->current_split_; - } - - CHECK(current != NULL) << "Missing scoped split (" << this->label_ - << ") in timing logger (" << timing_logger_->name_ << ")."; - CHECK(timing_logger_->current_split_ == this); - - timing_logger_->splits_.push_back(SplitTiming(running_ns_, label_)); - - timing_logger_->current_split_ = enclosing_split_; - if (enclosing_split_ != NULL) { - enclosing_split_->Resume(); +void TimingLogger::Verify() { + size_t counts[2] = { 0 }; + for (size_t i = 0; i < timings_.size(); ++i) { + if (i > 0) { + CHECK_LE(timings_[i - 1].GetTime(), timings_[i].GetTime()); + } + ++counts[timings_[i].IsStartTiming() ? 0 : 1]; } + CHECK_EQ(counts[0], counts[1]) << "Number of StartTiming and EndTiming doesn't match"; } - -void TimingLogger::ScopedSplit::TailInsertSplit(const char* label) { - // Sleight of hand here: Rather than embedding a new scoped split, we're updating the current - // scoped split in place. Basically, it's one way to make explicit and scoped splits compose - // well while maintaining the current semantics of NewSplit. An alternative is to push a new split - // since we unwind the stack of scoped splits in the scoped split destructor. However, this implies - // that the current split is not ended by NewSplit (which calls TailInsertSplit), which would - // be different from what we had before. - - uint64_t current_time = NanoTime(); - uint64_t split_time = current_time - start_ns_; - ATRACE_END(); - timing_logger_->splits_.push_back(std::pair<uint64_t, const char*>(split_time, label_)); - - if (timing_logger_->verbose_) { - LOG(INFO) << "End: " << label_ << " " << PrettyDuration(split_time) << "\n" - << "Begin: " << label; +TimingLogger::~TimingLogger() { + if (kIsDebugBuild) { + Verify(); } - - label_ = label; - start_ns_ = current_time; - running_ns_ = 0; - - ATRACE_BEGIN(label); -} - -void TimingLogger::ScopedSplit::Pause() { - uint64_t current_time = NanoTime(); - uint64_t split_time = current_time - start_ns_; - running_ns_ += split_time; - ATRACE_END(); -} - - -void TimingLogger::ScopedSplit::Resume() { - uint64_t current_time = NanoTime(); - - start_ns_ = current_time; - ATRACE_BEGIN(label_); } } // namespace art diff --git a/runtime/base/timing_logger.h b/runtime/base/timing_logger.h index 9b558980c5..b300109e31 100644 --- a/runtime/base/timing_logger.h +++ b/runtime/base/timing_logger.h @@ -77,93 +77,119 @@ class CumulativeLogger { // A timing logger that knows when a split starts for the purposes of logging tools, like systrace. class TimingLogger { public: - // Splits are nanosecond times and split names. - typedef std::pair<uint64_t, const char*> SplitTiming; - typedef std::vector<SplitTiming> SplitTimings; + static constexpr size_t kIndexNotFound = static_cast<size_t>(-1); - explicit TimingLogger(const char* name, bool precise, bool verbose); - ~TimingLogger() { - // TODO: DCHECK(current_split_ == nullptr) << "Forgot to end split: " << current_split_->label_; - } - // Clears current splits and labels. - void Reset(); + class Timing { + public: + Timing(uint64_t time, const char* name) : time_(time), name_(name) { + } + bool IsStartTiming() const { + return !IsEndTiming(); + } + bool IsEndTiming() const { + return name_ == nullptr; + } + uint64_t GetTime() const { + return time_; + } + const char* GetName() const { + return name_; + } - // Starts a split - void StartSplit(const char* new_split_label); + private: + uint64_t time_; + const char* name_; + }; - // Ends the current split and starts the one given by the label. - void NewSplit(const char* new_split_label); + // Extra data that is only calculated when you call dump to prevent excess allocation. + class TimingData { + public: + TimingData() = default; + TimingData(TimingData&& other) { + std::swap(data_, other.data_); + } + TimingData& operator=(TimingData&& other) { + std::swap(data_, other.data_); + return *this; + } + uint64_t GetTotalTime(size_t idx) { + return data_[idx].total_time; + } + uint64_t GetExclusiveTime(size_t idx) { + return data_[idx].exclusive_time; + } - // Ends the current split and records the end time. - void EndSplit(); + private: + // Each begin split has a total time and exclusive time. Exclusive time is total time - total + // time of children nodes. + struct CalculatedDataPoint { + CalculatedDataPoint() : total_time(0), exclusive_time(0) {} + uint64_t total_time; + uint64_t exclusive_time; + }; + std::vector<CalculatedDataPoint> data_; + friend class TimingLogger; + }; + explicit TimingLogger(const char* name, bool precise, bool verbose); + ~TimingLogger(); + // Verify that all open timings have related closed timings. + void Verify(); + // Clears current timings and labels. + void Reset(); + // Starts a timing. + void StartTiming(const char* new_split_label); + // Ends the current timing. + void EndTiming(); + // End the current timing and start a new timing. Usage not recommended. + void NewTiming(const char* new_split_label) { + EndTiming(); + StartTiming(new_split_label); + } + // Returns the total duration of the timings (sum of total times). uint64_t GetTotalNs() const; - - void Dump(std::ostream& os) const; + // Find the index of a timing by name. + size_t FindTimingIndex(const char* name, size_t start_idx) const; + void Dump(std::ostream& os, const char* indent_string = " ") const; // Scoped timing splits that can be nested and composed with the explicit split // starts and ends. - class ScopedSplit { - public: - explicit ScopedSplit(const char* label, TimingLogger* timing_logger); - - ~ScopedSplit(); - - friend class TimingLogger; - - private: - // Pauses timing of the split, usually due to nesting of another split. - void Pause(); - - // Resumes timing of the split, usually because a nested split has ended. - void Resume(); - - // Used by new split to swap splits in place in a ScopedSplit instance. - void TailInsertSplit(const char* label); - - // The scoped split immediately enclosing this split. Essentially, we get a - // stack of nested splits through this field. - ScopedSplit* enclosing_split_; - - // Was this created via TimingLogger's StartSplit? - bool explicit_; - - // The split's name. - const char* label_; - - // The current split's latest start time. (It may have been paused and restarted.) - uint64_t start_ns_; - - // The running time, outside of pauses. - uint64_t running_ns_; - - // The timing logger holding this split. - TimingLogger* timing_logger_; + class ScopedTiming { + public: + explicit ScopedTiming(const char* label, TimingLogger* logger) : logger_(logger) { + logger_->StartTiming(label); + } + ~ScopedTiming() { + logger_->EndTiming(); + } + // Closes the current timing and opens a new timing. + void NewTiming(const char* label) { + logger_->NewTiming(label); + } - DISALLOW_COPY_AND_ASSIGN(ScopedSplit); + private: + TimingLogger* const logger_; // The timing logger which the scoped timing is associated with. + DISALLOW_COPY_AND_ASSIGN(ScopedTiming); }; - const SplitTimings& GetSplits() const { - return splits_; + // Return the time points of when each start / end timings start and finish. + const std::vector<Timing>& GetTimings() const { + return timings_; } - friend class ScopedSplit; + TimingData CalculateTimingData() const; + protected: // The name of the timing logger. const char* const name_; - // Do we want to print the exactly recorded split (true) or round down to the time unit being // used (false). const bool precise_; - // Verbose logging. const bool verbose_; - - // The current scoped split is also the 'top' of the stack of splits in progress. - ScopedSplit* current_split_; - - // Splits that have ended. - SplitTimings splits_; + // Timing points that are either start or end points. For each starting point ret[i] = location + // of end split associated with i. If it is and end split ret[i] = i. + std::vector<Timing> timings_; private: DISALLOW_COPY_AND_ASSIGN(TimingLogger); diff --git a/runtime/base/timing_logger_test.cc b/runtime/base/timing_logger_test.cc index 0757751822..35a73d0a76 100644 --- a/runtime/base/timing_logger_test.cc +++ b/runtime/base/timing_logger_test.cc @@ -26,16 +26,14 @@ class TimingLoggerTest : public CommonRuntimeTest {}; TEST_F(TimingLoggerTest, StartEnd) { const char* split1name = "First Split"; - TimingLogger timings("StartEnd", true, false); - - timings.StartSplit(split1name); - - timings.EndSplit(); // Ends split1. - - const TimingLogger::SplitTimings& splits = timings.GetSplits(); - - EXPECT_EQ(1U, splits.size()); - EXPECT_STREQ(splits[0].second, split1name); + TimingLogger logger("StartEnd", true, false); + logger.StartTiming(split1name); + logger.EndTiming(); // Ends split1. + const auto& timings = logger.GetTimings(); + EXPECT_EQ(2U, timings.size()); // Start, End splits + EXPECT_TRUE(timings[0].IsStartTiming()); + EXPECT_STREQ(timings[0].GetName(), split1name); + EXPECT_TRUE(timings[1].IsEndTiming()); } @@ -43,56 +41,61 @@ TEST_F(TimingLoggerTest, StartNewEnd) { const char* split1name = "First Split"; const char* split2name = "Second Split"; const char* split3name = "Third Split"; - TimingLogger timings("StartNewEnd", true, false); - - timings.StartSplit(split1name); - - timings.NewSplit(split2name); // Ends split1. - - timings.NewSplit(split3name); // Ends split2. - - timings.EndSplit(); // Ends split3. - - const TimingLogger::SplitTimings& splits = timings.GetSplits(); - - EXPECT_EQ(3U, splits.size()); - EXPECT_STREQ(splits[0].second, split1name); - EXPECT_STREQ(splits[1].second, split2name); - EXPECT_STREQ(splits[2].second, split3name); + TimingLogger logger("StartNewEnd", true, false); + logger.StartTiming(split1name); + logger.NewTiming(split2name); + logger.NewTiming(split3name); + logger.EndTiming(); + // Get the timings and verify that they are sane. + const auto& timings = logger.GetTimings(); + // 6 timings in the timing logger at this point. + EXPECT_EQ(6U, timings.size()); + EXPECT_TRUE(timings[0].IsStartTiming()); + EXPECT_STREQ(timings[0].GetName(), split1name); + EXPECT_TRUE(timings[1].IsEndTiming()); + EXPECT_TRUE(timings[2].IsStartTiming()); + EXPECT_STREQ(timings[2].GetName(), split2name); + EXPECT_TRUE(timings[3].IsEndTiming()); + EXPECT_TRUE(timings[4].IsStartTiming()); + EXPECT_STREQ(timings[4].GetName(), split3name); + EXPECT_TRUE(timings[5].IsEndTiming()); } TEST_F(TimingLoggerTest, StartNewEndNested) { - const char* split1name = "First Split"; - const char* split2name = "Second Split"; - const char* split3name = "Third Split"; - const char* split4name = "Fourth Split"; - const char* split5name = "Fifth Split"; - TimingLogger timings("StartNewEndNested", true, false); - - timings.StartSplit(split1name); - - timings.NewSplit(split2name); // Ends split1. - - timings.StartSplit(split3name); - - timings.StartSplit(split4name); - - timings.NewSplit(split5name); // Ends split4. - - timings.EndSplit(); // Ends split5. - - timings.EndSplit(); // Ends split3. - - timings.EndSplit(); // Ends split2. - - const TimingLogger::SplitTimings& splits = timings.GetSplits(); - - EXPECT_EQ(5U, splits.size()); - EXPECT_STREQ(splits[0].second, split1name); - EXPECT_STREQ(splits[1].second, split4name); - EXPECT_STREQ(splits[2].second, split5name); - EXPECT_STREQ(splits[3].second, split3name); - EXPECT_STREQ(splits[4].second, split2name); + const char* name1 = "First Split"; + const char* name2 = "Second Split"; + const char* name3 = "Third Split"; + const char* name4 = "Fourth Split"; + const char* name5 = "Fifth Split"; + TimingLogger logger("StartNewEndNested", true, false); + logger.StartTiming(name1); + logger.NewTiming(name2); // Ends timing1. + logger.StartTiming(name3); + logger.StartTiming(name4); + logger.NewTiming(name5); // Ends timing4. + logger.EndTiming(); // Ends timing5. + logger.EndTiming(); // Ends timing3. + logger.EndTiming(); // Ends timing2. + const auto& timings = logger.GetTimings(); + EXPECT_EQ(10U, timings.size()); + size_t idx_1 = logger.FindTimingIndex(name1, 0); + size_t idx_2 = logger.FindTimingIndex(name2, 0); + size_t idx_3 = logger.FindTimingIndex(name3, 0); + size_t idx_4 = logger.FindTimingIndex(name4, 0); + size_t idx_5 = logger.FindTimingIndex(name5, 0); + size_t idx_6 = logger.FindTimingIndex("Not found", 0); + EXPECT_NE(idx_1, TimingLogger::kIndexNotFound); + EXPECT_NE(idx_2, TimingLogger::kIndexNotFound); + EXPECT_NE(idx_3, TimingLogger::kIndexNotFound); + EXPECT_NE(idx_4, TimingLogger::kIndexNotFound); + EXPECT_NE(idx_5, TimingLogger::kIndexNotFound); + EXPECT_EQ(idx_6, TimingLogger::kIndexNotFound); + TimingLogger::TimingData data = logger.CalculateTimingData(); + EXPECT_STREQ(timings[idx_1].GetName(), name1); + EXPECT_STREQ(timings[idx_2].GetName(), name2); + EXPECT_STREQ(timings[idx_3].GetName(), name3); + EXPECT_STREQ(timings[idx_4].GetName(), name4); + EXPECT_STREQ(timings[idx_5].GetName(), name5); } @@ -101,31 +104,32 @@ TEST_F(TimingLoggerTest, Scoped) { const char* innersplit1 = "Inner Split 1"; const char* innerinnersplit1 = "Inner Inner Split 1"; const char* innersplit2 = "Inner Split 2"; - TimingLogger timings("Scoped", true, false); - + TimingLogger logger("Scoped", true, false); { - TimingLogger::ScopedSplit outer(outersplit, &timings); - - { - TimingLogger::ScopedSplit inner1(innersplit1, &timings); - - { - TimingLogger::ScopedSplit innerinner1(innerinnersplit1, &timings); - } // Ends innerinnersplit1. - } // Ends innersplit1. - + TimingLogger::ScopedTiming outer(outersplit, &logger); + { + TimingLogger::ScopedTiming inner1(innersplit1, &logger); { - TimingLogger::ScopedSplit inner2(innersplit2, &timings); - } // Ends innersplit2. + TimingLogger::ScopedTiming innerinner1(innerinnersplit1, &logger); + } // Ends innerinnersplit1. + } // Ends innersplit1. + { + TimingLogger::ScopedTiming inner2(innersplit2, &logger); + } // Ends innersplit2. } // Ends outersplit. - - const TimingLogger::SplitTimings& splits = timings.GetSplits(); - - EXPECT_EQ(4U, splits.size()); - EXPECT_STREQ(splits[0].second, innerinnersplit1); - EXPECT_STREQ(splits[1].second, innersplit1); - EXPECT_STREQ(splits[2].second, innersplit2); - EXPECT_STREQ(splits[3].second, outersplit); + const size_t idx_outersplit = logger.FindTimingIndex(outersplit, 0); + const size_t idx_innersplit1 = logger.FindTimingIndex(innersplit1, 0); + const size_t idx_innerinnersplit1 = logger.FindTimingIndex(innerinnersplit1, 0); + const size_t idx_innersplit2 = logger.FindTimingIndex(innersplit2, 0); + const auto& timings = logger.GetTimings(); + EXPECT_EQ(8U, timings.size()); // 4 start timings and 4 end timings. + EXPECT_GE(timings[idx_innerinnersplit1].GetTime(), timings[idx_innersplit1].GetTime()); + EXPECT_GE(timings[idx_innersplit2].GetTime(), timings[idx_innersplit1].GetTime()); + TimingLogger::TimingData data(logger.CalculateTimingData()); + EXPECT_GE(data.GetTotalTime(idx_outersplit), data.GetTotalTime(idx_innerinnersplit1)); + EXPECT_GE(data.GetTotalTime(idx_outersplit), + data.GetTotalTime(idx_innersplit1) + data.GetTotalTime(idx_innersplit2)); + EXPECT_GE(data.GetTotalTime(idx_innersplit1), data.GetTotalTime(idx_innerinnersplit1)); } @@ -134,27 +138,24 @@ TEST_F(TimingLoggerTest, ScopedAndExplicit) { const char* innersplit = "Inner Split"; const char* innerinnersplit1 = "Inner Inner Split 1"; const char* innerinnersplit2 = "Inner Inner Split 2"; - TimingLogger timings("Scoped", true, false); - - timings.StartSplit(outersplit); - + TimingLogger logger("Scoped", true, false); + logger.StartTiming(outersplit); { - TimingLogger::ScopedSplit inner(innersplit, &timings); - - timings.StartSplit(innerinnersplit1); - - timings.NewSplit(innerinnersplit2); // Ends innerinnersplit1. + TimingLogger::ScopedTiming inner(innersplit, &logger); + logger.StartTiming(innerinnersplit1); + logger.NewTiming(innerinnersplit2); // Ends innerinnersplit1. + logger.EndTiming(); } // Ends innerinnersplit2, then innersplit. - - timings.EndSplit(); // Ends outersplit. - - const TimingLogger::SplitTimings& splits = timings.GetSplits(); - - EXPECT_EQ(4U, splits.size()); - EXPECT_STREQ(splits[0].second, innerinnersplit1); - EXPECT_STREQ(splits[1].second, innerinnersplit2); - EXPECT_STREQ(splits[2].second, innersplit); - EXPECT_STREQ(splits[3].second, outersplit); + logger.EndTiming(); // Ends outersplit. + const size_t idx_outersplit = logger.FindTimingIndex(outersplit, 0); + const size_t idx_innersplit = logger.FindTimingIndex(innersplit, 0); + const size_t idx_innerinnersplit1 = logger.FindTimingIndex(innerinnersplit1, 0); + const size_t idx_innerinnersplit2 = logger.FindTimingIndex(innerinnersplit2, 0); + const auto& timings = logger.GetTimings(); + EXPECT_EQ(8U, timings.size()); + EXPECT_LE(timings[idx_outersplit].GetTime(), timings[idx_innersplit].GetTime()); + EXPECT_LE(timings[idx_innersplit].GetTime(), timings[idx_innerinnersplit1].GetTime()); + EXPECT_LE(timings[idx_innerinnersplit1].GetTime(), timings[idx_innerinnersplit2].GetTime()); } } // namespace art diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc index 6d5b59cbeb..f29a7ec974 100644 --- a/runtime/base/unix_file/fd_file.cc +++ b/runtime/base/unix_file/fd_file.cc @@ -122,7 +122,9 @@ bool FdFile::ReadFully(void* buffer, size_t byte_count) { char* ptr = static_cast<char*>(buffer); while (byte_count > 0) { ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd_, ptr, byte_count)); - if (bytes_read == -1) { + if (bytes_read <= 0) { + // 0: end of file + // -1: error return false; } byte_count -= bytes_read; // Reduce the number of remaining bytes. diff --git a/runtime/base/unix_file/fd_file_test.cc b/runtime/base/unix_file/fd_file_test.cc index d620666747..33b3d3e186 100644 --- a/runtime/base/unix_file/fd_file_test.cc +++ b/runtime/base/unix_file/fd_file_test.cc @@ -16,6 +16,7 @@ #include "base/unix_file/fd_file.h" #include "base/unix_file/random_access_file_test.h" +#include "common_runtime_test.h" // For ScratchFile #include "gtest/gtest.h" namespace unix_file { @@ -60,4 +61,15 @@ TEST_F(FdFileTest, OpenClose) { EXPECT_TRUE(file.IsOpened()); } +TEST_F(FdFileTest, ReadFullyEmptyFile) { + // New scratch file, zero-length. + art::ScratchFile tmp; + FdFile file; + ASSERT_TRUE(file.Open(tmp.GetFilename(), O_RDONLY)); + EXPECT_GE(file.Fd(), 0); + EXPECT_TRUE(file.IsOpened()); + uint8_t buffer[16]; + EXPECT_FALSE(file.ReadFully(&buffer, 4)); +} + } // namespace unix_file diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index a81648958b..fefb907422 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -1757,9 +1757,7 @@ PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double, 'D'); CHECK_JNI_ENTRY(kFlag_Default, "EpJ", env, address, capacity); if (address == nullptr) { JniAbortF(__FUNCTION__, "non-nullable address is NULL"); - } - if (capacity < 0) { - JniAbortF(__FUNCTION__, "capacity must be non-negative: %" PRId64, capacity); + return nullptr; } return CHECK_JNI_EXIT("L", baseEnv(env)->NewDirectByteBuffer(env, address, capacity)); } diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index f745088dd6..a40a2e4cd2 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_CLASS_LINKER_INL_H_ #include "class_linker.h" +#include "gc/heap-inl.h" #include "mirror/art_field.h" #include "mirror/class_loader.h" #include "mirror/dex_cache-inl.h" @@ -40,7 +41,8 @@ inline mirror::Class* ClassLinker::FindSystemClass(Thread* self, const char* des inline mirror::Class* ClassLinker::FindArrayClass(Thread* self, mirror::Class** element_class) { for (size_t i = 0; i < kFindArrayCacheSize; ++i) { // Read the cached array class once to avoid races with other threads setting it. - mirror::Class* array_class = find_array_class_cache_[i]; + mirror::Class* array_class = ReadBarrier::BarrierForRoot<mirror::Class, kWithReadBarrier>( + &find_array_class_cache_[i]); if (array_class != nullptr && array_class->GetComponentType() == *element_class) { return array_class; } @@ -186,23 +188,38 @@ inline mirror::ObjectArray<mirror::ArtMethod>* ClassLinker::AllocArtMethodArray( inline mirror::IfTable* ClassLinker::AllocIfTable(Thread* self, size_t ifcount) { return down_cast<mirror::IfTable*>( - mirror::IfTable::Alloc(self, GetClassRoot(kObjectArrayClass), ifcount * mirror::IfTable::kMax)); + mirror::IfTable::Alloc(self, GetClassRoot(kObjectArrayClass), + ifcount * mirror::IfTable::kMax)); } inline mirror::ObjectArray<mirror::ArtField>* ClassLinker::AllocArtFieldArray(Thread* self, size_t length) { + gc::Heap* const heap = Runtime::Current()->GetHeap(); + // Can't have movable field arrays for mark compact since we need these arrays to always be valid + // so that we can do Object::VisitReferences in the case where the fields don't fit in the + // reference offsets word. return mirror::ObjectArray<mirror::ArtField>::Alloc( - self, GetClassRoot(kJavaLangReflectArtFieldArrayClass), length); + self, GetClassRoot(kJavaLangReflectArtFieldArrayClass), length, + kMoveFieldArrays ? heap->GetCurrentAllocator() : heap->GetCurrentNonMovingAllocator()); } inline mirror::Class* ClassLinker::GetClassRoot(ClassRoot class_root) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(class_roots_ != NULL); - mirror::Class* klass = class_roots_->Get(class_root); + mirror::ObjectArray<mirror::Class>* class_roots = + ReadBarrier::BarrierForRoot<mirror::ObjectArray<mirror::Class>, kWithReadBarrier>( + &class_roots_); + mirror::Class* klass = class_roots->Get(class_root); DCHECK(klass != NULL); return klass; } +inline mirror::DexCache* ClassLinker::GetDexCache(size_t idx) { + dex_lock_.AssertSharedHeld(Thread::Current()); + DCHECK(idx < dex_caches_.size()); + return ReadBarrier::BarrierForRoot<mirror::DexCache, kWithReadBarrier>(&dex_caches_[idx]); +} + } // namespace art #endif // ART_RUNTIME_CLASS_LINKER_INL_H_ diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index d684a50731..60453c3cc1 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -408,8 +408,12 @@ void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class CHECK(java_io_Serializable != NULL); // 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_->SetInterface(0, java_lang_Cloneable); - array_iftable_->SetInterface(1, java_io_Serializable); + { + mirror::IfTable* array_iftable = + ReadBarrier::BarrierForRoot<mirror::IfTable, kWithReadBarrier>(&array_iftable_); + array_iftable->SetInterface(0, java_lang_Cloneable); + array_iftable->SetInterface(1, java_io_Serializable); + } // Sanity check Class[] and Object[]'s interfaces. CHECK_EQ(java_lang_Cloneable, mirror::Class::GetDirectInterface(self, class_array_class, 0)); @@ -630,15 +634,22 @@ OatFile& ClassLinker::GetImageOatFile(gc::space::ImageSpace* space) { const OatFile* ClassLinker::FindOpenedOatFileForDexFile(const DexFile& dex_file) { const char* dex_location = dex_file.GetLocation().c_str(); uint32_t dex_location_checksum = dex_file.GetLocationChecksum(); - return FindOpenedOatFileFromDexLocation(dex_location, &dex_location_checksum); + return FindOpenedOatFile(nullptr, dex_location, &dex_location_checksum); } -const OatFile* ClassLinker::FindOpenedOatFileFromDexLocation( - const char* dex_location, const uint32_t* const dex_location_checksum) { +const OatFile* ClassLinker::FindOpenedOatFile(const char* oat_location, const char* dex_location, + const uint32_t* const dex_location_checksum) { ReaderMutexLock mu(Thread::Current(), dex_lock_); for (size_t i = 0; i < oat_files_.size(); i++) { const OatFile* oat_file = oat_files_[i]; DCHECK(oat_file != NULL); + + if (oat_location != nullptr) { + if (oat_file->GetLocation() != oat_location) { + continue; + } + } + const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, dex_location_checksum, false); @@ -649,10 +660,229 @@ const OatFile* ClassLinker::FindOpenedOatFileFromDexLocation( return NULL; } -const DexFile* ClassLinker::FindDexFileInOatLocation(const char* dex_location, - uint32_t dex_location_checksum, - const char* oat_location, - std::string* error_msg) { +static std::string GetMultiDexClassesDexName(size_t number, const char* dex_location) { + if (number == 0) { + return dex_location; + } else { + return StringPrintf("%s" kMultiDexSeparatorString "classes%zu.dex", dex_location, number + 1); + } +} + +static bool LoadMultiDexFilesFromOatFile(const OatFile* oat_file, const char* dex_location, + bool generated, + std::vector<std::string>* error_msgs, + std::vector<const DexFile*>* dex_files) { + if (oat_file == nullptr) { + return false; + } + + size_t old_size = dex_files->size(); // To rollback on error. + + bool success = true; + for (size_t i = 0; success; ++i) { + std::string next_name_str = GetMultiDexClassesDexName(i, dex_location); + const char* next_name = next_name_str.c_str(); + + uint32_t dex_location_checksum; + uint32_t* dex_location_checksum_pointer = &dex_location_checksum; + std::string error_msg; + if (!DexFile::GetChecksum(next_name, dex_location_checksum_pointer, &error_msg)) { + DCHECK_EQ(false, i == 0 && generated); + dex_location_checksum_pointer = nullptr; + } + + const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(next_name, nullptr, false); + + if (oat_dex_file == nullptr) { + if (i == 0 && generated) { + std::string error_msg; + error_msg = StringPrintf("\nFailed to find dex file '%s' (checksum 0x%x) in generated out " + " file'%s'", dex_location, dex_location_checksum, + oat_file->GetLocation().c_str()); + error_msgs->push_back(error_msg); + } + break; // Not found, done. + } + + // Checksum test. Test must succeed when generated. + success = !generated; + if (dex_location_checksum_pointer != nullptr) { + success = dex_location_checksum == oat_dex_file->GetDexFileLocationChecksum(); + } + + if (success) { + const DexFile* dex_file = oat_dex_file->OpenDexFile(&error_msg); + if (dex_file == nullptr) { + success = false; + error_msgs->push_back(error_msg); + } else { + dex_files->push_back(dex_file); + } + } + + // When we generated the file, we expect success, or something is terribly wrong. + CHECK_EQ(false, generated && !success) + << "dex_location=" << next_name << " oat_location=" << oat_file->GetLocation().c_str() + << std::hex << " dex_location_checksum=" << dex_location_checksum + << " OatDexFile::GetLocationChecksum()=" << oat_dex_file->GetDexFileLocationChecksum(); + } + + if (dex_files->size() == old_size) { + success = false; // We did not even find classes.dex + } + + if (success) { + return true; + } else { + // Free all the dex files we have loaded. + auto it = dex_files->begin() + old_size; + auto it_end = dex_files->end(); + for (; it != it_end; it++) { + delete *it; + } + dex_files->erase(dex_files->begin() + old_size, it_end); + + return false; + } +} + +// Multidex files make it possible that some, but not all, dex files can be broken/outdated. This +// complicates the loading process, as we should not use an iterative loading process, because that +// would register the oat file and dex files that come before the broken one. Instead, check all +// multidex ahead of time. +bool ClassLinker::OpenDexFilesFromOat(const char* dex_location, const char* oat_location, + std::vector<std::string>* error_msgs, + std::vector<const DexFile*>* dex_files) { + // 1) Check whether we have an open oat file. + // This requires a dex checksum, use the "primary" one. + uint32_t dex_location_checksum; + uint32_t* dex_location_checksum_pointer = &dex_location_checksum; + bool have_checksum = true; + std::string checksum_error_msg; + if (!DexFile::GetChecksum(dex_location, dex_location_checksum_pointer, &checksum_error_msg)) { + dex_location_checksum_pointer = nullptr; + have_checksum = false; + } + + bool needs_registering = false; + + std::unique_ptr<const OatFile> open_oat_file(FindOpenedOatFile(oat_location, dex_location, + dex_location_checksum_pointer)); + + // 2) If we do not have an open one, maybe there's one on disk already. + + // In case the oat file is not open, we play a locking game here so + // that if two different processes race to load and register or generate + // (or worse, one tries to open a partial generated file) we will be okay. + // This is actually common with apps that use DexClassLoader to work + // around the dex method reference limit and that have a background + // service running in a separate process. + ScopedFlock scoped_flock; + + if (open_oat_file.get() == nullptr) { + if (oat_location != nullptr) { + // Can only do this if we have a checksum, else error. + if (!have_checksum) { + error_msgs->push_back(checksum_error_msg); + return false; + } + + std::string error_msg; + + // We are loading or creating one in the future. Time to set up the file lock. + if (!scoped_flock.Init(oat_location, &error_msg)) { + error_msgs->push_back(error_msg); + return false; + } + + open_oat_file.reset(FindOatFileInOatLocationForDexFile(dex_location, dex_location_checksum, + oat_location, &error_msg)); + + if (open_oat_file.get() == nullptr) { + std::string compound_msg = StringPrintf("Failed to find dex file '%s' in oat location '%s': %s", + dex_location, oat_location, error_msg.c_str()); + VLOG(class_linker) << compound_msg; + error_msgs->push_back(compound_msg); + } + } else { + // TODO: What to lock here? + open_oat_file.reset(FindOatFileContainingDexFileFromDexLocation(dex_location, + dex_location_checksum_pointer, + kRuntimeISA, error_msgs)); + } + needs_registering = true; + } + + // 3) If we have an oat file, check all contained multidex files for our dex_location. + // Note: LoadMultiDexFilesFromOatFile will check for nullptr in the first argument. + bool success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location, false, error_msgs, + dex_files); + if (success) { + const OatFile* oat_file = open_oat_file.release(); // Avoid deleting it. + if (needs_registering) { + // We opened the oat file, so we must register it. + RegisterOatFile(oat_file); + } + return true; + } else { + if (needs_registering) { + // We opened it, delete it. + open_oat_file.reset(); + } else { + open_oat_file.release(); // Do not delete open oat files. + } + } + + // 4) If it's not the case (either no oat file or mismatches), regenerate and load. + + // Need a checksum, fail else. + if (!have_checksum) { + error_msgs->push_back(checksum_error_msg); + return false; + } + + // Look in cache location if no oat_location is given. + std::string cache_location; + if (oat_location == nullptr) { + // Use the dalvik cache. + const std::string dalvik_cache(GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA))); + cache_location = GetDalvikCacheFilenameOrDie(dex_location, dalvik_cache.c_str()); + oat_location = cache_location.c_str(); + } + + // Definitely need to lock now. + if (!scoped_flock.HasFile()) { + std::string error_msg; + if (!scoped_flock.Init(oat_location, &error_msg)) { + error_msgs->push_back(error_msg); + return false; + } + } + + // Create the oat file. + open_oat_file.reset(CreateOatFileForDexLocation(dex_location, scoped_flock.GetFile()->Fd(), + oat_location, error_msgs)); + + // Failed, bail. + if (open_oat_file.get() == nullptr) { + return false; + } + + // Try to load again, but stronger checks. + success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location, true, error_msgs, + dex_files); + if (success) { + RegisterOatFile(open_oat_file.release()); + return true; + } else { + return false; + } +} + +const OatFile* ClassLinker::FindOatFileInOatLocationForDexFile(const char* dex_location, + uint32_t dex_location_checksum, + const char* oat_location, + std::string* error_msg) { std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, NULL, !Runtime::Current()->IsCompiler(), error_msg)); @@ -695,44 +925,21 @@ const DexFile* ClassLinker::FindDexFileInOatLocation(const char* dex_location, actual_dex_checksum); return nullptr; } - const DexFile* dex_file = oat_dex_file->OpenDexFile(error_msg); - if (dex_file != nullptr) { - RegisterOatFile(oat_file.release()); - } - return dex_file; -} - -const DexFile* ClassLinker::FindOrCreateOatFileForDexLocation( - const char* dex_location, - uint32_t dex_location_checksum, - const char* oat_location, - std::vector<std::string>* error_msgs) { - // We play a locking game here so that if two different processes - // race to generate (or worse, one tries to open a partial generated - // file) we will be okay. This is actually common with apps that use - // DexClassLoader to work around the dex method reference limit and - // that have a background service running in a separate process. - ScopedFlock scoped_flock; - std::string error_msg; - if (!scoped_flock.Init(oat_location, &error_msg)) { - error_msgs->push_back(error_msg); + std::unique_ptr<const DexFile> dex_file(oat_dex_file->OpenDexFile(error_msg)); + if (dex_file.get() != nullptr) { + return oat_file.release(); + } else { return nullptr; } +} - // Check if we already have an up-to-date output file - const DexFile* dex_file = FindDexFileInOatLocation(dex_location, dex_location_checksum, - oat_location, &error_msg); - if (dex_file != nullptr) { - return dex_file; - } - std::string compound_msg = StringPrintf("Failed to find dex file '%s' in oat location '%s': %s", - dex_location, oat_location, error_msg.c_str()); - VLOG(class_linker) << compound_msg; - error_msgs->push_back(compound_msg); - +const OatFile* ClassLinker::CreateOatFileForDexLocation(const char* dex_location, + int fd, const char* oat_location, + std::vector<std::string>* error_msgs) { // Generate the output oat file for the dex file VLOG(class_linker) << "Generating oat file " << oat_location << " for " << dex_location; - if (!GenerateOatFile(dex_location, scoped_flock.GetFile()->Fd(), oat_location, &error_msg)) { + std::string error_msg; + if (!GenerateOatFile(dex_location, fd, oat_location, &error_msg)) { CHECK(!error_msg.empty()); error_msgs->push_back(error_msg); return nullptr; @@ -741,27 +948,13 @@ const DexFile* ClassLinker::FindOrCreateOatFileForDexLocation( !Runtime::Current()->IsCompiler(), &error_msg)); if (oat_file.get() == nullptr) { - compound_msg = StringPrintf("\nFailed to open generated oat file '%s': %s", - oat_location, error_msg.c_str()); + std::string compound_msg = StringPrintf("\nFailed to open generated oat file '%s': %s", + oat_location, error_msg.c_str()); error_msgs->push_back(compound_msg); return nullptr; } - const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, - &dex_location_checksum); - if (oat_dex_file == nullptr) { - error_msg = StringPrintf("\nFailed to find dex file '%s' (checksum 0x%x) in generated out file " - "'%s'", dex_location, dex_location_checksum, oat_location); - error_msgs->push_back(error_msg); - return nullptr; - } - const DexFile* result = oat_dex_file->OpenDexFile(&error_msg); - CHECK(result != nullptr) << error_msgs << ", " << error_msg; - CHECK_EQ(dex_location_checksum, result->GetLocationChecksum()) - << "dex_location=" << dex_location << " oat_location=" << oat_location << std::hex - << " dex_location_checksum=" << dex_location_checksum - << " DexFile::GetLocationChecksum()=" << result->GetLocationChecksum(); - RegisterOatFile(oat_file.release()); - return result; + + return oat_file.release(); } bool ClassLinker::VerifyOatFileChecksums(const OatFile* oat_file, @@ -828,17 +1021,17 @@ bool ClassLinker::VerifyOatFileChecksums(const OatFile* oat_file, return false; } -const DexFile* ClassLinker::VerifyAndOpenDexFileFromOatFile(const std::string& oat_file_location, - const char* dex_location, - std::string* error_msg, - bool* open_failed) { +const OatFile* ClassLinker::LoadOatFileAndVerifyDexFile(const std::string& oat_file_location, + const char* dex_location, + std::string* error_msg, + bool* open_failed) { std::unique_ptr<const OatFile> oat_file(FindOatFileFromOatLocation(oat_file_location, error_msg)); if (oat_file.get() == nullptr) { *open_failed = true; return nullptr; } *open_failed = false; - const DexFile* dex_file = nullptr; + std::unique_ptr<const DexFile> dex_file; uint32_t dex_location_checksum; if (!DexFile::GetChecksum(dex_location, &dex_location_checksum, error_msg)) { // If no classes.dex found in dex_location, it has been stripped or is corrupt, assume oat is @@ -851,49 +1044,38 @@ const DexFile* ClassLinker::VerifyAndOpenDexFileFromOatFile(const std::string& o error_msg->c_str()); return nullptr; } - dex_file = oat_dex_file->OpenDexFile(error_msg); + dex_file.reset(oat_dex_file->OpenDexFile(error_msg)); } else { bool verified = VerifyOatFileChecksums(oat_file.get(), dex_location, dex_location_checksum, kRuntimeISA, error_msg); if (!verified) { return nullptr; } - dex_file = oat_file->GetOatDexFile(dex_location, - &dex_location_checksum)->OpenDexFile(error_msg); + dex_file.reset(oat_file->GetOatDexFile(dex_location, + &dex_location_checksum)->OpenDexFile(error_msg)); } - if (dex_file != nullptr) { - RegisterOatFile(oat_file.release()); + + if (dex_file.get() != nullptr) { + return oat_file.release(); + } else { + return nullptr; } - return dex_file; } -const DexFile* ClassLinker::FindDexFileInOatFileFromDexLocation( +const OatFile* ClassLinker::FindOatFileContainingDexFileFromDexLocation( const char* dex_location, const uint32_t* const dex_location_checksum, InstructionSet isa, std::vector<std::string>* error_msgs) { - const OatFile* open_oat_file = FindOpenedOatFileFromDexLocation(dex_location, - dex_location_checksum); - if (open_oat_file != nullptr) { - const OatFile::OatDexFile* oat_dex_file = open_oat_file->GetOatDexFile(dex_location, - dex_location_checksum); - std::string error_msg; - const DexFile* ret = oat_dex_file->OpenDexFile(&error_msg); - if (ret == nullptr) { - error_msgs->push_back(error_msg); - } - return ret; - } - // Look for an existing file next to dex. for example, for // /foo/bar/baz.jar, look for /foo/bar/<isa>/baz.odex. std::string odex_filename(DexFilenameToOdexFilename(dex_location, isa)); bool open_failed; std::string error_msg; - const DexFile* dex_file = VerifyAndOpenDexFileFromOatFile(odex_filename, dex_location, - &error_msg, &open_failed); - if (dex_file != nullptr) { - return dex_file; + const OatFile* oat_file = LoadOatFileAndVerifyDexFile(odex_filename, dex_location, &error_msg, + &open_failed); + if (oat_file != nullptr) { + return oat_file; } if (dex_location_checksum == nullptr) { error_msgs->push_back(StringPrintf("Failed to open oat file from %s and no classes.dex found in" @@ -906,10 +1088,10 @@ const DexFile* ClassLinker::FindDexFileInOatFileFromDexLocation( const std::string dalvik_cache(GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA))); std::string cache_location(GetDalvikCacheFilenameOrDie(dex_location, dalvik_cache.c_str())); - dex_file = VerifyAndOpenDexFileFromOatFile(cache_location, dex_location, &cache_error_msg, - &open_failed); - if (dex_file != nullptr) { - return dex_file; + oat_file = LoadOatFileAndVerifyDexFile(cache_location, dex_location, &cache_error_msg, + &open_failed); + if (oat_file != nullptr) { + return oat_file; } if (!open_failed && TEMP_FAILURE_RETRY(unlink(cache_location.c_str())) != 0) { PLOG(FATAL) << "Failed to remove obsolete oat file from " << cache_location; @@ -920,9 +1102,7 @@ const DexFile* ClassLinker::FindDexFileInOatFileFromDexLocation( VLOG(class_linker) << compound_msg; error_msgs->push_back(compound_msg); - // Try to generate oat file if it wasn't found or was obsolete. - return FindOrCreateOatFileForDexLocation(dex_location, *dex_location_checksum, - cache_location.c_str(), error_msgs); + return nullptr; } const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& oat_location) { @@ -1061,6 +1241,42 @@ void ClassLinker::InitFromImage() { VLOG(startup) << "ClassLinker::InitFromImage exiting"; } +void ClassLinker::VisitClassRoots(RootCallback* callback, void* arg, VisitRootFlags flags) { + WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + if ((flags & kVisitRootFlagAllRoots) != 0) { + for (std::pair<const size_t, mirror::Class*>& it : class_table_) { + callback(reinterpret_cast<mirror::Object**>(&it.second), arg, 0, kRootStickyClass); + } + } else if ((flags & kVisitRootFlagNewRoots) != 0) { + for (auto& pair : new_class_roots_) { + mirror::Object* old_ref = pair.second; + callback(reinterpret_cast<mirror::Object**>(&pair.second), arg, 0, kRootStickyClass); + if (UNLIKELY(pair.second != old_ref)) { + // Uh ohes, GC moved a root in the log. Need to search the class_table and update the + // corresponding object. This is slow, but luckily for us, this may only happen with a + // concurrent moving GC. + for (auto it = class_table_.lower_bound(pair.first), end = class_table_.end(); + it != end && it->first == pair.first; ++it) { + // If the class stored matches the old class, update it to the new value. + if (old_ref == it->second) { + it->second = pair.second; + } + } + } + } + } + if ((flags & kVisitRootFlagClearRootLog) != 0) { + new_class_roots_.clear(); + } + if ((flags & kVisitRootFlagStartLoggingNewRoots) != 0) { + log_new_class_table_roots_ = true; + } else if ((flags & kVisitRootFlagStopLoggingNewRoots) != 0) { + log_new_class_table_roots_ = false; + } + // We deliberately ignore the class roots in the image since we + // handle image roots by using the MS/CMS rescanning of dirty cards. +} + // Keep in sync with InitCallback. Anything we visit, we need to // reinit references to when reinitializing a ClassLinker from a // mapped image. @@ -1087,41 +1303,7 @@ void ClassLinker::VisitRoots(RootCallback* callback, void* arg, VisitRootFlags f log_new_dex_caches_roots_ = false; } } - { - WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); - if ((flags & kVisitRootFlagAllRoots) != 0) { - for (std::pair<const size_t, mirror::Class*>& it : class_table_) { - callback(reinterpret_cast<mirror::Object**>(&it.second), arg, 0, kRootStickyClass); - } - } else if ((flags & kVisitRootFlagNewRoots) != 0) { - for (auto& pair : new_class_roots_) { - mirror::Object* old_ref = pair.second; - callback(reinterpret_cast<mirror::Object**>(&pair.second), arg, 0, kRootStickyClass); - if (UNLIKELY(pair.second != old_ref)) { - // Uh ohes, GC moved a root in the log. Need to search the class_table and update the - // corresponding object. This is slow, but luckily for us, this may only happen with a - // concurrent moving GC. - for (auto it = class_table_.lower_bound(pair.first), end = class_table_.end(); - it != end && it->first == pair.first; ++it) { - // If the class stored matches the old class, update it to the new value. - if (old_ref == it->second) { - it->second = pair.second; - } - } - } - } - } - if ((flags & kVisitRootFlagClearRootLog) != 0) { - new_class_roots_.clear(); - } - if ((flags & kVisitRootFlagStartLoggingNewRoots) != 0) { - log_new_class_table_roots_ = true; - } else if ((flags & kVisitRootFlagStopLoggingNewRoots) != 0) { - log_new_class_table_roots_ = false; - } - // We deliberately ignore the class roots in the image since we - // handle image roots by using the MS/CMS rescanning of dirty cards. - } + VisitClassRoots(callback, arg, flags); callback(reinterpret_cast<mirror::Object**>(&array_iftable_), arg, 0, kRootVMInternal); DCHECK(array_iftable_ != nullptr); for (size_t i = 0; i < kFindArrayCacheSize; ++i) { @@ -1252,7 +1434,7 @@ mirror::Class* ClassLinker::AllocClass(Thread* self, mirror::Class* java_lang_Cl DCHECK_GE(class_size, sizeof(mirror::Class)); gc::Heap* heap = Runtime::Current()->GetHeap(); InitializeClassVisitor visitor(class_size); - mirror::Object* k = (kMovingClasses) ? + mirror::Object* k = kMovingClasses ? heap->AllocObject<true>(self, java_lang_Class, class_size, visitor) : heap->AllocNonMovableObject<true>(self, java_lang_Class, class_size, visitor); if (UNLIKELY(k == nullptr)) { @@ -2034,17 +2216,18 @@ void ClassLinker::AppendToBootClassPath(const DexFile& dex_file, RegisterDexFile(dex_file, dex_cache); } -bool ClassLinker::IsDexFileRegisteredLocked(const DexFile& dex_file) const { +bool ClassLinker::IsDexFileRegisteredLocked(const DexFile& dex_file) { dex_lock_.AssertSharedHeld(Thread::Current()); for (size_t i = 0; i != dex_caches_.size(); ++i) { - if (dex_caches_[i]->GetDexFile() == &dex_file) { + mirror::DexCache* dex_cache = GetDexCache(i); + if (dex_cache->GetDexFile() == &dex_file) { return true; } } return false; } -bool ClassLinker::IsDexFileRegistered(const DexFile& dex_file) const { +bool ClassLinker::IsDexFileRegistered(const DexFile& dex_file) { ReaderMutexLock mu(Thread::Current(), dex_lock_); return IsDexFileRegisteredLocked(dex_file); } @@ -2092,11 +2275,11 @@ void ClassLinker::RegisterDexFile(const DexFile& dex_file, RegisterDexFileLocked(dex_file, dex_cache); } -mirror::DexCache* ClassLinker::FindDexCache(const DexFile& dex_file) const { +mirror::DexCache* ClassLinker::FindDexCache(const DexFile& dex_file) { ReaderMutexLock mu(Thread::Current(), dex_lock_); // Search assuming unique-ness of dex file. for (size_t i = 0; i != dex_caches_.size(); ++i) { - mirror::DexCache* dex_cache = dex_caches_[i]; + mirror::DexCache* dex_cache = GetDexCache(i); if (dex_cache->GetDexFile() == &dex_file) { return dex_cache; } @@ -2104,24 +2287,25 @@ mirror::DexCache* ClassLinker::FindDexCache(const DexFile& dex_file) const { // Search matching by location name. std::string location(dex_file.GetLocation()); for (size_t i = 0; i != dex_caches_.size(); ++i) { - mirror::DexCache* dex_cache = dex_caches_[i]; + mirror::DexCache* dex_cache = GetDexCache(i); if (dex_cache->GetDexFile()->GetLocation() == location) { return dex_cache; } } // Failure, dump diagnostic and abort. for (size_t i = 0; i != dex_caches_.size(); ++i) { - mirror::DexCache* dex_cache = dex_caches_[i]; + mirror::DexCache* dex_cache = GetDexCache(i); LOG(ERROR) << "Registered dex file " << i << " = " << dex_cache->GetDexFile()->GetLocation(); } LOG(FATAL) << "Failed to find DexCache for DexFile " << location; return NULL; } -void ClassLinker::FixupDexCaches(mirror::ArtMethod* resolution_method) const { +void ClassLinker::FixupDexCaches(mirror::ArtMethod* resolution_method) { ReaderMutexLock mu(Thread::Current(), dex_lock_); for (size_t i = 0; i != dex_caches_.size(); ++i) { - dex_caches_[i]->Fixup(resolution_method); + mirror::DexCache* dex_cache = GetDexCache(i); + dex_cache->Fixup(resolution_method); } } @@ -2261,8 +2445,12 @@ 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). - CHECK(array_iftable_ != nullptr); - new_class->SetIfTable(array_iftable_); + { + mirror::IfTable* array_iftable = + ReadBarrier::BarrierForRoot<mirror::IfTable, kWithReadBarrier>(&array_iftable_); + CHECK(array_iftable != nullptr); + new_class->SetIfTable(array_iftable); + } // Inherit access flags from the component type. int access_flags = new_class->GetComponentType()->GetAccessFlags(); @@ -2929,8 +3117,9 @@ mirror::ArtMethod* ClassLinker::FindMethodForProxy(mirror::Class* proxy_class, mirror::ObjectArray<mirror::Class>* resolved_types = proxy_method->GetDexCacheResolvedTypes(); ReaderMutexLock mu(Thread::Current(), dex_lock_); for (size_t i = 0; i != dex_caches_.size(); ++i) { - if (dex_caches_[i]->GetResolvedTypes() == resolved_types) { - dex_cache = dex_caches_[i]; + mirror::DexCache* a_dex_cache = GetDexCache(i); + if (a_dex_cache->GetResolvedTypes() == resolved_types) { + dex_cache = a_dex_cache; break; } } @@ -4410,9 +4599,12 @@ void ClassLinker::SetClassRoot(ClassRoot class_root, mirror::Class* klass) { DCHECK(klass != NULL); DCHECK(klass->GetClassLoader() == NULL); - DCHECK(class_roots_ != NULL); - DCHECK(class_roots_->Get(class_root) == NULL); - class_roots_->Set<false>(class_root, klass); + mirror::ObjectArray<mirror::Class>* class_roots = + ReadBarrier::BarrierForRoot<mirror::ObjectArray<mirror::Class>, kWithReadBarrier>( + &class_roots_); + DCHECK(class_roots != NULL); + DCHECK(class_roots->Get(class_root) == NULL); + class_roots->Set<false>(class_root, klass); } } // namespace art diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 6d96aa2637..60dad7b930 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -28,6 +28,7 @@ #include "jni.h" #include "oat_file.h" #include "object_callbacks.h" +#include "read_barrier.h" namespace art { namespace gc { @@ -247,15 +248,17 @@ class ClassLinker { LOCKS_EXCLUDED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void VisitClassRoots(RootCallback* callback, void* arg, VisitRootFlags flags) + LOCKS_EXCLUDED(Locks::classlinker_classes_lock_); void VisitRoots(RootCallback* callback, void* arg, VisitRootFlags flags) - LOCKS_EXCLUDED(Locks::classlinker_classes_lock_, dex_lock_); + LOCKS_EXCLUDED(dex_lock_); - mirror::DexCache* FindDexCache(const DexFile& dex_file) const + mirror::DexCache* FindDexCache(const DexFile& dex_file) LOCKS_EXCLUDED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool IsDexFileRegistered(const DexFile& dex_file) const + bool IsDexFileRegistered(const DexFile& dex_file) LOCKS_EXCLUDED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void FixupDexCaches(mirror::ArtMethod* resolution_method) const + void FixupDexCaches(mirror::ArtMethod* resolution_method) LOCKS_EXCLUDED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -270,24 +273,13 @@ class ClassLinker { std::string* error_msg) LOCKS_EXCLUDED(dex_lock_); - // Finds the oat file for a dex location, generating the oat file if - // it is missing or out of date. Returns the DexFile from within the - // created oat file. - const DexFile* FindOrCreateOatFileForDexLocation(const char* dex_location, - uint32_t dex_location_checksum, - const char* oat_location, - std::vector<std::string>* error_msgs) - LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_); - // Find a DexFile within an OatFile given a DexFile location. Note - // that this returns null if the location checksum of the DexFile - // does not match the OatFile. - const DexFile* FindDexFileInOatFileFromDexLocation(const char* location, - const uint32_t* const location_checksum, - InstructionSet isa, - std::vector<std::string>* error_msgs) + // Find or create the oat file holding dex_location. Then load all corresponding dex files + // (if multidex) into the given vector. + bool OpenDexFilesFromOat(const char* dex_location, const char* oat_location, + std::vector<std::string>* error_msgs, + std::vector<const DexFile*>* dex_files) LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_); - // Returns true if oat file contains the dex file with the given location and checksum. static bool VerifyOatFileChecksums(const OatFile* oat_file, const char* dex_location, @@ -468,7 +460,7 @@ class ClassLinker { void RegisterDexFileLocked(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache) EXCLUSIVE_LOCKS_REQUIRED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool IsDexFileRegisteredLocked(const DexFile& dex_file) const + bool IsDexFileRegisteredLocked(const DexFile& dex_file) SHARED_LOCKS_REQUIRED(dex_lock_, Locks::mutator_lock_); bool InitializeClass(Handle<mirror::Class> klass, bool can_run_clinit, @@ -530,28 +522,59 @@ class ClassLinker { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // For use by ImageWriter to find DexCaches for its roots - const std::vector<mirror::DexCache*>& GetDexCaches() { - return dex_caches_; + ReaderWriterMutex* DexLock() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCK_RETURNED(dex_lock_) { + return &dex_lock_; } + size_t GetDexCacheCount() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, dex_lock_) { + return dex_caches_.size(); + } + mirror::DexCache* GetDexCache(size_t idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, dex_lock_); const OatFile* FindOpenedOatFileForDexFile(const DexFile& dex_file) LOCKS_EXCLUDED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - const OatFile* FindOpenedOatFileFromDexLocation(const char* dex_location, - const uint32_t* const dex_location_checksum) + + // Find an opened oat file that contains dex_location. If oat_location is not nullptr, the file + // must have that location, else any oat location is accepted. + const OatFile* FindOpenedOatFile(const char* oat_location, const char* dex_location, + const uint32_t* const dex_location_checksum) LOCKS_EXCLUDED(dex_lock_); const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location) LOCKS_EXCLUDED(dex_lock_); - const DexFile* FindDexFileInOatLocation(const char* dex_location, - uint32_t dex_location_checksum, - const char* oat_location, - std::string* error_msg) + + // Note: will not register the oat file. + const OatFile* FindOatFileInOatLocationForDexFile(const char* dex_location, + uint32_t dex_location_checksum, + const char* oat_location, + std::string* error_msg) LOCKS_EXCLUDED(dex_lock_); - const DexFile* VerifyAndOpenDexFileFromOatFile(const std::string& oat_file_location, - const char* dex_location, - std::string* error_msg, - bool* open_failed) + // Creates the oat file from the dex_location to the oat_location. Needs a file descriptor for + // the file to be written, which is assumed to be under a lock. + const OatFile* CreateOatFileForDexLocation(const char* dex_location, + int fd, const char* oat_location, + std::vector<std::string>* error_msgs) + LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_); + + // Finds an OatFile that contains a DexFile for the given a DexFile location. + // + // Note 1: this will not check open oat files, which are assumed to be stale when this is run. + // Note 2: Does not register the oat file. It is the caller's job to register if the file is to + // be kept. + const OatFile* FindOatFileContainingDexFileFromDexLocation(const char* location, + const uint32_t* const location_checksum, + InstructionSet isa, + std::vector<std::string>* error_msgs) + LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_); + + // Find a verify an oat file with the given dex file. Will return nullptr when the oat file + // was not found or the dex file could not be verified. + // Note: Does not register the oat file. + const OatFile* LoadOatFileAndVerifyDexFile(const std::string& oat_file_location, + const char* dex_location, + std::string* error_msg, + bool* open_failed) LOCKS_EXCLUDED(dex_lock_); mirror::ArtMethod* CreateProxyConstructor(Thread* self, Handle<mirror::Class> klass, @@ -641,9 +664,12 @@ class ClassLinker { void SetClassRoot(ClassRoot class_root, mirror::Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - mirror::ObjectArray<mirror::Class>* GetClassRoots() { - DCHECK(class_roots_ != NULL); - return class_roots_; + mirror::ObjectArray<mirror::Class>* GetClassRoots() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::ObjectArray<mirror::Class>* class_roots = + ReadBarrier::BarrierForRoot<mirror::ObjectArray<mirror::Class>, kWithReadBarrier>( + &class_roots_); + DCHECK(class_roots != NULL); + return class_roots; } static const char* class_roots_descriptors_[]; diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index bac212a110..fdbc9c22c3 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -114,32 +114,42 @@ class CommonRuntimeTest : public testing::Test { public: static void SetEnvironmentVariables(std::string& android_data) { if (IsHost()) { - // $ANDROID_ROOT is set on the device, but not on the host. - // We need to set this so that icu4c can find its locale data. - 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); - } + // $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"; + root += "/out/host/linux-x86"; #elif defined(__APPLE__) - root += "/out/host/darwin-x86"; + root += "/out/host/darwin-x86"; #else #error unsupported OS #endif - setenv("ANDROID_ROOT", root.c_str(), 1); + setenv("ANDROID_ROOT", root.c_str(), 1); + } + } setenv("LD_LIBRARY_PATH", ":", 0); // Required by java.lang.System.<clinit>. // Not set by build server, so default if (getenv("ANDROID_HOST_OUT") == nullptr) { - setenv("ANDROID_HOST_OUT", root.c_str(), 1); + setenv("ANDROID_HOST_OUT", getenv("ANDROID_ROOT"), 1); } } @@ -156,6 +166,18 @@ class CommonRuntimeTest : public testing::Test { return !kIsTargetBuild; } + const DexFile* LoadExpectSingleDexFile(const char* location) { + std::vector<const DexFile*> dex_files; + std::string error_msg; + if (!DexFile::Open(location, location, &error_msg, &dex_files)) { + LOG(FATAL) << "Could not open .dex file '" << location << "': " << error_msg << "\n"; + return nullptr; + } else { + CHECK_EQ(1U, dex_files.size()) << "Expected only one dex file in " << location; + return dex_files[0]; + } + } + virtual void SetUp() { SetEnvironmentVariables(android_data_); dalvik_cache_.append(android_data_.c_str()); @@ -164,12 +186,7 @@ class CommonRuntimeTest : public testing::Test { ASSERT_EQ(mkdir_result, 0); std::string error_msg; - java_lang_dex_file_ = DexFile::Open(GetLibCoreDexFileName().c_str(), - GetLibCoreDexFileName().c_str(), &error_msg); - if (java_lang_dex_file_ == nullptr) { - LOG(FATAL) << "Could not open .dex file '" << GetLibCoreDexFileName() << "': " - << error_msg << "\n"; - } + java_lang_dex_file_ = LoadExpectSingleDexFile(GetLibCoreDexFileName().c_str()); boot_class_path_.push_back(java_lang_dex_file_); std::string min_heap_string(StringPrintf("-Xms%zdm", gc::Heap::kDefaultInitialSize / MB)); @@ -233,7 +250,7 @@ class CommonRuntimeTest : public testing::Test { // There's a function to clear the array, but it's not public... typedef void (*IcuCleanupFn)(); void* sym = dlsym(RTLD_DEFAULT, "u_cleanup_" U_ICU_VERSION_SHORT); - CHECK(sym != nullptr); + CHECK(sym != nullptr) << dlerror(); IcuCleanupFn icu_cleanup_fn = reinterpret_cast<IcuCleanupFn>(sym); (*icu_cleanup_fn)(); @@ -264,7 +281,8 @@ class CommonRuntimeTest : public testing::Test { return GetAndroidRoot(); } - const DexFile* OpenTestDexFile(const char* name) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + std::vector<const DexFile*> OpenTestDexFiles(const char* name) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { CHECK(name != nullptr); std::string filename; if (IsHost()) { @@ -273,30 +291,40 @@ class CommonRuntimeTest : public testing::Test { } else { filename += "/data/nativetest/art/"; } - filename += "art-test-dex-"; + filename += "art-gtest-"; filename += name; filename += ".jar"; std::string error_msg; - const DexFile* dex_file = DexFile::Open(filename.c_str(), filename.c_str(), &error_msg); - CHECK(dex_file != nullptr) << "Failed to open '" << filename << "': " << error_msg; - CHECK_EQ(PROT_READ, dex_file->GetPermissions()); - CHECK(dex_file->IsReadOnly()); - opened_dex_files_.push_back(dex_file); - return dex_file; + std::vector<const DexFile*> dex_files; + bool success = DexFile::Open(filename.c_str(), filename.c_str(), &error_msg, &dex_files); + CHECK(success) << "Failed to open '" << filename << "': " << error_msg; + for (const DexFile* dex_file : dex_files) { + CHECK_EQ(PROT_READ, dex_file->GetPermissions()); + CHECK(dex_file->IsReadOnly()); + } + opened_dex_files_.insert(opened_dex_files_.end(), dex_files.begin(), dex_files.end()); + return dex_files; + } + + const DexFile* OpenTestDexFile(const char* name) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + std::vector<const DexFile*> vector = OpenTestDexFiles(name); + EXPECT_EQ(1U, vector.size()); + return vector[0]; } jobject LoadDex(const char* dex_name) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - const DexFile* dex_file = OpenTestDexFile(dex_name); - CHECK(dex_file != nullptr); - class_linker_->RegisterDexFile(*dex_file); - std::vector<const DexFile*> class_path; - class_path.push_back(dex_file); + std::vector<const DexFile*> dex_files = OpenTestDexFiles(dex_name); + CHECK_NE(0U, dex_files.size()); + for (const DexFile* dex_file : dex_files) { + class_linker_->RegisterDexFile(*dex_file); + } ScopedObjectAccessUnchecked soa(Thread::Current()); ScopedLocalRef<jobject> class_loader_local(soa.Env(), soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader)); jobject class_loader = soa.Env()->NewGlobalRef(class_loader_local.get()); soa.Self()->SetClassLoaderOverride(soa.Decode<mirror::ClassLoader*>(class_loader_local.get())); - Runtime::Current()->SetCompileTimeClassPath(class_loader, class_path); + Runtime::Current()->SetCompileTimeClassPath(class_loader, dex_files); return class_loader; } diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 3ff55ab323..e5bc7c8c86 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -87,7 +87,21 @@ static int OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg) { CHECK(checksum != NULL); uint32_t magic; - ScopedFd fd(OpenAndReadMagic(filename, &magic, error_msg)); + + // Strip ":...", which is the location + const char* zip_entry_name = kClassesDex; + const char* file_part = filename; + std::unique_ptr<const char> file_part_ptr; + + + if (IsMultiDexLocation(filename)) { + std::pair<const char*, const char*> pair = SplitMultiDexLocation(filename); + file_part_ptr.reset(pair.first); + file_part = pair.first; + zip_entry_name = pair.second; + } + + ScopedFd fd(OpenAndReadMagic(file_part, &magic, error_msg)); if (fd.get() == -1) { DCHECK(!error_msg->empty()); return false; @@ -95,13 +109,13 @@ bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* if (IsZipMagic(magic)) { std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd.release(), filename, error_msg)); if (zip_archive.get() == NULL) { - *error_msg = StringPrintf("Failed to open zip archive '%s'", filename); + *error_msg = StringPrintf("Failed to open zip archive '%s'", file_part); return false; } - std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(kClassesDex, error_msg)); + std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name, error_msg)); if (zip_entry.get() == NULL) { - *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", filename, - kClassesDex, error_msg->c_str()); + *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", file_part, + zip_entry_name, error_msg->c_str()); return false; } *checksum = zip_entry->GetCrc32(); @@ -119,23 +133,29 @@ bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* return false; } -const DexFile* DexFile::Open(const char* filename, - const char* location, - std::string* error_msg) { +bool DexFile::Open(const char* filename, const char* location, std::string* error_msg, + std::vector<const DexFile*>* dex_files) { uint32_t magic; ScopedFd fd(OpenAndReadMagic(filename, &magic, error_msg)); if (fd.get() == -1) { DCHECK(!error_msg->empty()); - return NULL; + return false; } if (IsZipMagic(magic)) { - return DexFile::OpenZip(fd.release(), location, error_msg); + return DexFile::OpenZip(fd.release(), location, error_msg, dex_files); } if (IsDexMagic(magic)) { - return DexFile::OpenFile(fd.release(), location, true, error_msg); + std::unique_ptr<const DexFile> dex_file(DexFile::OpenFile(fd.release(), location, true, + error_msg)); + if (dex_file.get() != nullptr) { + dex_files->push_back(dex_file.release()); + return true; + } else { + return false; + } } *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename); - return nullptr; + return false; } int DexFile::GetPermissions() const { @@ -217,13 +237,14 @@ const DexFile* DexFile::OpenFile(int fd, const char* location, bool verify, const char* DexFile::kClassesDex = "classes.dex"; -const DexFile* DexFile::OpenZip(int fd, const std::string& location, std::string* error_msg) { +bool DexFile::OpenZip(int fd, const std::string& location, std::string* error_msg, + std::vector<const DexFile*>* dex_files) { std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd, location.c_str(), error_msg)); if (zip_archive.get() == nullptr) { DCHECK(!error_msg->empty()); - return nullptr; + return false; } - return DexFile::Open(*zip_archive, location, error_msg); + return DexFile::OpenFromZip(*zip_archive, location, error_msg, dex_files); } const DexFile* DexFile::OpenMemory(const std::string& location, @@ -238,17 +259,20 @@ const DexFile* DexFile::OpenMemory(const std::string& location, error_msg); } -const DexFile* DexFile::Open(const ZipArchive& zip_archive, const std::string& location, - std::string* error_msg) { +const DexFile* DexFile::Open(const ZipArchive& zip_archive, const char* entry_name, + const std::string& location, std::string* error_msg, + ZipOpenErrorCode* error_code) { CHECK(!location.empty()); - std::unique_ptr<ZipEntry> zip_entry(zip_archive.Find(kClassesDex, error_msg)); + std::unique_ptr<ZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg)); if (zip_entry.get() == NULL) { + *error_code = ZipOpenErrorCode::kEntryNotFound; return nullptr; } - std::unique_ptr<MemMap> map(zip_entry->ExtractToMemMap(location.c_str(), kClassesDex, error_msg)); + std::unique_ptr<MemMap> map(zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg)); if (map.get() == NULL) { - *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", kClassesDex, location.c_str(), + *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(), error_msg->c_str()); + *error_code = ZipOpenErrorCode::kExtractToMemoryError; return nullptr; } std::unique_ptr<const DexFile> dex_file(OpenMemory(location, zip_entry->GetCrc32(), map.release(), @@ -256,20 +280,63 @@ const DexFile* DexFile::Open(const ZipArchive& zip_archive, const std::string& l if (dex_file.get() == nullptr) { *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(), error_msg->c_str()); + *error_code = ZipOpenErrorCode::kDexFileError; return nullptr; } if (!dex_file->DisableWrite()) { *error_msg = StringPrintf("Failed to make dex file '%s' read only", location.c_str()); + *error_code = ZipOpenErrorCode::kMakeReadOnlyError; return nullptr; } CHECK(dex_file->IsReadOnly()) << location; if (!DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(), location.c_str(), error_msg)) { + *error_code = ZipOpenErrorCode::kVerifyError; return nullptr; } + *error_code = ZipOpenErrorCode::kNoError; return dex_file.release(); } +bool DexFile::OpenFromZip(const ZipArchive& zip_archive, const std::string& location, + std::string* error_msg, std::vector<const DexFile*>* dex_files) { + ZipOpenErrorCode error_code; + std::unique_ptr<const DexFile> dex_file(Open(zip_archive, kClassesDex, location, error_msg, + &error_code)); + if (dex_file.get() == nullptr) { + return false; + } else { + // Had at least classes.dex. + dex_files->push_back(dex_file.release()); + + // Now try some more. + size_t i = 2; + + // We could try to avoid std::string allocations by working on a char array directly. As we + // do not expect a lot of iterations, this seems too involved and brittle. + + while (i < 100) { + std::string name = StringPrintf("classes%zu.dex", i); + std::string fake_location = location + ":" + name; + std::unique_ptr<const DexFile> next_dex_file(Open(zip_archive, name.c_str(), fake_location, + error_msg, &error_code)); + if (next_dex_file.get() == nullptr) { + if (error_code != ZipOpenErrorCode::kEntryNotFound) { + LOG(WARNING) << error_msg; + } + break; + } else { + dex_files->push_back(next_dex_file.release()); + } + + i++; + } + + return true; + } +} + + const DexFile* DexFile::OpenMemory(const byte* base, size_t size, const std::string& location, @@ -865,6 +932,25 @@ bool DexFile::LineNumForPcCb(void* raw_context, uint32_t address, uint32_t line_ } } +bool DexFile::IsMultiDexLocation(const char* location) { + return strrchr(location, kMultiDexSeparator) != nullptr; +} + +std::pair<const char*, const char*> DexFile::SplitMultiDexLocation( + const char* location) { + const char* colon_ptr = strrchr(location, kMultiDexSeparator); + + // Check it's synthetic. + CHECK_NE(colon_ptr, static_cast<const char*>(nullptr)); + + size_t colon_index = colon_ptr - location; + char* tmp = new char[colon_index + 1]; + strncpy(tmp, location, colon_index); + tmp[colon_index] = 0; + + return std::make_pair(tmp, colon_ptr + 1); +} + std::ostream& operator<<(std::ostream& os, const DexFile& dex_file) { os << StringPrintf("[DexFile: %s dex-checksum=%08x location-checksum=%08x %p-%p]", dex_file.GetLocation().c_str(), @@ -935,8 +1021,7 @@ void ClassDataItemIterator::ReadClassDataField() { field_.field_idx_delta_ = DecodeUnsignedLeb128(&ptr_pos_); field_.access_flags_ = DecodeUnsignedLeb128(&ptr_pos_); if (last_idx_ != 0 && field_.field_idx_delta_ == 0) { - LOG(WARNING) << "Duplicate field " << PrettyField(GetMemberIndex(), dex_file_) - << " in " << dex_file_.GetLocation(); + LOG(WARNING) << "Duplicate field in " << dex_file_.GetLocation(); } } @@ -945,8 +1030,7 @@ void ClassDataItemIterator::ReadClassDataMethod() { method_.access_flags_ = DecodeUnsignedLeb128(&ptr_pos_); method_.code_off_ = DecodeUnsignedLeb128(&ptr_pos_); if (last_idx_ != 0 && method_.method_idx_delta_ == 0) { - LOG(WARNING) << "Duplicate method " << PrettyMethod(GetMemberIndex(), dex_file_) - << " in " << dex_file_.GetLocation(); + LOG(WARNING) << "Duplicate method in " << dex_file_.GetLocation(); } } diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 8270a2b006..04f1cc1417 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -63,6 +63,13 @@ class DexFile { // The value of an invalid index. static const uint16_t kDexNoIndex16 = 0xFFFF; + // The separator charactor in MultiDex locations. + static constexpr char kMultiDexSeparator = ':'; + + // A string version of the previous. This is a define so that we can merge string literals in the + // preprocessor. + #define kMultiDexSeparatorString ":" + // Raw header_item. struct Header { uint8_t magic_[8]; @@ -352,8 +359,9 @@ class DexFile { // Return true if the checksum could be found, false otherwise. static bool GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg); - // Opens .dex file, guessing the container format based on file extension - static const DexFile* Open(const char* filename, const char* location, std::string* error_msg); + // Opens .dex files found in the container, guessing the container format based on file extension. + static bool Open(const char* filename, const char* location, std::string* error_msg, + std::vector<const DexFile*>* dex_files); // Opens .dex file, backed by existing memory static const DexFile* Open(const uint8_t* base, size_t size, @@ -363,9 +371,9 @@ class DexFile { return OpenMemory(base, size, location, location_checksum, NULL, error_msg); } - // Opens .dex file from the classes.dex in a zip archive - static const DexFile* Open(const ZipArchive& zip_archive, const std::string& location, - std::string* error_msg); + // Open all classesXXX.dex files from a zip archive. + static bool OpenFromZip(const ZipArchive& zip_archive, const std::string& location, + std::string* error_msg, std::vector<const DexFile*>* dex_files); // Closes a .dex file. virtual ~DexFile(); @@ -823,8 +831,24 @@ class DexFile { // Opens a .dex file static const DexFile* OpenFile(int fd, const char* location, bool verify, std::string* error_msg); - // Opens a dex file from within a .jar, .zip, or .apk file - static const DexFile* OpenZip(int fd, const std::string& location, std::string* error_msg); + // Opens dex files from within a .jar, .zip, or .apk file + static bool OpenZip(int fd, const std::string& location, std::string* error_msg, + std::vector<const DexFile*>* dex_files); + + enum class ZipOpenErrorCode { // private + kNoError, + kEntryNotFound, + kExtractToMemoryError, + kDexFileError, + kMakeReadOnlyError, + kVerifyError + }; + + // Opens .dex file from the entry_name in a zip archive. error_code is undefined when non-nullptr + // return. + static const DexFile* Open(const ZipArchive& zip_archive, const char* entry_name, + const std::string& location, std::string* error_msg, + ZipOpenErrorCode* error_code); // Opens a .dex file at the given address backed by a MemMap static const DexFile* OpenMemory(const std::string& location, @@ -855,6 +879,18 @@ class DexFile { DexDebugNewPositionCb position_cb, DexDebugNewLocalCb local_cb, void* context, const byte* stream, LocalInfo* local_in_reg) const; + // Check whether a location denotes a multidex dex file. This is a very simple check: returns + // whether the string contains the separator character. + static bool IsMultiDexLocation(const char* location); + + // Splits a multidex location at the last separator character. The second component is a pointer + // to the character after the separator. The first is a copy of the substring up to the separator. + // + // Note: It's the caller's job to free the first component of the returned pair. + // Bug 15313523: gcc/libc++ don't allow a unique_ptr for the first component + static std::pair<const char*, const char*> SplitMultiDexLocation(const char* location); + + // The base address of the memory mapping. const byte* const begin_; diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index a814c343cd..c1e00fcb0a 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -146,8 +146,11 @@ static const DexFile* OpenDexFileBase64(const char* base64, // read dex file ScopedObjectAccess soa(Thread::Current()); std::string error_msg; - const DexFile* dex_file = DexFile::Open(location, location, &error_msg); - CHECK(dex_file != nullptr) << error_msg; + std::vector<const DexFile*> tmp; + bool success = DexFile::Open(location, location, &error_msg, &tmp); + CHECK(success) << error_msg; + EXPECT_EQ(1U, tmp.size()); + const DexFile* dex_file = tmp[0]; EXPECT_EQ(PROT_READ, dex_file->GetPermissions()); EXPECT_TRUE(dex_file->IsReadOnly()); return dex_file; diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc index d0ce00fc66..93faeaee7c 100644 --- a/runtime/dex_file_verifier_test.cc +++ b/runtime/dex_file_verifier_test.cc @@ -115,7 +115,14 @@ static const DexFile* OpenDexFileBase64(const char* base64, const char* location // read dex file ScopedObjectAccess soa(Thread::Current()); - return DexFile::Open(location, location, error_msg); + std::vector<const DexFile*> tmp; + bool success = DexFile::Open(location, location, error_msg, &tmp); + CHECK(success) << error_msg; + EXPECT_EQ(1U, tmp.size()); + const DexFile* dex_file = tmp[0]; + EXPECT_EQ(PROT_READ, dex_file->GetPermissions()); + EXPECT_TRUE(dex_file->IsReadOnly()); + return dex_file; } @@ -170,7 +177,15 @@ static const DexFile* FixChecksumAndOpen(byte* bytes, size_t length, const char* // read dex file ScopedObjectAccess soa(Thread::Current()); - return DexFile::Open(location, location, error_msg); + std::vector<const DexFile*> tmp; + if (!DexFile::Open(location, location, error_msg, &tmp)) { + return nullptr; + } + EXPECT_EQ(1U, tmp.size()); + const DexFile* dex_file = tmp[0]; + EXPECT_EQ(PROT_READ, dex_file->GetPermissions()); + EXPECT_TRUE(dex_file->IsReadOnly()); + return dex_file; } static bool ModifyAndLoad(const char* location, size_t offset, uint8_t new_val, diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h index edba5020d4..b6810b02b2 100644 --- a/runtime/dex_instruction.h +++ b/runtime/dex_instruction.h @@ -145,28 +145,30 @@ class Instruction { }; enum VerifyFlag { - kVerifyNone = 0x000000, - kVerifyRegA = 0x000001, - kVerifyRegAWide = 0x000002, - kVerifyRegB = 0x000004, - kVerifyRegBField = 0x000008, - kVerifyRegBMethod = 0x000010, - kVerifyRegBNewInstance = 0x000020, - kVerifyRegBString = 0x000040, - kVerifyRegBType = 0x000080, - kVerifyRegBWide = 0x000100, - kVerifyRegC = 0x000200, - kVerifyRegCField = 0x000400, - kVerifyRegCNewArray = 0x000800, - kVerifyRegCType = 0x001000, - kVerifyRegCWide = 0x002000, - kVerifyArrayData = 0x004000, - kVerifyBranchTarget = 0x008000, - kVerifySwitchTargets = 0x010000, - kVerifyVarArg = 0x020000, - kVerifyVarArgRange = 0x040000, - kVerifyRuntimeOnly = 0x080000, - kVerifyError = 0x100000, + kVerifyNone = 0x000000, + kVerifyRegA = 0x000001, + kVerifyRegAWide = 0x000002, + kVerifyRegB = 0x000004, + kVerifyRegBField = 0x000008, + kVerifyRegBMethod = 0x000010, + kVerifyRegBNewInstance = 0x000020, + kVerifyRegBString = 0x000040, + kVerifyRegBType = 0x000080, + kVerifyRegBWide = 0x000100, + kVerifyRegC = 0x000200, + kVerifyRegCField = 0x000400, + kVerifyRegCNewArray = 0x000800, + kVerifyRegCType = 0x001000, + kVerifyRegCWide = 0x002000, + kVerifyArrayData = 0x004000, + kVerifyBranchTarget = 0x008000, + kVerifySwitchTargets = 0x010000, + kVerifyVarArg = 0x020000, + kVerifyVarArgNonZero = 0x040000, + kVerifyVarArgRange = 0x080000, + kVerifyVarArgRangeNonZero = 0x100000, + kVerifyRuntimeOnly = 0x200000, + kVerifyError = 0x400000, }; static constexpr uint32_t kMaxVarArgRegs = 5; @@ -506,7 +508,8 @@ class Instruction { int GetVerifyExtraFlags() const { return (kInstructionVerifyFlags[Opcode()] & (kVerifyArrayData | kVerifyBranchTarget | - kVerifySwitchTargets | kVerifyVarArg | kVerifyVarArgRange | kVerifyError)); + kVerifySwitchTargets | kVerifyVarArg | kVerifyVarArgNonZero | kVerifyVarArgRange | + kVerifyVarArgRangeNonZero | kVerifyError)); } bool GetVerifyIsRuntimeOnly() const { diff --git a/runtime/dex_instruction_list.h b/runtime/dex_instruction_list.h index 4cda58b92e..103b0d74ef 100644 --- a/runtime/dex_instruction_list.h +++ b/runtime/dex_instruction_list.h @@ -128,17 +128,17 @@ V(0x6B, SPUT_BYTE, "sput-byte", k21c, false, kFieldRef, kContinue | kThrow | kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \ V(0x6C, SPUT_CHAR, "sput-char", k21c, false, kFieldRef, kContinue | kThrow | kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \ V(0x6D, SPUT_SHORT, "sput-short", k21c, false, kFieldRef, kContinue | kThrow | kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \ - V(0x6E, INVOKE_VIRTUAL, "invoke-virtual", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArg) \ - V(0x6F, INVOKE_SUPER, "invoke-super", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArg) \ - V(0x70, INVOKE_DIRECT, "invoke-direct", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArg) \ + V(0x6E, INVOKE_VIRTUAL, "invoke-virtual", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgNonZero) \ + V(0x6F, INVOKE_SUPER, "invoke-super", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgNonZero) \ + V(0x70, INVOKE_DIRECT, "invoke-direct", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgNonZero) \ V(0x71, INVOKE_STATIC, "invoke-static", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArg) \ - V(0x72, INVOKE_INTERFACE, "invoke-interface", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArg) \ + V(0x72, INVOKE_INTERFACE, "invoke-interface", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgNonZero) \ V(0x73, RETURN_VOID_BARRIER, "return-void-barrier", k10x, false, kNone, kReturn, kVerifyNone) \ - V(0x74, INVOKE_VIRTUAL_RANGE, "invoke-virtual/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRange) \ - V(0x75, INVOKE_SUPER_RANGE, "invoke-super/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRange) \ - V(0x76, INVOKE_DIRECT_RANGE, "invoke-direct/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRange) \ + V(0x74, INVOKE_VIRTUAL_RANGE, "invoke-virtual/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \ + V(0x75, INVOKE_SUPER_RANGE, "invoke-super/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \ + V(0x76, INVOKE_DIRECT_RANGE, "invoke-direct/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \ V(0x77, INVOKE_STATIC_RANGE, "invoke-static/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRange) \ - V(0x78, INVOKE_INTERFACE_RANGE, "invoke-interface/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRange) \ + V(0x78, INVOKE_INTERFACE_RANGE, "invoke-interface/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \ V(0x79, UNUSED_79, "unused-79", k10x, false, kUnknown, 0, kVerifyError) \ V(0x7A, UNUSED_7A, "unused-7a", k10x, false, kUnknown, 0, kVerifyError) \ V(0x7B, NEG_INT, "neg-int", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \ @@ -251,8 +251,8 @@ V(0xE6, IPUT_QUICK, "iput-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \ V(0xE7, IPUT_WIDE_QUICK, "iput-wide-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegAWide | kVerifyRegB | kVerifyRuntimeOnly) \ V(0xE8, IPUT_OBJECT_QUICK, "iput-object-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \ - V(0xE9, INVOKE_VIRTUAL_QUICK, "invoke-virtual-quick", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyVarArg | kVerifyRuntimeOnly) \ - V(0xEA, INVOKE_VIRTUAL_RANGE_QUICK, "invoke-virtual/range-quick", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyVarArgRange | kVerifyRuntimeOnly) \ + V(0xE9, INVOKE_VIRTUAL_QUICK, "invoke-virtual-quick", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyVarArgNonZero | kVerifyRuntimeOnly) \ + V(0xEA, INVOKE_VIRTUAL_RANGE_QUICK, "invoke-virtual/range-quick", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyVarArgRangeNonZero | kVerifyRuntimeOnly) \ V(0xEB, UNUSED_EB, "unused-eb", k10x, false, kUnknown, 0, kVerifyError) \ V(0xEC, UNUSED_EC, "unused-ec", k10x, false, kUnknown, 0, kVerifyError) \ V(0xED, UNUSED_ED, "unused-ed", k10x, false, kUnknown, 0, kVerifyError) \ diff --git a/runtime/dex_method_iterator_test.cc b/runtime/dex_method_iterator_test.cc index 5e2d89ebeb..0d00cc3134 100644 --- a/runtime/dex_method_iterator_test.cc +++ b/runtime/dex_method_iterator_test.cc @@ -21,26 +21,15 @@ namespace art { class DexMethodIteratorTest : public CommonRuntimeTest { - public: - const DexFile* OpenDexFile(const std::string& partial_filename) { - std::string dfn = GetDexFileName(partial_filename); - std::string error_msg; - const DexFile* dexfile = DexFile::Open(dfn.c_str(), dfn.c_str(), &error_msg); - if (dexfile == nullptr) { - LG << "Failed to open '" << dfn << "': " << error_msg; - } - return dexfile; - } }; TEST_F(DexMethodIteratorTest, Basic) { ScopedObjectAccess soa(Thread::Current()); std::vector<const DexFile*> dex_files; - dex_files.push_back(OpenDexFile("core-libart")); - dex_files.push_back(OpenDexFile("conscrypt")); - dex_files.push_back(OpenDexFile("okhttp")); - dex_files.push_back(OpenDexFile("core-junit")); - dex_files.push_back(OpenDexFile("bouncycastle")); + const char* jars[] = { "core-libart", "conscrypt", "okhttp", "core-junit", "bouncycastle" }; + for (size_t i = 0; i < 5; ++i) { + dex_files.push_back(LoadExpectSingleDexFile(GetDexFileName(jars[i]).c_str())); + } DexMethodIterator it(dex_files); while (it.HasNext()) { const DexFile& dex_file = it.GetDexFile(); diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc index 330125458e..dde74de87a 100644 --- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc @@ -154,10 +154,12 @@ void SetQuickAllocEntryPoints##suffix(QuickEntryPoints* qpoints, bool instrument } // Generate the entrypoint functions. +#if !defined(__APPLE__) || !defined(__LP64__) GENERATE_ENTRYPOINTS(_dlmalloc); GENERATE_ENTRYPOINTS(_rosalloc); GENERATE_ENTRYPOINTS(_bump_pointer); GENERATE_ENTRYPOINTS(_tlab); +#endif static bool entry_points_instrumented = false; static gc::AllocatorType entry_points_allocator = gc::kAllocatorTypeDlMalloc; @@ -172,6 +174,7 @@ void SetQuickAllocEntryPointsInstrumented(bool instrumented) { void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints) { switch (entry_points_allocator) { +#if !defined(__APPLE__) || !defined(__LP64__) case gc::kAllocatorTypeDlMalloc: { SetQuickAllocEntryPoints_dlmalloc(qpoints, entry_points_instrumented); break; @@ -190,6 +193,7 @@ void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints) { SetQuickAllocEntryPoints_tlab(qpoints, entry_points_instrumented); break; } +#endif default: { LOG(FATAL) << "Unimplemented"; } diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc index 224b33e260..c0aa43ea41 100644 --- a/runtime/gc/accounting/space_bitmap.cc +++ b/runtime/gc/accounting/space_bitmap.cc @@ -52,7 +52,7 @@ SpaceBitmap<kAlignment>* SpaceBitmap<kAlignment>::Create( const size_t bitmap_size = ComputeBitmapSize(heap_capacity); std::string error_msg; std::unique_ptr<MemMap> mem_map(MemMap::MapAnonymous(name.c_str(), nullptr, bitmap_size, - PROT_READ | PROT_WRITE, false, &error_msg)); + PROT_READ | PROT_WRITE, false, &error_msg)); if (UNLIKELY(mem_map.get() == nullptr)) { LOG(ERROR) << "Failed to allocate bitmap " << name << ": " << error_msg; return nullptr; diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h index 0849171c6b..27fb08753d 100644 --- a/runtime/gc/accounting/space_bitmap.h +++ b/runtime/gc/accounting/space_bitmap.h @@ -42,7 +42,6 @@ template<size_t kAlignment> class SpaceBitmap { public: typedef void ScanCallback(mirror::Object* obj, void* finger, void* arg); - typedef void SweepCallback(size_t ptr_count, mirror::Object** ptrs, void* arg); // Initialize a space bitmap so that it points to a bitmap large enough to cover a heap at diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc index 55262f2359..09fb97a5c9 100644 --- a/runtime/gc/allocator/rosalloc.cc +++ b/runtime/gc/allocator/rosalloc.cc @@ -1642,7 +1642,7 @@ void RosAlloc::SetFootprintLimit(size_t new_capacity) { void RosAlloc::RevokeThreadLocalRuns(Thread* thread) { Thread* self = Thread::Current(); // Avoid race conditions on the bulk free bit maps with BulkFree() (GC). - WriterMutexLock wmu(self, bulk_free_lock_); + ReaderMutexLock wmu(self, bulk_free_lock_); for (size_t idx = 0; idx < kNumThreadLocalSizeBrackets; idx++) { MutexLock mu(self, *size_bracket_locks_[idx]); Run* thread_local_run = reinterpret_cast<Run*>(thread->GetRosAllocRun(idx)); @@ -1720,7 +1720,7 @@ void RosAlloc::AssertThreadLocalRunsAreRevoked(Thread* thread) { if (kIsDebugBuild) { Thread* self = Thread::Current(); // Avoid race conditions on the bulk free bit maps with BulkFree() (GC). - WriterMutexLock wmu(self, bulk_free_lock_); + ReaderMutexLock wmu(self, bulk_free_lock_); for (size_t idx = 0; idx < kNumThreadLocalSizeBrackets; idx++) { MutexLock mu(self, *size_bracket_locks_[idx]); Run* thread_local_run = reinterpret_cast<Run*>(thread->GetRosAllocRun(idx)); @@ -1867,7 +1867,7 @@ void RosAlloc::Verify() { CHECK(Locks::mutator_lock_->IsExclusiveHeld(self)) << "The mutator locks isn't exclusively locked at RosAlloc::Verify()"; MutexLock mu(self, *Locks::thread_list_lock_); - WriterMutexLock wmu(self, bulk_free_lock_); + ReaderMutexLock wmu(self, bulk_free_lock_); std::vector<Run*> runs; { MutexLock mu(self, lock_); diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h index a439188858..13f61ec935 100644 --- a/runtime/gc/allocator/rosalloc.h +++ b/runtime/gc/allocator/rosalloc.h @@ -45,10 +45,7 @@ class RosAlloc { byte magic_num_; // The magic number used for debugging only. bool IsFree() const { - if (kIsDebugBuild) { - return magic_num_ == kMagicNumFree; - } - return true; + return !kIsDebugBuild || magic_num_ == kMagicNumFree; } size_t ByteSize(RosAlloc* rosalloc) const EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) { const byte* fpr_base = reinterpret_cast<const byte*>(this); diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc index a17c36be6d..46d79bf796 100644 --- a/runtime/gc/collector/garbage_collector.cc +++ b/runtime/gc/collector/garbage_collector.cc @@ -31,20 +31,36 @@ namespace art { namespace gc { namespace collector { +Iteration::Iteration() + : duration_ns_(0), timings_("GC iteration timing logger", true, VLOG_IS_ON(heap)) { + Reset(kGcCauseBackground, false); // Reset to some place holder values. +} + +void Iteration::Reset(GcCause gc_cause, bool clear_soft_references) { + timings_.Reset(); + pause_times_.clear(); + duration_ns_ = 0; + clear_soft_references_ = clear_soft_references; + gc_cause_ = gc_cause; + freed_ = ObjectBytePair(); + freed_los_ = ObjectBytePair(); +} + +uint64_t Iteration::GetEstimatedThroughput() const { + // Add 1ms to prevent possible division by 0. + return (static_cast<uint64_t>(freed_.bytes) * 1000) / (NsToMs(GetDurationNs()) + 1); +} + GarbageCollector::GarbageCollector(Heap* heap, const std::string& name) : heap_(heap), name_(name), - gc_cause_(kGcCauseForAlloc), - clear_soft_references_(false), - duration_ns_(0), - timings_(name_.c_str(), true, VLOG_IS_ON(heap)), pause_histogram_((name_ + " paused").c_str(), kPauseBucketSize, kPauseBucketCount), cumulative_timings_(name) { ResetCumulativeStatistics(); } void GarbageCollector::RegisterPause(uint64_t nano_length) { - pause_times_.push_back(nano_length); + GetCurrentIteration()->pause_times_.push_back(nano_length); } void GarbageCollector::ResetCumulativeStatistics() { @@ -59,38 +75,33 @@ void GarbageCollector::Run(GcCause gc_cause, bool clear_soft_references) { ATRACE_BEGIN(StringPrintf("%s %s GC", PrettyCause(gc_cause), GetName()).c_str()); Thread* self = Thread::Current(); uint64_t start_time = NanoTime(); - timings_.Reset(); - pause_times_.clear(); - duration_ns_ = 0; - clear_soft_references_ = clear_soft_references; - gc_cause_ = gc_cause; - // Reset stats. - freed_bytes_ = 0; - freed_large_object_bytes_ = 0; - freed_objects_ = 0; - freed_large_objects_ = 0; + Iteration* current_iteration = GetCurrentIteration(); + current_iteration->Reset(gc_cause, clear_soft_references); RunPhases(); // Run all the GC phases. // Add the current timings to the cumulative timings. - cumulative_timings_.AddLogger(timings_); + cumulative_timings_.AddLogger(*GetTimings()); // Update cumulative statistics with how many bytes the GC iteration freed. - total_freed_objects_ += GetFreedObjects() + GetFreedLargeObjects(); - total_freed_bytes_ += GetFreedBytes() + GetFreedLargeObjectBytes(); + total_freed_objects_ += current_iteration->GetFreedObjects() + + current_iteration->GetFreedLargeObjects(); + total_freed_bytes_ += current_iteration->GetFreedBytes() + + current_iteration->GetFreedLargeObjectBytes(); uint64_t end_time = NanoTime(); - duration_ns_ = end_time - start_time; + current_iteration->SetDurationNs(end_time - start_time); if (Locks::mutator_lock_->IsExclusiveHeld(self)) { // The entire GC was paused, clear the fake pauses which might be in the pause times and add // the whole GC duration. - pause_times_.clear(); - RegisterPause(duration_ns_); + current_iteration->pause_times_.clear(); + RegisterPause(current_iteration->GetDurationNs()); } - total_time_ns_ += GetDurationNs(); - for (uint64_t pause_time : pause_times_) { + total_time_ns_ += current_iteration->GetDurationNs(); + for (uint64_t pause_time : current_iteration->GetPauseTimes()) { pause_histogram_.AddValue(pause_time / 1000); } ATRACE_END(); } void GarbageCollector::SwapBitmaps() { + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); // Swap the live and mark bitmaps for each alloc space. This is needed since sweep re-swaps // these bitmaps. The bitmap swapping is an optimization so that we do not need to clear the live // bits of dead objects in the live bitmap. @@ -125,23 +136,6 @@ uint64_t GarbageCollector::GetEstimatedMeanThroughput() const { return (total_freed_bytes_ * 1000) / (NsToMs(GetCumulativeTimings().GetTotalNs()) + 1); } -uint64_t GarbageCollector::GetEstimatedLastIterationThroughput() const { - // Add 1ms to prevent possible division by 0. - return (static_cast<uint64_t>(freed_bytes_) * 1000) / (NsToMs(GetDurationNs()) + 1); -} - -void GarbageCollector::RecordFree(uint64_t freed_objects, int64_t freed_bytes) { - freed_objects_ += freed_objects; - freed_bytes_ += freed_bytes; - GetHeap()->RecordFree(freed_objects, freed_bytes); -} - -void GarbageCollector::RecordFreeLargeObjects(uint64_t freed_objects, int64_t freed_bytes) { - freed_large_objects_ += freed_objects; - freed_large_object_bytes_ += freed_bytes; - GetHeap()->RecordFree(freed_objects, freed_bytes); -} - void GarbageCollector::ResetMeasurements() { cumulative_timings_.Reset(); pause_histogram_.Reset(); @@ -160,6 +154,23 @@ GarbageCollector::ScopedPause::~ScopedPause() { Runtime::Current()->GetThreadList()->ResumeAll(); } +// Returns the current GC iteration and assocated info. +Iteration* GarbageCollector::GetCurrentIteration() { + return heap_->GetCurrentGcIteration(); +} +const Iteration* GarbageCollector::GetCurrentIteration() const { + return heap_->GetCurrentGcIteration(); +} + +void GarbageCollector::RecordFree(const ObjectBytePair& freed) { + GetCurrentIteration()->freed_.Add(freed); + heap_->RecordFree(freed.objects, freed.bytes); +} +void GarbageCollector::RecordFreeLOS(const ObjectBytePair& freed) { + GetCurrentIteration()->freed_los_.Add(freed); + heap_->RecordFree(freed.objects, freed.bytes); +} + } // namespace collector } // namespace gc } // namespace art diff --git a/runtime/gc/collector/garbage_collector.h b/runtime/gc/collector/garbage_collector.h index f4f9dbb40a..885569efd9 100644 --- a/runtime/gc/collector/garbage_collector.h +++ b/runtime/gc/collector/garbage_collector.h @@ -33,6 +33,78 @@ class Heap; namespace collector { +struct ObjectBytePair { + ObjectBytePair(uint64_t num_objects = 0, int64_t num_bytes = 0) + : objects(num_objects), bytes(num_bytes) {} + void Add(const ObjectBytePair& other) { + objects += other.objects; + bytes += other.bytes; + } + // Number of objects which were freed. + uint64_t objects; + // Freed bytes are signed since the GC can free negative bytes if it promotes objects to a space + // which has a larger allocation size. + int64_t bytes; +}; + +// A information related single garbage collector iteration. Since we only ever have one GC running +// at any given time, we can have a single iteration info. +class Iteration { + public: + Iteration(); + // Returns how long the mutators were paused in nanoseconds. + const std::vector<uint64_t>& GetPauseTimes() const { + return pause_times_; + } + TimingLogger* GetTimings() { + return &timings_; + } + // Returns how long the GC took to complete in nanoseconds. + uint64_t GetDurationNs() const { + return duration_ns_; + } + int64_t GetFreedBytes() const { + return freed_.bytes; + } + int64_t GetFreedLargeObjectBytes() const { + return freed_los_.bytes; + } + uint64_t GetFreedObjects() const { + return freed_.objects; + } + uint64_t GetFreedLargeObjects() const { + return freed_los_.objects; + } + void Reset(GcCause gc_cause, bool clear_soft_references); + // Returns the estimated throughput of the iteration. + uint64_t GetEstimatedThroughput() const; + bool GetClearSoftReferences() const { + return clear_soft_references_; + } + void SetClearSoftReferences(bool clear_soft_references) { + clear_soft_references_ = clear_soft_references; + } + GcCause GetGcCause() const { + return gc_cause_; + } + + private: + void SetDurationNs(uint64_t duration) { + duration_ns_ = duration; + } + + GcCause gc_cause_; + bool clear_soft_references_; + uint64_t duration_ns_; + TimingLogger timings_; + ObjectBytePair freed_; + ObjectBytePair freed_los_; + std::vector<uint64_t> pause_times_; + + friend class GarbageCollector; + DISALLOW_COPY_AND_ASSIGN(Iteration); +}; + class GarbageCollector { public: class SCOPED_LOCKABLE ScopedPause { @@ -62,22 +134,7 @@ class GarbageCollector { Heap* GetHeap() const { return heap_; } - - // Returns how long the mutators were paused in nanoseconds. - const std::vector<uint64_t>& GetPauseTimes() const { - return pause_times_; - } - - // Returns how long the GC took to complete in nanoseconds. - uint64_t GetDurationNs() const { - return duration_ns_; - } - void RegisterPause(uint64_t nano_length); - - TimingLogger& GetTimings() { - return timings_; - } const CumulativeLogger& GetCumulativeTimings() const { return cumulative_timings_; } @@ -87,52 +144,36 @@ class GarbageCollector { // Swap the live and mark bitmaps of spaces that are active for the collector. For partial GC, // this is the allocation space, for full GC then we swap the zygote bitmaps too. void SwapBitmaps() EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - - int64_t GetFreedBytes() const { - return freed_bytes_; - } - - int64_t GetFreedLargeObjectBytes() const { - return freed_large_object_bytes_; - } - - uint64_t GetFreedObjects() const { - return freed_objects_; - } - - uint64_t GetFreedLargeObjects() const { - return freed_large_objects_; - } - uint64_t GetTotalPausedTimeNs() const { return pause_histogram_.AdjustedSum(); } - int64_t GetTotalFreedBytes() const { return total_freed_bytes_; } - uint64_t GetTotalFreedObjects() const { return total_freed_objects_; } - const Histogram<uint64_t>& GetPauseHistogram() const { return pause_histogram_; } - // Reset the cumulative timings and pause histogram. void ResetMeasurements(); - // Returns the estimated throughput in bytes / second. uint64_t GetEstimatedMeanThroughput() const; - - // Returns the estimated throughput of the last GC iteration. - uint64_t GetEstimatedLastIterationThroughput() const; - // Returns how many GC iterations have been run. - size_t GetIterations() const { + size_t NumberOfIterations() const { return GetCumulativeTimings().GetIterations(); } + // Returns the current GC iteration and assocated info. + Iteration* GetCurrentIteration(); + const Iteration* GetCurrentIteration() const; + TimingLogger* GetTimings() { + return &GetCurrentIteration()->timings_; + } + // Record a free of normal objects. + void RecordFree(const ObjectBytePair& freed); + // Record a free of large objects. + void RecordFreeLOS(const ObjectBytePair& freed); protected: // Run all of the GC phases. @@ -141,40 +182,17 @@ class GarbageCollector { // Revoke all the thread-local buffers. virtual void RevokeAllThreadLocalBuffers() = 0; - // Record that you have freed some objects or large objects, calls Heap::RecordFree. - // TODO: These are not thread safe, add a lock if we get parallel sweeping. - void RecordFree(uint64_t freed_objects, int64_t freed_bytes); - void RecordFreeLargeObjects(uint64_t freed_objects, int64_t freed_bytes); - static constexpr size_t kPauseBucketSize = 500; static constexpr size_t kPauseBucketCount = 32; Heap* const heap_; - std::string name_; - - GcCause gc_cause_; - bool clear_soft_references_; - - uint64_t duration_ns_; - TimingLogger timings_; - // Cumulative statistics. Histogram<uint64_t> pause_histogram_; uint64_t total_time_ns_; uint64_t total_freed_objects_; int64_t total_freed_bytes_; - - // Single GC statitstics, freed bytes are signed since the GC can free negative bytes if it - // promotes objects to a space which has a larger allocation size. - int64_t freed_bytes_; - int64_t freed_large_object_bytes_; - uint64_t freed_objects_; - uint64_t freed_large_objects_; - CumulativeLogger cumulative_timings_; - - std::vector<uint64_t> pause_times_; }; } // namespace collector diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc new file mode 100644 index 0000000000..40448524c6 --- /dev/null +++ b/runtime/gc/collector/mark_compact.cc @@ -0,0 +1,623 @@ +/* + * 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 "base/logging.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/remembered_set.h" +#include "gc/accounting/space_bitmap-inl.h" +#include "gc/heap.h" +#include "gc/reference_processor.h" +#include "gc/space/bump_pointer_space.h" +#include "gc/space/bump_pointer_space-inl.h" +#include "gc/space/image_space.h" +#include "gc/space/large_object_space.h" +#include "gc/space/space-inl.h" +#include "indirect_reference_table.h" +#include "intern_table.h" +#include "jni_internal.h" +#include "mark_sweep-inl.h" +#include "monitor.h" +#include "mirror/art_field.h" +#include "mirror/art_field-inl.h" +#include "mirror/class-inl.h" +#include "mirror/class_loader.h" +#include "mirror/dex_cache.h" +#include "mirror/reference-inl.h" +#include "mirror/object-inl.h" +#include "mirror/object_array.h" +#include "mirror/object_array-inl.h" +#include "runtime.h" +#include "stack.h" +#include "thread-inl.h" +#include "thread_list.h" + +using ::art::mirror::Object; + +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) { + CHECK(immune_region_.AddContinuousSpace(space)) << "Failed to add space " << *space; + } + } +} + +MarkCompact::MarkCompact(Heap* heap, const std::string& name_prefix) + : GarbageCollector(heap, name_prefix + (name_prefix.empty() ? "" : " ") + "mark compact"), + space_(nullptr), collector_name_(name_) { +} + +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 (lock_word.GetValue() != LockWord().GetValue()) { + // 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<size_t>(bump_pointer_)), + false); + bump_pointer_ += alloc_size; + ++live_objects_in_space_; +} + +class CalculateObjectForwardingAddressVisitor { + public: + explicit CalculateObjectForwardingAddressVisitor(MarkCompact* collector) + : collector_(collector) {} + void operator()(mirror::Object* obj) const EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, + Locks::heap_bitmap_lock_) { + DCHECK_ALIGNED(obj, space::BumpPointerSpace::kAlignment); + DCHECK(collector_->IsMarked(obj)); + collector_->ForwardObject(obj); + } + + private: + MarkCompact* const collector_; +}; + +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<byte*>(space_->Begin()); + // Visit all the marked objects in the bitmap. + CalculateObjectForwardingAddressVisitor visitor(this); + objects_before_forwarding_->VisitMarkedRange(reinterpret_cast<uintptr_t>(space_->Begin()), + reinterpret_cast<uintptr_t>(space_->End()), + visitor); +} + +void MarkCompact::InitializePhase() { + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); + mark_stack_ = heap_->GetMarkStack(); + DCHECK(mark_stack_ != nullptr); + immune_region_.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(), + &HeapReferenceMarkedCallback, &MarkObjectCallback, &ProcessMarkStackCallback, this); +} + +class BitmapSetSlowPathVisitor { + public: + void operator()(const mirror::Object* obj) const { + // Marking a large object, make sure its aligned as a sanity check. + if (!IsAligned<kPageSize>(obj)) { + Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR)); + LOG(FATAL) << obj; + } + } +}; + +inline void MarkCompact::MarkObject(mirror::Object* obj) { + if (obj == nullptr) { + return; + } + if (kUseBakerOrBrooksReadBarrier) { + // Verify all the objects have the correct forward pointer installed. + obj->AssertReadBarrierPointer(); + } + if (immune_region_.ContainsObject(obj)) { + return; + } + 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)); + BitmapSetSlowPathVisitor visitor; + if (!mark_bitmap_->Set(obj, visitor)) { + // This object was not previously marked. + MarkStackPush(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); + // Clear the whole card table since we can not 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(self); + { + 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(); + } + // 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_region_.ContainsSpace(space)) { + accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space); + if (table != nullptr) { + // TODO: Improve naming. + TimingLogger::ScopedTiming t( + space->IsZygoteSpace() ? "UpdateAndMarkZygoteModUnionTable" : + "UpdateAndMarkImageModUnionTable", GetTimings()); + table->UpdateAndMarkReferences(MarkHeapReferenceCallback, 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<Object*> temp(mark_stack_->Begin(), mark_stack_->End()); + CHECK_LE(mark_stack_->Size(), new_size); + mark_stack_->Resize(new_size); + for (const auto& obj : temp) { + mark_stack_->PushBack(obj); + } +} + +inline void MarkCompact::MarkStackPush(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::ProcessMarkStackCallback(void* arg) { + reinterpret_cast<MarkCompact*>(arg)->ProcessMarkStack(); +} + +mirror::Object* MarkCompact::MarkObjectCallback(mirror::Object* root, void* arg) { + reinterpret_cast<MarkCompact*>(arg)->MarkObject(root); + return root; +} + +void MarkCompact::MarkHeapReferenceCallback(mirror::HeapReference<mirror::Object>* obj_ptr, + void* arg) { + reinterpret_cast<MarkCompact*>(arg)->MarkObject(obj_ptr->AsMirrorPtr()); +} + +void MarkCompact::DelayReferenceReferentCallback(mirror::Class* klass, mirror::Reference* ref, + void* arg) { + reinterpret_cast<MarkCompact*>(arg)->DelayReferenceReferent(klass, ref); +} + +void MarkCompact::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id*/, + RootType /*root_type*/) { + reinterpret_cast<MarkCompact*>(arg)->MarkObject(*root); +} + +void MarkCompact::UpdateRootCallback(Object** root, void* arg, uint32_t /*thread_id*/, + RootType /*root_type*/) { + mirror::Object* obj = *root; + mirror::Object* new_obj = reinterpret_cast<MarkCompact*>(arg)->GetMarkedForwardAddress(obj); + if (obj != new_obj) { + *root = new_obj; + DCHECK(new_obj != nullptr); + } +} + +class UpdateObjectReferencesVisitor { + public: + explicit UpdateObjectReferencesVisitor(MarkCompact* collector) : collector_(collector) { + } + void operator()(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE { + collector_->UpdateObjectReferences(obj); + } + + private: + MarkCompact* const collector_; +}; + +void MarkCompact::UpdateReferences() { + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); + Runtime* runtime = Runtime::Current(); + // Update roots. + runtime->VisitRoots(UpdateRootCallback, this); + // 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 t( + space->IsZygoteSpace() ? "UpdateZygoteModUnionTableReferences" : + "UpdateImageModUnionTableReferences", + GetTimings()); + table->UpdateAndMarkReferences(&UpdateHeapReferenceCallback, 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<uintptr_t>(space->Begin()), + reinterpret_cast<uintptr_t>(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(&MarkedForwardingAddressCallback, 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<uintptr_t>(space_->Begin()), + reinterpret_cast<uintptr_t>(space_->End()), + visitor); + // Update the reference processor cleared list. + heap_->GetReferenceProcessor()->UpdateRoots(&MarkedForwardingAddressCallback, this); +} + +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<int64_t>(space_->End()) - + reinterpret_cast<int64_t>(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(MarkRootCallback, this); +} + +mirror::Object* MarkCompact::MarkedForwardingAddressCallback(mirror::Object* obj, void* arg) { + return reinterpret_cast<MarkCompact*>(arg)->GetMarkedForwardAddress(obj); +} + +inline void MarkCompact::UpdateHeapReference(mirror::HeapReference<mirror::Object>* 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); + } + } +} + +void MarkCompact::UpdateHeapReferenceCallback(mirror::HeapReference<mirror::Object>* reference, + void* arg) { + reinterpret_cast<MarkCompact*>(arg)->UpdateHeapReference(reference); +} + +class UpdateReferenceVisitor { + public: + explicit UpdateReferenceVisitor(MarkCompact* collector) : collector_(collector) { + } + + void operator()(Object* obj, MemberOffset offset, bool /*is_static*/) const + ALWAYS_INLINE EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { + collector_->UpdateHeapReference(obj->GetFieldObjectReferenceAddr<kVerifyNone>(offset)); + } + + void operator()(mirror::Class* /*klass*/, mirror::Reference* ref) const + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { + collector_->UpdateHeapReference( + ref->GetFieldObjectReferenceAddr<kVerifyNone>(mirror::Reference::ReferentOffset())); + } + + private: + MarkCompact* const collector_; +}; + +void MarkCompact::UpdateObjectReferences(mirror::Object* obj) { + UpdateReferenceVisitor visitor(this); + obj->VisitReferences<kMovingClasses>(visitor, visitor); +} + +inline mirror::Object* MarkCompact::GetMarkedForwardAddress(mirror::Object* obj) const { + DCHECK(obj != nullptr); + if (objects_before_forwarding_->HasAddress(obj)) { + DCHECK(objects_before_forwarding_->Test(obj)); + mirror::Object* ret = + reinterpret_cast<mirror::Object*>(obj->GetLockWord(false).ForwardingAddress()); + DCHECK(ret != nullptr); + return ret; + } + DCHECK(!space_->HasAddress(obj)); + DCHECK(IsMarked(obj)); + return obj; +} + +inline bool MarkCompact::IsMarked(const Object* object) const { + if (immune_region_.ContainsObject(object)) { + return true; + } + if (objects_before_forwarding_->HasAddress(object)) { + return objects_before_forwarding_->Test(object); + } + return mark_bitmap_->Test(object); +} + +mirror::Object* MarkCompact::IsMarkedCallback(mirror::Object* object, void* arg) { + return reinterpret_cast<MarkCompact*>(arg)->IsMarked(object) ? object : nullptr; +} + +bool MarkCompact::HeapReferenceMarkedCallback(mirror::HeapReference<mirror::Object>* ref_ptr, + void* arg) { + // Side effect free since we call this before ever moving objects. + return reinterpret_cast<MarkCompact*>(arg)->IsMarked(ref_ptr->AsMirrorPtr()); +} + +void MarkCompact::SweepSystemWeaks() { + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); + Runtime::Current()->SweepSystemWeaks(IsMarkedCallback, this); +} + +bool MarkCompact::ShouldSweepSpace(space::ContinuousSpace* space) const { + return space != space_ && !immune_region_.ContainsSpace(space); +} + +class MoveObjectVisitor { + public: + explicit MoveObjectVisitor(MarkCompact* collector) : collector_(collector) { + } + void operator()(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE { + collector_->MoveObject(obj, obj->SizeOf()); + } + + private: + MarkCompact* const collector_; +}; + +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<mirror::Object*>(dest_addr); + DCHECK(space_->HasAddress(dest_obj)) << dest_obj; + // Use memmove since there may be overlap. + memmove(reinterpret_cast<void*>(dest_addr), reinterpret_cast<const void*>(obj), len); + // Restore the saved lock word if needed. + LockWord lock_word; + 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. + MoveObjectVisitor visitor(this); + objects_before_forwarding_->VisitMarkedRange(reinterpret_cast<uintptr_t>(space_->Begin()), + reinterpret_cast<uintptr_t>(space_->End()), + visitor); + 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 t( + alloc_space->IsZygoteSpace() ? "SweepZygoteSpace" : "SweepAllocSpace", GetTimings()); + RecordFree(alloc_space->Sweep(swap_bitmaps)); + } + } + SweepLargeObjects(swap_bitmaps); +} + +void MarkCompact::SweepLargeObjects(bool swap_bitmaps) { + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); + RecordFreeLOS(heap_->GetLargeObjectsSpace()->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(mirror::Class* klass, mirror::Reference* reference) { + heap_->GetReferenceProcessor()->DelayReferenceReferent(klass, reference, + &HeapReferenceMarkedCallback, this); +} + +class MarkCompactMarkObjectVisitor { + public: + explicit MarkCompactMarkObjectVisitor(MarkCompact* collector) : collector_(collector) { + } + + void operator()(Object* obj, MemberOffset offset, bool /*is_static*/) const ALWAYS_INLINE + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { + // Object was already verified when we scanned it. + collector_->MarkObject(obj->GetFieldObject<mirror::Object, kVerifyNone>(offset)); + } + + void operator()(mirror::Class* klass, mirror::Reference* ref) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { + collector_->DelayReferenceReferent(klass, ref); + } + + private: + MarkCompact* const collector_; +}; + +// Visit all of the references of an object and update. +void MarkCompact::ScanObject(Object* obj) { + MarkCompactMarkObjectVisitor visitor(this); + obj->VisitReferences<kMovingClasses>(visitor, visitor); +} + +// Scan anything that's on the mark stack. +void MarkCompact::ProcessMarkStack() { + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); + while (!mark_stack_->IsEmpty()) { + 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 new file mode 100644 index 0000000000..25cfe0fbfd --- /dev/null +++ b/runtime/gc/collector/mark_compact.h @@ -0,0 +1,255 @@ +/* + * 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 <deque> +#include <memory> // For unique_ptr. + +#include "atomic.h" +#include "base/macros.h" +#include "base/mutex.h" +#include "garbage_collector.h" +#include "gc/accounting/heap_bitmap.h" +#include "immune_region.h" +#include "lock_word.h" +#include "object_callbacks.h" +#include "offsets.h" + +namespace art { + +class Thread; + +namespace mirror { + class Class; + class Object; +} // namespace mirror + +namespace gc { + +class Heap; + +namespace accounting { + template <typename T> class AtomicStack; + typedef AtomicStack<mirror::Object*> ObjectStack; +} // namespace accounting + +namespace space { + 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() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) + LOCKS_EXCLUDED(Locks::heap_bitmap_lock_); + void ReclaimPhase() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) + LOCKS_EXCLUDED(Locks::heap_bitmap_lock_); + void FinishPhase() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + void MarkReachableObjects() + EXCLUSIVE_LOCKS_REQUIRED(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) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + // Marks the root set at the start of a garbage collection. + void MarkRoots() + EXCLUSIVE_LOCKS_REQUIRED(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() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + LOCKS_EXCLUDED(Locks::heap_bitmap_lock_); + + void UnBindBitmaps() + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + void ProcessReferences(Thread* self) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Sweeps unmarked objects to complete the garbage collection. + void Sweep(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + // Sweeps unmarked objects to complete the garbage collection. + void SweepLargeObjects(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + void SweepSystemWeaks() + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + static void MarkRootCallback(mirror::Object** root, void* arg, uint32_t /*tid*/, + RootType /*root_type*/) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + static mirror::Object* MarkObjectCallback(mirror::Object* root, void* arg) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + static void MarkHeapReferenceCallback(mirror::HeapReference<mirror::Object>* obj_ptr, void* arg) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + static bool HeapReferenceMarkedCallback(mirror::HeapReference<mirror::Object>* ref_ptr, + void* arg) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + static void ProcessMarkStackCallback(void* arg) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); + + static void DelayReferenceReferentCallback(mirror::Class* klass, mirror::Reference* ref, + void* arg) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + // Schedules an unmarked object for reference processing. + void DelayReferenceReferent(mirror::Class* klass, mirror::Reference* reference) + SHARED_LOCKS_REQUIRED(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) const + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + static mirror::Object* MarkedForwardingAddressCallback(mirror::Object* object, void* arg) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) + SHARED_LOCKS_REQUIRED(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) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Expand mark stack to 2x its current size. + void ResizeMarkStack(size_t new_size); + + // 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); + + void UpdateAndMarkModUnion() + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Recursively blackens objects on the mark stack. + void ProcessMarkStack() + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); + + // 3 pass mark compact approach. + void Compact() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); + // Calculate the forwarding address of objects marked as "live" in the objects_before_forwarding + // bitmap. + void CalculateObjectForwardingAddresses() + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); + // Update the references of objects by using the forwarding addresses. + void UpdateReferences() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); + static void UpdateRootCallback(mirror::Object** root, void* arg, uint32_t /*thread_id*/, + RootType /*root_type*/) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + // Move objects and restore lock words. + void MoveObjects() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + // Move a single object to its forward address. + void MoveObject(mirror::Object* obj, size_t len) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + // Mark a single object. + void MarkObject(mirror::Object* obj) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, + Locks::mutator_lock_); + bool IsMarked(const mirror::Object* obj) const + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + static mirror::Object* IsMarkedCallback(mirror::Object* object, void* arg) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + void ForwardObject(mirror::Object* obj) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, + Locks::mutator_lock_); + // Update a single heap reference. + void UpdateHeapReference(mirror::HeapReference<mirror::Object>* reference) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + static void UpdateHeapReferenceCallback(mirror::HeapReference<mirror::Object>* reference, + void* arg) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + // Update all of the references of a single object. + void UpdateObjectReferences(mirror::Object* obj) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Revoke all the thread-local buffers. + void RevokeAllThreadLocalBuffers(); + + accounting::ObjectStack* mark_stack_; + + // Immune region, every object inside the immune region is assumed to be marked. + ImmuneRegion immune_region_; + + // 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. + byte* 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<accounting::ContinuousSpaceBitmap> objects_before_forwarding_; + // Bitmap which describes which lock words we need to restore. + std::unique_ptr<accounting::ContinuousSpaceBitmap> objects_with_lockword_; + // Which lock words we need to restore as we are moving objects. + std::deque<LockWord> lock_words_to_restore_; + + private: + friend class BitmapSetSlowPathVisitor; + friend class CalculateObjectForwardingAddressVisitor; + friend class MarkCompactMarkObjectVisitor; + friend class MoveObjectVisitor; + friend class UpdateObjectReferencesVisitor; + friend class UpdateReferenceVisitor; + DISALLOW_COPY_AND_ASSIGN(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 c72913a401..7e97b3b16b 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -43,10 +43,7 @@ #include "thread-inl.h" #include "thread_list.h" -using ::art::mirror::ArtField; -using ::art::mirror::Class; using ::art::mirror::Object; -using ::art::mirror::ObjectArray; namespace art { namespace gc { @@ -84,7 +81,7 @@ static constexpr bool kVerifyRootsMarked = kIsDebugBuild; static constexpr bool kRevokeRosAllocThreadLocalBuffersAtCheckpoint = true; void MarkSweep::BindBitmaps() { - timings_.StartSplit("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()) { @@ -92,7 +89,6 @@ void MarkSweep::BindBitmaps() { CHECK(immune_region_.AddContinuousSpace(space)) << "Failed to add space " << *space; } } - timings_.EndSplit(); } MarkSweep::MarkSweep(Heap* heap, bool is_concurrent, const std::string& name_prefix) @@ -113,7 +109,7 @@ MarkSweep::MarkSweep(Heap* heap, bool is_concurrent, const std::string& name_pre } void MarkSweep::InitializePhase() { - TimingLogger::ScopedSplit split("InitializePhase", &timings_); + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); mark_stack_ = heap_->GetMarkStack(); DCHECK(mark_stack_ != nullptr); immune_region_.Reset(); @@ -135,9 +131,9 @@ void MarkSweep::InitializePhase() { ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); mark_bitmap_ = heap_->GetMarkBitmap(); } - if (!clear_soft_references_) { + if (!GetCurrentIteration()->GetClearSoftReferences()) { // Always clear soft references if a non-sticky collection. - clear_soft_references_ = GetGcType() != collector::kGcTypeSticky; + GetCurrentIteration()->SetClearSoftReferences(GetGcType() != collector::kGcTypeSticky); } } @@ -173,15 +169,14 @@ void MarkSweep::RunPhases() { } void MarkSweep::ProcessReferences(Thread* self) { - TimingLogger::ScopedSplit split("ProcessReferences", &timings_); WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); GetHeap()->GetReferenceProcessor()->ProcessReferences( - true, &timings_, clear_soft_references_, &HeapReferenceMarkedCallback, &MarkObjectCallback, - &ProcessMarkStackCallback, this); + true, GetTimings(), GetCurrentIteration()->GetClearSoftReferences(), + &HeapReferenceMarkedCallback, &MarkObjectCallback, &ProcessMarkStackCallback, this); } void MarkSweep::PausePhase() { - TimingLogger::ScopedSplit split("(Paused)PausePhase", &timings_); + TimingLogger::ScopedTiming t("(Paused)PausePhase", GetTimings()); Thread* self = Thread::Current(); Locks::mutator_lock_->AssertExclusiveHeld(self); if (IsConcurrent()) { @@ -193,7 +188,7 @@ void MarkSweep::PausePhase() { RecursiveMarkDirtyObjects(true, accounting::CardTable::kCardDirty); } { - TimingLogger::ScopedSplit split("SwapStacks", &timings_); + TimingLogger::ScopedTiming t2("SwapStacks", GetTimings()); WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); heap_->SwapStacks(self); live_stack_freeze_size_ = heap_->GetLiveStack()->Size(); @@ -201,9 +196,7 @@ void MarkSweep::PausePhase() { // stacks and don't want anybody to allocate into the live stack. RevokeAllThreadLocalAllocationStacks(self); } - timings_.StartSplit("PreSweepingGcVerification"); heap_->PreSweepingGcVerification(this); - timings_.EndSplit(); // Disallow new system weaks to prevent a race which occurs when someone adds a new system // weak before we sweep them. Since this new system weak may not be marked, the GC may // incorrectly sweep it. This also fixes a race where interning may attempt to return a strong @@ -217,10 +210,11 @@ void MarkSweep::PausePhase() { void MarkSweep::PreCleanCards() { // Don't do this for non concurrent GCs since they don't have any dirty cards. if (kPreCleanCards && IsConcurrent()) { + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); Thread* self = Thread::Current(); CHECK(!Locks::mutator_lock_->IsExclusiveHeld(self)); // Process dirty cards and add dirty cards to mod union tables, also ages cards. - heap_->ProcessCards(timings_, false); + heap_->ProcessCards(GetTimings(), false); // The checkpoint root marking is required to avoid a race condition which occurs if the // following happens during a reference write: // 1. mutator dirties the card (write barrier) @@ -246,22 +240,19 @@ void MarkSweep::PreCleanCards() { void MarkSweep::RevokeAllThreadLocalAllocationStacks(Thread* self) { if (kUseThreadLocalAllocationStack) { - timings_.NewSplit("RevokeAllThreadLocalAllocationStacks"); + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); Locks::mutator_lock_->AssertExclusiveHeld(self); heap_->RevokeAllThreadLocalAllocationStacks(self); } } void MarkSweep::MarkingPhase() { - TimingLogger::ScopedSplit split("MarkingPhase", &timings_); + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); Thread* self = Thread::Current(); - BindBitmaps(); FindDefaultSpaceBitmap(); - // Process dirty cards and add dirty cards to mod union tables. - heap_->ProcessCards(timings_, false); - + heap_->ProcessCards(GetTimings(), false); WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); MarkRoots(self); MarkReachableObjects(); @@ -274,7 +265,7 @@ void MarkSweep::UpdateAndMarkModUnion() { if (immune_region_.ContainsSpace(space)) { const char* name = space->IsZygoteSpace() ? "UpdateAndMarkZygoteModUnionTable" : "UpdateAndMarkImageModUnionTable"; - TimingLogger::ScopedSplit split(name, &timings_); + TimingLogger::ScopedTiming t(name, GetTimings()); accounting::ModUnionTable* mod_union_table = heap_->FindModUnionTableFromSpace(space); CHECK(mod_union_table != nullptr); mod_union_table->UpdateAndMarkReferences(MarkHeapReferenceCallback, this); @@ -289,7 +280,7 @@ void MarkSweep::MarkReachableObjects() { } void MarkSweep::ReclaimPhase() { - TimingLogger::ScopedSplit split("ReclaimPhase", &timings_); + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); Thread* self = Thread::Current(); // Process the references concurrently. ProcessReferences(self); @@ -297,25 +288,19 @@ void MarkSweep::ReclaimPhase() { Runtime::Current()->AllowNewSystemWeaks(); { WriterMutexLock mu(self, *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. - timings_.StartSplit("SwapBitmaps"); SwapBitmaps(); - timings_.EndSplit(); - // Unbind the live and mark bitmaps. - TimingLogger::ScopedSplit split("UnBindBitmaps", &timings_); GetHeap()->UnBindBitmaps(); } } void MarkSweep::FindDefaultSpaceBitmap() { - TimingLogger::ScopedSplit split("FindDefaultMarkBitmap", &timings_); + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); for (const auto& space : GetHeap()->GetContinuousSpaces()) { accounting::ContinuousSpaceBitmap* bitmap = space->GetMarkBitmap(); // We want to have the main space instead of non moving if possible. @@ -512,11 +497,10 @@ void MarkSweep::VerifyRoots() { } void MarkSweep::MarkRoots(Thread* self) { + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); if (Locks::mutator_lock_->IsExclusiveHeld(self)) { // If we exclusively hold the mutator lock, all threads must be suspended. - timings_.StartSplit("MarkRoots"); Runtime::Current()->VisitRoots(MarkRootCallback, this); - timings_.EndSplit(); RevokeAllThreadLocalAllocationStacks(self); } else { MarkRootsCheckpoint(self, kRevokeRosAllocThreadLocalBuffersAtCheckpoint); @@ -528,16 +512,14 @@ void MarkSweep::MarkRoots(Thread* self) { } void MarkSweep::MarkNonThreadRoots() { - timings_.StartSplit("MarkNonThreadRoots"); + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); Runtime::Current()->VisitNonThreadRoots(MarkRootCallback, this); - timings_.EndSplit(); } void MarkSweep::MarkConcurrentRoots(VisitRootFlags flags) { - timings_.StartSplit("MarkConcurrentRoots"); + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); // Visit all runtime roots and clear dirty flags. Runtime::Current()->VisitConcurrentRoots(MarkRootCallback, this, flags); - timings_.EndSplit(); } class ScanObjectVisitor { @@ -758,7 +740,8 @@ void MarkSweep::ScanGrayObjects(bool paused, byte minimum_age) { Thread* self = Thread::Current(); // Can't have a different split for each space since multiple spaces can have their cards being // scanned at the same time. - timings_.StartSplit(paused ? "(Paused)ScanGrayObjects" : "ScanGrayObjects"); + TimingLogger::ScopedTiming t(paused ? "(Paused)ScanGrayObjects" : __FUNCTION__, + GetTimings()); // Try to take some of the mark stack since we can pass this off to the worker tasks. Object** mark_stack_begin = mark_stack_->Begin(); Object** mark_stack_end = mark_stack_->End(); @@ -811,28 +794,28 @@ void MarkSweep::ScanGrayObjects(bool paused, byte minimum_age) { thread_pool->StartWorkers(self); thread_pool->Wait(self, true, true); thread_pool->StopWorkers(self); - timings_.EndSplit(); } else { for (const auto& space : GetHeap()->GetContinuousSpaces()) { if (space->GetMarkBitmap() != nullptr) { // Image spaces are handled properly since live == marked for them. + const char* name = nullptr; switch (space->GetGcRetentionPolicy()) { - case space::kGcRetentionPolicyNeverCollect: - timings_.StartSplit(paused ? "(Paused)ScanGrayImageSpaceObjects" : - "ScanGrayImageSpaceObjects"); - break; - case space::kGcRetentionPolicyFullCollect: - timings_.StartSplit(paused ? "(Paused)ScanGrayZygoteSpaceObjects" : - "ScanGrayZygoteSpaceObjects"); - break; - case space::kGcRetentionPolicyAlwaysCollect: - timings_.StartSplit(paused ? "(Paused)ScanGrayAllocSpaceObjects" : - "ScanGrayAllocSpaceObjects"); - break; - } + case space::kGcRetentionPolicyNeverCollect: + name = paused ? "(Paused)ScanGrayImageSpaceObjects" : "ScanGrayImageSpaceObjects"; + break; + case space::kGcRetentionPolicyFullCollect: + name = paused ? "(Paused)ScanGrayZygoteSpaceObjects" : "ScanGrayZygoteSpaceObjects"; + break; + case space::kGcRetentionPolicyAlwaysCollect: + name = paused ? "(Paused)ScanGrayAllocSpaceObjects" : "ScanGrayAllocSpaceObjects"; + break; + default: + LOG(FATAL) << "Unreachable"; + } + TimingLogger::ScopedTiming t(name, GetTimings()); ScanObjectVisitor visitor(this); - card_table->Scan(space->GetMarkBitmap(), space->Begin(), space->End(), visitor, minimum_age); - timings_.EndSplit(); + card_table->Scan(space->GetMarkBitmap(), space->Begin(), space->End(), visitor, + minimum_age); } } } @@ -842,9 +825,7 @@ class RecursiveMarkTask : public MarkStackTask<false> { public: RecursiveMarkTask(ThreadPool* thread_pool, MarkSweep* mark_sweep, accounting::ContinuousSpaceBitmap* bitmap, uintptr_t begin, uintptr_t end) - : MarkStackTask<false>(thread_pool, mark_sweep, 0, NULL), - bitmap_(bitmap), - begin_(begin), + : MarkStackTask<false>(thread_pool, mark_sweep, 0, NULL), bitmap_(bitmap), begin_(begin), end_(end) { } @@ -869,7 +850,7 @@ class RecursiveMarkTask : public MarkStackTask<false> { // Populates the mark stack based on the set of marked objects and // recursively marks until the mark stack is emptied. void MarkSweep::RecursiveMark() { - TimingLogger::ScopedSplit split("RecursiveMark", &timings_); + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); // RecursiveMark will build the lists of known instances of the Reference classes. See // DelayReferenceReferent for details. if (kUseRecursiveMark) { @@ -936,25 +917,22 @@ void MarkSweep::RecursiveMarkDirtyObjects(bool paused, byte minimum_age) { } void MarkSweep::ReMarkRoots() { + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current()); - timings_.StartSplit("(Paused)ReMarkRoots"); Runtime::Current()->VisitRoots( MarkRootCallback, this, static_cast<VisitRootFlags>(kVisitRootFlagNewRoots | kVisitRootFlagStopLoggingNewRoots | kVisitRootFlagClearRootLog)); - timings_.EndSplit(); if (kVerifyRootsMarked) { - timings_.StartSplit("(Paused)VerifyRoots"); + TimingLogger::ScopedTiming t("(Paused)VerifyRoots", GetTimings()); Runtime::Current()->VisitRoots(VerifyRootMarked, this); - timings_.EndSplit(); } } void MarkSweep::SweepSystemWeaks(Thread* self) { + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - timings_.StartSplit("SweepSystemWeaks"); Runtime::Current()->SweepSystemWeaks(IsMarkedCallback, this); - timings_.EndSplit(); } mirror::Object* MarkSweep::VerifySystemWeakIsLiveCallback(Object* obj, void* arg) { @@ -975,6 +953,7 @@ void MarkSweep::VerifyIsLive(const Object* obj) { } void MarkSweep::VerifySystemWeaks() { + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); // Verify system weaks, uses a special object visitor which returns the input object. Runtime::Current()->SweepSystemWeaks(VerifySystemWeakIsLiveCallback, this); } @@ -1011,8 +990,8 @@ class CheckpointMarkThreadRoots : public Closure { void MarkSweep::MarkRootsCheckpoint(Thread* self, bool revoke_ros_alloc_thread_local_buffers_at_checkpoint) { + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); CheckpointMarkThreadRoots check_point(this, revoke_ros_alloc_thread_local_buffers_at_checkpoint); - timings_.StartSplit("MarkRootsCheckpoint"); ThreadList* thread_list = Runtime::Current()->GetThreadList(); // Request the check point is run on all threads returning a count of the threads that must // run through the barrier including self. @@ -1027,19 +1006,16 @@ void MarkSweep::MarkRootsCheckpoint(Thread* self, } Locks::mutator_lock_->SharedLock(self); Locks::heap_bitmap_lock_->ExclusiveLock(self); - timings_.EndSplit(); } void MarkSweep::SweepArray(accounting::ObjectStack* allocations, bool swap_bitmaps) { - timings_.StartSplit("SweepArray"); + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); Thread* self = Thread::Current(); mirror::Object** chunk_free_buffer = reinterpret_cast<mirror::Object**>( sweep_array_free_buffer_mem_map_->BaseBegin()); size_t chunk_free_pos = 0; - size_t freed_bytes = 0; - size_t freed_large_object_bytes = 0; - size_t freed_objects = 0; - size_t freed_large_objects = 0; + ObjectBytePair freed; + ObjectBytePair freed_los; // How many objects are left in the array, modified after each space is swept. Object** objects = allocations->Begin(); size_t count = allocations->Size(); @@ -1080,10 +1056,9 @@ void MarkSweep::SweepArray(accounting::ObjectStack* allocations, bool swap_bitma // if needed. if (!mark_bitmap->Test(obj)) { if (chunk_free_pos >= kSweepArrayChunkFreeSize) { - timings_.StartSplit("FreeList"); - freed_objects += chunk_free_pos; - freed_bytes += alloc_space->FreeList(self, chunk_free_pos, chunk_free_buffer); - timings_.EndSplit(); + TimingLogger::ScopedTiming t("FreeList", GetTimings()); + freed.objects += chunk_free_pos; + freed.bytes += alloc_space->FreeList(self, chunk_free_pos, chunk_free_buffer); chunk_free_pos = 0; } chunk_free_buffer[chunk_free_pos++] = obj; @@ -1093,10 +1068,9 @@ void MarkSweep::SweepArray(accounting::ObjectStack* allocations, bool swap_bitma } } if (chunk_free_pos > 0) { - timings_.StartSplit("FreeList"); - freed_objects += chunk_free_pos; - freed_bytes += alloc_space->FreeList(self, chunk_free_pos, chunk_free_buffer); - timings_.EndSplit(); + TimingLogger::ScopedTiming t("FreeList", GetTimings()); + freed.objects += chunk_free_pos; + freed.bytes += alloc_space->FreeList(self, chunk_free_pos, chunk_free_buffer); chunk_free_pos = 0; } // All of the references which space contained are no longer in the allocation stack, update @@ -1117,58 +1091,47 @@ void MarkSweep::SweepArray(accounting::ObjectStack* allocations, bool swap_bitma continue; } if (!large_mark_objects->Test(obj)) { - ++freed_large_objects; - freed_large_object_bytes += large_object_space->Free(self, obj); + ++freed_los.objects; + freed_los.bytes += large_object_space->Free(self, obj); } } - timings_.EndSplit(); - - timings_.StartSplit("RecordFree"); - VLOG(heap) << "Freed " << freed_objects << "/" << count << " objects with size " - << PrettySize(freed_bytes); - RecordFree(freed_objects, freed_bytes); - RecordFreeLargeObjects(freed_large_objects, freed_large_object_bytes); - timings_.EndSplit(); - - timings_.StartSplit("ResetStack"); - allocations->Reset(); - timings_.EndSplit(); - + { + TimingLogger::ScopedTiming t("RecordFree", GetTimings()); + RecordFree(freed); + RecordFreeLOS(freed_los); + t.NewTiming("ResetStack"); + allocations->Reset(); + } sweep_array_free_buffer_mem_map_->MadviseDontNeedAndZero(); } void MarkSweep::Sweep(bool swap_bitmaps) { + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); // Ensure that nobody inserted items in the live stack after we swapped the stacks. CHECK_GE(live_stack_freeze_size_, GetHeap()->GetLiveStack()->Size()); - // Mark everything allocated since the last as GC live so that we can sweep concurrently, - // knowing that new allocations won't be marked as live. - timings_.StartSplit("MarkStackAsLive"); - accounting::ObjectStack* live_stack = heap_->GetLiveStack(); - heap_->MarkAllocStackAsLive(live_stack); - live_stack->Reset(); - timings_.EndSplit(); - - DCHECK(mark_stack_->IsEmpty()); + { + TimingLogger::ScopedTiming t2("MarkAllocStackAsLive", GetTimings()); + // Mark everything allocated since the last as GC live so that we can sweep concurrently, + // knowing that new allocations won't be marked as live. + accounting::ObjectStack* live_stack = heap_->GetLiveStack(); + heap_->MarkAllocStackAsLive(live_stack); + live_stack->Reset(); + DCHECK(mark_stack_->IsEmpty()); + } for (const auto& space : GetHeap()->GetContinuousSpaces()) { if (space->IsContinuousMemMapAllocSpace()) { space::ContinuousMemMapAllocSpace* alloc_space = space->AsContinuousMemMapAllocSpace(); - TimingLogger::ScopedSplit split( - alloc_space->IsZygoteSpace() ? "SweepZygoteSpace" : "SweepMallocSpace", &timings_); - size_t freed_objects = 0; - size_t freed_bytes = 0; - alloc_space->Sweep(swap_bitmaps, &freed_objects, &freed_bytes); - RecordFree(freed_objects, freed_bytes); + TimingLogger::ScopedTiming split( + alloc_space->IsZygoteSpace() ? "SweepZygoteSpace" : "SweepMallocSpace", GetTimings()); + RecordFree(alloc_space->Sweep(swap_bitmaps)); } } SweepLargeObjects(swap_bitmaps); } void MarkSweep::SweepLargeObjects(bool swap_bitmaps) { - TimingLogger::ScopedSplit split("SweepLargeObjects", &timings_); - size_t freed_objects = 0; - size_t freed_bytes = 0; - heap_->GetLargeObjectsSpace()->Sweep(swap_bitmaps, &freed_objects, &freed_bytes); - RecordFreeLargeObjects(freed_objects, freed_bytes); + TimingLogger::ScopedTiming split(__FUNCTION__, GetTimings()); + RecordFreeLOS(heap_->GetLargeObjectsSpace()->Sweep(swap_bitmaps)); } // Process the "referent" field in a java.lang.ref.Reference. If the referent has not yet been @@ -1236,7 +1199,7 @@ void MarkSweep::ProcessMarkStackParallel(size_t thread_count) { // Scan anything that's on the mark stack. void MarkSweep::ProcessMarkStack(bool paused) { - timings_.StartSplit(paused ? "(Paused)ProcessMarkStack" : "ProcessMarkStack"); + TimingLogger::ScopedTiming t(paused ? "(Paused)ProcessMarkStack" : __FUNCTION__, GetTimings()); size_t thread_count = GetThreadCount(paused); if (kParallelProcessMarkStack && thread_count > 1 && mark_stack_->Size() >= kMinimumParallelMarkStackSize) { @@ -1269,12 +1232,9 @@ void MarkSweep::ProcessMarkStack(bool paused) { ScanObject(obj); } } - timings_.EndSplit(); } -inline bool MarkSweep::IsMarked(const Object* object) const - SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { - DCHECK(object != nullptr); +inline bool MarkSweep::IsMarked(const Object* object) const { if (immune_region_.ContainsObject(object)) { return true; } @@ -1285,7 +1245,7 @@ inline bool MarkSweep::IsMarked(const Object* object) const } void MarkSweep::FinishPhase() { - TimingLogger::ScopedSplit split("FinishPhase", &timings_); + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); if (kCountScannedTypes) { VLOG(gc) << "MarkSweep scanned classes=" << class_count_.LoadRelaxed() << " arrays=" << array_count_.LoadRelaxed() << " other=" << other_count_.LoadRelaxed(); @@ -1322,9 +1282,8 @@ void MarkSweep::RevokeAllThreadLocalBuffers() { // not be in use. GetHeap()->AssertAllBumpPointerSpaceThreadLocalBuffersAreRevoked(); } else { - timings_.StartSplit("(Paused)RevokeAllThreadLocalBuffers"); + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); GetHeap()->RevokeAllThreadLocalBuffers(); - timings_.EndSplit(); } } diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index a44d8a1f90..2780099fe2 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -215,7 +215,8 @@ class MarkSweep : public GarbageCollector { protected: // Returns true if the object has its bit set in the mark bitmap. - bool IsMarked(const mirror::Object* object) const; + bool IsMarked(const mirror::Object* object) const + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); static mirror::Object* IsMarkedCallback(mirror::Object* object, void* arg) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index badf8b3501..cabfe2176c 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -41,22 +41,12 @@ #include "jni_internal.h" #include "mark_sweep-inl.h" #include "monitor.h" -#include "mirror/art_field.h" -#include "mirror/art_field-inl.h" -#include "mirror/class-inl.h" -#include "mirror/class_loader.h" -#include "mirror/dex_cache.h" #include "mirror/reference-inl.h" #include "mirror/object-inl.h" -#include "mirror/object_array.h" -#include "mirror/object_array-inl.h" #include "runtime.h" -#include "stack.h" #include "thread-inl.h" #include "thread_list.h" -#include "verifier/method_verifier.h" -using ::art::mirror::Class; using ::art::mirror::Object; namespace art { @@ -69,7 +59,7 @@ static constexpr size_t kBytesPromotedThreshold = 4 * MB; static constexpr size_t kLargeObjectBytesAllocatedThreshold = 16 * MB; void SemiSpace::BindBitmaps() { - timings_.StartSplit("BindBitmaps"); + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); WriterMutexLock mu(self_, *Locks::heap_bitmap_lock_); // Mark all of the spaces we never collect as immune. for (const auto& space : GetHeap()->GetContinuousSpaces()) { @@ -93,7 +83,6 @@ void SemiSpace::BindBitmaps() { // We won't collect the large object space if a bump pointer space only collection. is_large_object_space_immune_ = true; } - timings_.EndSplit(); } SemiSpace::SemiSpace(Heap* heap, bool generational, const std::string& name_prefix) @@ -141,7 +130,7 @@ void SemiSpace::RunPhases() { } void SemiSpace::InitializePhase() { - TimingLogger::ScopedSplit split("InitializePhase", &timings_); + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); mark_stack_ = heap_->GetMarkStack(); DCHECK(mark_stack_ != nullptr); immune_region_.Reset(); @@ -161,14 +150,14 @@ void SemiSpace::InitializePhase() { } void SemiSpace::ProcessReferences(Thread* self) { - TimingLogger::ScopedSplit split("ProcessReferences", &timings_); WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); GetHeap()->GetReferenceProcessor()->ProcessReferences( - false, &timings_, clear_soft_references_, &HeapReferenceMarkedCallback, - &MarkObjectCallback, &ProcessMarkStackCallback, this); + false, GetTimings(), GetCurrentIteration()->GetClearSoftReferences(), + &HeapReferenceMarkedCallback, &MarkObjectCallback, &ProcessMarkStackCallback, this); } void SemiSpace::MarkingPhase() { + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); CHECK(Locks::mutator_lock_->IsExclusiveHeld(self_)); if (kStoreStackTraces) { Locks::mutator_lock_->AssertExclusiveHeld(self_); @@ -186,8 +175,9 @@ void SemiSpace::MarkingPhase() { // to prevent fragmentation. RevokeAllThreadLocalBuffers(); if (generational_) { - if (gc_cause_ == kGcCauseExplicit || gc_cause_ == kGcCauseForNativeAlloc || - clear_soft_references_) { + if (GetCurrentIteration()->GetGcCause() == kGcCauseExplicit || + GetCurrentIteration()->GetGcCause() == kGcCauseForNativeAlloc || + GetCurrentIteration()->GetClearSoftReferences()) { // If an explicit, native allocation-triggered, or last attempt // collection, collect the whole heap. whole_heap_collection_ = true; @@ -201,21 +191,12 @@ void SemiSpace::MarkingPhase() { } } - if (!clear_soft_references_) { - if (!generational_) { - // If non-generational, always clear soft references. - clear_soft_references_ = true; - } else { - // If generational, clear soft references if a whole heap collection. - if (whole_heap_collection_) { - clear_soft_references_ = true; - } - } + if (!generational_ || whole_heap_collection_) { + // If non-generational, always clear soft references. + // If generational, clear soft references if a whole heap collection. + GetCurrentIteration()->SetClearSoftReferences(true); } - Locks::mutator_lock_->AssertExclusiveHeld(self_); - - TimingLogger::ScopedSplit split("MarkingPhase", &timings_); if (generational_) { // If last_gc_to_space_end_ is out of the bounds of the from-space // (the to-space from last GC), then point it to the beginning of @@ -230,15 +211,16 @@ void SemiSpace::MarkingPhase() { // Assume the cleared space is already empty. BindBitmaps(); // Process dirty cards and add dirty cards to mod-union tables. - heap_->ProcessCards(timings_, kUseRememberedSet && generational_); + heap_->ProcessCards(GetTimings(), kUseRememberedSet && generational_); // Clear the whole card table since we can not Get any additional dirty cards during the // paused GC. This saves memory but only works for pause the world collectors. - timings_.NewSplit("ClearCardTable"); + 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. - timings_.NewSplit("SwapStacks"); + t.NewTiming("SwapStacks"); if (kUseThreadLocalAllocationStack) { + TimingLogger::ScopedTiming t("RevokeAllThreadLocalAllocationStacks", GetTimings()); heap_->RevokeAllThreadLocalAllocationStacks(self_); } heap_->SwapStacks(self_); @@ -255,7 +237,6 @@ void SemiSpace::MarkingPhase() { ReaderMutexLock mu(self_, *Locks::heap_bitmap_lock_); SweepSystemWeaks(); } - timings_.NewSplit("RecordFree"); // Revoke buffers before measuring how many objects were moved since the TLABs need to be revoked // before they are properly counted. RevokeAllThreadLocalBuffers(); @@ -267,14 +248,12 @@ void SemiSpace::MarkingPhase() { CHECK_LE(to_objects, from_objects); // Note: Freed bytes can be negative if we copy form a compacted space to a free-list backed // space. - RecordFree(from_objects - to_objects, from_bytes - to_bytes); + RecordFree(ObjectBytePair(from_objects - to_objects, from_bytes - to_bytes)); // Clear and protect the from space. from_space_->Clear(); VLOG(heap) << "Protecting from_space_: " << *from_space_; from_space_->GetMemMap()->Protect(kProtectFromSpace ? PROT_NONE : PROT_READ); - timings_.StartSplit("PreSweepingGcVerification"); heap_->PreSweepingGcVerification(this); - timings_.EndSplit(); if (swap_semi_spaces_) { heap_->SwapSemiSpaces(); } @@ -287,10 +266,10 @@ void SemiSpace::UpdateAndMarkModUnion() { accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space); if (table != nullptr) { // TODO: Improve naming. - TimingLogger::ScopedSplit split( + TimingLogger::ScopedTiming t( space->IsZygoteSpace() ? "UpdateAndMarkZygoteModUnionTable" : "UpdateAndMarkImageModUnionTable", - &timings_); + GetTimings()); table->UpdateAndMarkReferences(MarkHeapReferenceCallback, this); } else if (heap_->FindRememberedSetFromSpace(space) != nullptr) { DCHECK(kUseRememberedSet); @@ -369,12 +348,14 @@ class SemiSpaceVerifyNoFromSpaceReferencesObjectVisitor { }; void SemiSpace::MarkReachableObjects() { - timings_.StartSplit("MarkStackAsLive"); - accounting::ObjectStack* live_stack = heap_->GetLiveStack(); - heap_->MarkAllocStackAsLive(live_stack); - live_stack->Reset(); - - timings_.NewSplit("UpdateAndMarkRememberedSets"); + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); + { + TimingLogger::ScopedTiming t2("MarkStackAsLive", GetTimings()); + accounting::ObjectStack* live_stack = heap_->GetLiveStack(); + heap_->MarkAllocStackAsLive(live_stack); + live_stack->Reset(); + } + t.NewTiming("UpdateAndMarkRememberedSets"); for (auto& space : heap_->GetContinuousSpaces()) { // If the space is immune and has no mod union table (the // non-moving space when the bump pointer space only collection is @@ -413,7 +394,7 @@ void SemiSpace::MarkReachableObjects() { } if (is_large_object_space_immune_) { - timings_.NewSplit("VisitLargeObjects"); + TimingLogger::ScopedTiming t("VisitLargeObjects", GetTimings()); DCHECK(generational_ && !whole_heap_collection_); // Delay copying the live set to the marked set until here from // BindBitmaps() as the large objects on the allocation stack may @@ -431,31 +412,24 @@ void SemiSpace::MarkReachableObjects() { reinterpret_cast<uintptr_t>(large_object_space->End()), visitor); } - timings_.EndSplit(); // Recursively process the mark stack. ProcessMarkStack(); } void SemiSpace::ReclaimPhase() { - TimingLogger::ScopedSplit split("ReclaimPhase", &timings_); - { - WriterMutexLock mu(self_, *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. - timings_.StartSplit("SwapBitmaps"); - SwapBitmaps(); - timings_.EndSplit(); - // Unbind the live and mark bitmaps. - TimingLogger::ScopedSplit split("UnBindBitmaps", &timings_); - GetHeap()->UnBindBitmaps(); - } + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); + WriterMutexLock mu(self_, *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(); + // Unbind the live and mark bitmaps. + GetHeap()->UnBindBitmaps(); if (saved_bytes_ > 0) { VLOG(heap) << "Avoided dirtying " << PrettySize(saved_bytes_); } - if (generational_) { // Record the end (top) of the to space so we can distinguish // between objects that were allocated since the last GC and the @@ -644,8 +618,7 @@ void SemiSpace::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id* // Marks all objects in the root set. void SemiSpace::MarkRoots() { - timings_.NewSplit("MarkRoots"); - // TODO: Visit up image roots as well? + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); Runtime::Current()->VisitRoots(MarkRootCallback, this); } @@ -670,9 +643,8 @@ mirror::Object* SemiSpace::MarkedForwardingAddressCallback(mirror::Object* objec } void SemiSpace::SweepSystemWeaks() { - timings_.StartSplit("SweepSystemWeaks"); + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); Runtime::Current()->SweepSystemWeaks(MarkedForwardingAddressCallback, this); - timings_.EndSplit(); } bool SemiSpace::ShouldSweepSpace(space::ContinuousSpace* space) const { @@ -680,20 +652,17 @@ bool SemiSpace::ShouldSweepSpace(space::ContinuousSpace* space) const { } void SemiSpace::Sweep(bool swap_bitmaps) { + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); DCHECK(mark_stack_->IsEmpty()); - TimingLogger::ScopedSplit split("Sweep", &timings_); for (const auto& space : GetHeap()->GetContinuousSpaces()) { if (space->IsContinuousMemMapAllocSpace()) { space::ContinuousMemMapAllocSpace* alloc_space = space->AsContinuousMemMapAllocSpace(); if (!ShouldSweepSpace(alloc_space)) { continue; } - TimingLogger::ScopedSplit split( - alloc_space->IsZygoteSpace() ? "SweepZygoteSpace" : "SweepAllocSpace", &timings_); - size_t freed_objects = 0; - size_t freed_bytes = 0; - alloc_space->Sweep(swap_bitmaps, &freed_objects, &freed_bytes); - RecordFree(freed_objects, freed_bytes); + TimingLogger::ScopedTiming split( + alloc_space->IsZygoteSpace() ? "SweepZygoteSpace" : "SweepAllocSpace", GetTimings()); + RecordFree(alloc_space->Sweep(swap_bitmaps)); } } if (!is_large_object_space_immune_) { @@ -703,11 +672,8 @@ void SemiSpace::Sweep(bool swap_bitmaps) { void SemiSpace::SweepLargeObjects(bool swap_bitmaps) { DCHECK(!is_large_object_space_immune_); - TimingLogger::ScopedSplit split("SweepLargeObjects", &timings_); - size_t freed_objects = 0; - size_t freed_bytes = 0; - heap_->GetLargeObjectsSpace()->Sweep(swap_bitmaps, &freed_objects, &freed_bytes); - RecordFreeLargeObjects(freed_objects, freed_bytes); + TimingLogger::ScopedTiming split("SweepLargeObjects", GetTimings()); + RecordFreeLOS(heap_->GetLargeObjectsSpace()->Sweep(swap_bitmaps)); } // Process the "referent" field in a java.lang.ref.Reference. If the referent has not yet been @@ -747,6 +713,7 @@ void SemiSpace::ScanObject(Object* obj) { // Scan anything that's on the mark stack. void SemiSpace::ProcessMarkStack() { + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); space::MallocSpace* promo_dest_space = nullptr; accounting::ContinuousSpaceBitmap* live_bitmap = nullptr; if (generational_ && !whole_heap_collection_) { @@ -760,7 +727,6 @@ void SemiSpace::ProcessMarkStack() { DCHECK(mark_bitmap != nullptr); DCHECK_EQ(live_bitmap, mark_bitmap); } - timings_.StartSplit("ProcessMarkStack"); while (!mark_stack_->IsEmpty()) { Object* obj = mark_stack_->PopBack(); if (generational_ && !whole_heap_collection_ && promo_dest_space->HasAddress(obj)) { @@ -771,7 +737,6 @@ void SemiSpace::ProcessMarkStack() { } ScanObject(obj); } - timings_.EndSplit(); } inline Object* SemiSpace::GetMarkedForwardAddress(mirror::Object* obj) const @@ -788,7 +753,7 @@ inline Object* SemiSpace::GetMarkedForwardAddress(mirror::Object* obj) const // Already forwarded, must be marked. return obj; } - return heap_->GetMarkBitmap()->Test(obj) ? obj : nullptr; + return mark_bitmap_->Test(obj) ? obj : nullptr; } void SemiSpace::SetToSpace(space::ContinuousMemMapAllocSpace* to_space) { @@ -802,7 +767,7 @@ void SemiSpace::SetFromSpace(space::ContinuousMemMapAllocSpace* from_space) { } void SemiSpace::FinishPhase() { - TimingLogger::ScopedSplit split("FinishPhase", &timings_); + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); // Null the "to" and "from" spaces since compacting from one to the other isn't valid until // further action is done by the heap. to_space_ = nullptr; @@ -843,9 +808,8 @@ void SemiSpace::FinishPhase() { } void SemiSpace::RevokeAllThreadLocalBuffers() { - timings_.StartSplit("(Paused)RevokeAllThreadLocalBuffers"); + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); GetHeap()->RevokeAllThreadLocalBuffers(); - timings_.EndSplit(); } } // namespace collector diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h index c0a6b6a59c..530a3c9e72 100644 --- a/runtime/gc/collector_type.h +++ b/runtime/gc/collector_type.h @@ -34,6 +34,8 @@ enum CollectorType { kCollectorTypeSS, // A generational variant of kCollectorTypeSS. kCollectorTypeGSS, + // Mark compact colector. + kCollectorTypeMC, // Heap trimming collector, doesn't do any actual collecting. kCollectorTypeHeapTrim, // A (mostly) concurrent copying collector. diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index e6a5380da1..696728ba9a 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -36,6 +36,7 @@ #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-inl.h" #include "gc/collector/partial_mark_sweep.h" #include "gc/collector/semi_space.h" @@ -331,9 +332,10 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max semi_space_collector_ = new collector::SemiSpace(this, generational, generational ? "generational" : ""); garbage_collectors_.push_back(semi_space_collector_); - concurrent_copying_collector_ = new collector::ConcurrentCopying(this); garbage_collectors_.push_back(concurrent_copying_collector_); + mark_compact_collector_ = new collector::MarkCompact(this); + garbage_collectors_.push_back(mark_compact_collector_); } if (GetImageSpace() != nullptr && main_space_ != nullptr) { @@ -864,7 +866,10 @@ void Heap::DoPendingTransitionOrTrim() { // about pauses. Runtime* runtime = Runtime::Current(); runtime->GetThreadList()->SuspendAll(); - runtime->GetMonitorList()->DeflateMonitors(); + uint64_t start_time = NanoTime(); + size_t count = runtime->GetMonitorList()->DeflateMonitors(); + VLOG(heap) << "Deflating " << count << " monitors took " + << PrettyDuration(NanoTime() - start_time); runtime->GetThreadList()->ResumeAll(); // Do a heap trim if it is needed. Trim(); @@ -1341,8 +1346,9 @@ void Heap::TransitionCollector(CollectorType collector_type) { << " -> " << static_cast<int>(collector_type); uint64_t start_time = NanoTime(); uint32_t before_allocated = num_bytes_allocated_.LoadSequentiallyConsistent(); - ThreadList* tl = Runtime::Current()->GetThreadList(); - Thread* self = Thread::Current(); + Runtime* const runtime = Runtime::Current(); + ThreadList* const tl = runtime->GetThreadList(); + Thread* const self = Thread::Current(); ScopedThreadStateChange tsc(self, kWaitingPerformingGc); Locks::mutator_lock_->AssertNotHeld(self); const bool copying_transition = @@ -1371,7 +1377,7 @@ void Heap::TransitionCollector(CollectorType collector_type) { } usleep(1000); } - if (Runtime::Current()->IsShuttingDown(self)) { + if (runtime->IsShuttingDown(self)) { // Don't allow heap transitions to happen if the runtime is shutting down since these can // cause objects to get finalized. FinishGC(self, collector::kGcTypeNone); @@ -1432,10 +1438,15 @@ 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_) { case kCollectorTypeCC: // Fall-through. + case kCollectorTypeMC: // Fall-through. case kCollectorTypeSS: // Fall-through. case kCollectorTypeGSS: { gc_plan_.push_back(collector::kGcTypeFull); @@ -1572,6 +1583,7 @@ class ZygoteCompactingCollector FINAL : public collector::SemiSpace { }; void Heap::UnBindBitmaps() { + TimingLogger::ScopedTiming t("UnBindBitmaps", GetCurrentGcIteration()->GetTimings()); for (const auto& space : GetContinuousSpaces()) { if (space->IsContinuousMemMapAllocSpace()) { space::ContinuousMemMapAllocSpace* alloc_space = space->AsContinuousMemMapAllocSpace(); @@ -1635,8 +1647,8 @@ void Heap::PreZygoteFork() { if (temp_space_ != nullptr) { CHECK(temp_space_->IsEmpty()); } - total_objects_freed_ever_ += semi_space_collector_->GetFreedObjects(); - total_bytes_freed_ever_ += semi_space_collector_->GetFreedBytes(); + total_objects_freed_ever_ += GetCurrentGcIteration()->GetFreedObjects(); + total_bytes_freed_ever_ += GetCurrentGcIteration()->GetFreedBytes(); // Update the end and write out image. non_moving_space_->SetEnd(target_space.End()); non_moving_space_->SetLimit(target_space.Limit()); @@ -1722,13 +1734,17 @@ void Heap::SwapSemiSpaces() { void Heap::Compact(space::ContinuousMemMapAllocSpace* target_space, space::ContinuousMemMapAllocSpace* source_space) { CHECK(kMovingCollector); - CHECK_NE(target_space, source_space) << "In-place compaction currently unsupported"; if (target_space != source_space) { // Don't swap spaces since this isn't a typical semi space collection. semi_space_collector_->SetSwapSemiSpaces(false); semi_space_collector_->SetFromSpace(source_space); semi_space_collector_->SetToSpace(target_space); semi_space_collector_->Run(kGcCauseCollectorTransition, false); + } 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); } } @@ -1792,21 +1808,30 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus if (compacting_gc) { DCHECK(current_allocator_ == kAllocatorTypeBumpPointer || current_allocator_ == kAllocatorTypeTLAB); - if (collector_type_ == kCollectorTypeSS || collector_type_ == kCollectorTypeGSS) { - gc_type = semi_space_collector_->GetGcType(); - semi_space_collector_->SetFromSpace(bump_pointer_space_); - semi_space_collector_->SetToSpace(temp_space_); - collector = semi_space_collector_; - semi_space_collector_->SetSwapSemiSpaces(true); - } else if (collector_type_ == kCollectorTypeCC) { - gc_type = concurrent_copying_collector_->GetGcType(); - collector = concurrent_copying_collector_; - } else { - LOG(FATAL) << "Unreachable - invalid collector type " << static_cast<size_t>(collector_type_); + switch (collector_type_) { + case kCollectorTypeSS: + // Fall-through. + case kCollectorTypeGSS: + semi_space_collector_->SetFromSpace(bump_pointer_space_); + semi_space_collector_->SetToSpace(temp_space_); + semi_space_collector_->SetSwapSemiSpaces(true); + collector = semi_space_collector_; + break; + 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<size_t>(collector_type_); + } + if (collector != mark_compact_collector_) { + temp_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); + CHECK(temp_space_->IsEmpty()); } - temp_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); - CHECK(temp_space_->IsEmpty()); - gc_type = collector::kGcTypeFull; + gc_type = collector::kGcTypeFull; // TODO: Not hard code this in. } else if (current_allocator_ == kAllocatorTypeRosAlloc || current_allocator_ == kAllocatorTypeDlMalloc) { collector = FindCollectorByGcType(gc_type); @@ -1817,17 +1842,17 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus << "Could not find garbage collector with collector_type=" << static_cast<size_t>(collector_type_) << " and gc_type=" << gc_type; collector->Run(gc_cause, clear_soft_references || runtime->IsZygote()); - total_objects_freed_ever_ += collector->GetFreedObjects(); - total_bytes_freed_ever_ += collector->GetFreedBytes(); + total_objects_freed_ever_ += GetCurrentGcIteration()->GetFreedObjects(); + total_bytes_freed_ever_ += GetCurrentGcIteration()->GetFreedBytes(); RequestHeapTrim(); // Enqueue cleared references. reference_processor_.EnqueueClearedReferences(self); // Grow the heap so that we know when to perform the next GC. GrowForUtilization(collector); - const size_t duration = collector->GetDurationNs(); - const std::vector<uint64_t>& pause_times = collector->GetPauseTimes(); + const size_t duration = GetCurrentGcIteration()->GetDurationNs(); + const std::vector<uint64_t>& pause_times = GetCurrentGcIteration()->GetPauseTimes(); // Print the GC if it is an explicit GC (e.g. Runtime.gc()) or a slow GC - // (mutator time blocked >= long_pause_log_threshold_). + // (mutator time blocked >= long_pause_log_threshold_). bool log_gc = gc_cause == kGcCauseExplicit; if (!log_gc && CareAboutPauseTimes()) { // GC for alloc pauses the allocating thread, so consider it as a pause. @@ -1847,14 +1872,14 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus << ((i != pause_times.size() - 1) ? "," : ""); } LOG(INFO) << gc_cause << " " << collector->GetName() - << " GC freed " << collector->GetFreedObjects() << "(" - << PrettySize(collector->GetFreedBytes()) << ") AllocSpace objects, " - << collector->GetFreedLargeObjects() << "(" - << PrettySize(collector->GetFreedLargeObjectBytes()) << ") LOS objects, " + << " GC freed " << current_gc_iteration_.GetFreedObjects() << "(" + << PrettySize(current_gc_iteration_.GetFreedBytes()) << ") AllocSpace objects, " + << current_gc_iteration_.GetFreedLargeObjects() << "(" + << PrettySize(current_gc_iteration_.GetFreedLargeObjectBytes()) << ") LOS objects, " << percent_free << "% free, " << PrettySize(current_heap_size) << "/" << PrettySize(total_memory) << ", " << "paused " << pause_string.str() << " total " << PrettyDuration((duration / 1000) * 1000); - VLOG(heap) << ConstDumpable<TimingLogger>(collector->GetTimings()); + VLOG(heap) << ConstDumpable<TimingLogger>(*current_gc_iteration_.GetTimings()); } FinishGC(self, gc_type); // Inform DDMS that a GC completed. @@ -2292,7 +2317,8 @@ accounting::RememberedSet* Heap::FindRememberedSetFromSpace(space::Space* space) return it->second; } -void Heap::ProcessCards(TimingLogger& timings, bool use_rem_sets) { +void Heap::ProcessCards(TimingLogger* timings, bool use_rem_sets) { + TimingLogger::ScopedTiming t(__FUNCTION__, timings); // Clear cards and keep track of cards cleared in the mod-union table. for (const auto& space : continuous_spaces_) { accounting::ModUnionTable* table = FindModUnionTableFromSpace(space); @@ -2300,15 +2326,15 @@ void Heap::ProcessCards(TimingLogger& timings, bool use_rem_sets) { if (table != nullptr) { const char* name = space->IsZygoteSpace() ? "ZygoteModUnionClearCards" : "ImageModUnionClearCards"; - TimingLogger::ScopedSplit split(name, &timings); + TimingLogger::ScopedTiming t(name, timings); table->ClearCards(); } else if (use_rem_sets && rem_set != nullptr) { DCHECK(collector::SemiSpace::kUseRememberedSet && collector_type_ == kCollectorTypeGSS) << static_cast<int>(collector_type_); - TimingLogger::ScopedSplit split("AllocSpaceRemSetClearCards", &timings); + TimingLogger::ScopedTiming t("AllocSpaceRemSetClearCards", timings); rem_set->ClearCards(); } else if (space->GetType() != space::kSpaceTypeBumpPointerSpace) { - TimingLogger::ScopedSplit split("AllocSpaceClearCards", &timings); + TimingLogger::ScopedTiming t("AllocSpaceClearCards", timings); // No mod union table for the AllocSpace. Age the cards so that the GC knows that these cards // were dirty before the GC started. // TODO: Need to use atomic for the case where aged(cleaning thread) -> dirty(other thread) @@ -2316,7 +2342,8 @@ void Heap::ProcessCards(TimingLogger& timings, bool use_rem_sets) { // The races are we either end up with: Aged card, unaged card. Since we have the checkpoint // roots and then we scan / update mod union tables after. We will always scan either card. // If we end up with the non aged card, we scan it it in the pause. - card_table_->ModifyCardsAtomic(space->Begin(), space->End(), AgeCardVisitor(), VoidFunctor()); + card_table_->ModifyCardsAtomic(space->Begin(), space->End(), AgeCardVisitor(), + VoidFunctor()); } } } @@ -2326,9 +2353,10 @@ static void IdentityMarkHeapReferenceCallback(mirror::HeapReference<mirror::Obje void Heap::PreGcVerificationPaused(collector::GarbageCollector* gc) { Thread* const self = Thread::Current(); - TimingLogger* const timings = &gc->GetTimings(); + TimingLogger* const timings = current_gc_iteration_.GetTimings(); + TimingLogger::ScopedTiming t(__FUNCTION__, timings); if (verify_pre_gc_heap_) { - TimingLogger::ScopedSplit split("PreGcVerifyHeapReferences", timings); + TimingLogger::ScopedTiming t("(Paused)PreGcVerifyHeapReferences", timings); ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); size_t failures = VerifyHeapReferences(); if (failures > 0) { @@ -2338,7 +2366,7 @@ void Heap::PreGcVerificationPaused(collector::GarbageCollector* gc) { } // Check that all objects which reference things in the live stack are on dirty cards. if (verify_missing_card_marks_) { - TimingLogger::ScopedSplit split("PreGcVerifyMissingCardMarks", timings); + TimingLogger::ScopedTiming t("(Paused)PreGcVerifyMissingCardMarks", timings); ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); SwapStacks(self); // Sort the live stack so that we can quickly binary search it later. @@ -2348,7 +2376,7 @@ void Heap::PreGcVerificationPaused(collector::GarbageCollector* gc) { SwapStacks(self); } if (verify_mod_union_table_) { - TimingLogger::ScopedSplit split("PreGcVerifyModUnionTables", timings); + TimingLogger::ScopedTiming t("(Paused)PreGcVerifyModUnionTables", timings); ReaderMutexLock reader_lock(self, *Locks::heap_bitmap_lock_); for (const auto& table_pair : mod_union_tables_) { accounting::ModUnionTable* mod_union_table = table_pair.second; @@ -2368,17 +2396,18 @@ void Heap::PreGcVerification(collector::GarbageCollector* gc) { void Heap::PrePauseRosAllocVerification(collector::GarbageCollector* gc) { // TODO: Add a new runtime option for this? if (verify_pre_gc_rosalloc_) { - RosAllocVerification(&gc->GetTimings(), "PreGcRosAllocVerification"); + RosAllocVerification(current_gc_iteration_.GetTimings(), "PreGcRosAllocVerification"); } } void Heap::PreSweepingGcVerification(collector::GarbageCollector* gc) { Thread* const self = Thread::Current(); - TimingLogger* const timings = &gc->GetTimings(); + TimingLogger* const timings = current_gc_iteration_.GetTimings(); + TimingLogger::ScopedTiming t(__FUNCTION__, timings); // Called before sweeping occurs since we want to make sure we are not going so reclaim any // reachable objects. if (verify_pre_sweeping_heap_) { - TimingLogger::ScopedSplit split("PostSweepingVerifyHeapReferences", timings); + TimingLogger::ScopedTiming t("(Paused)PostSweepingVerifyHeapReferences", timings); CHECK_NE(self->GetState(), kRunnable); WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); // Swapping bound bitmaps does nothing. @@ -2400,17 +2429,18 @@ void Heap::PreSweepingGcVerification(collector::GarbageCollector* gc) { void Heap::PostGcVerificationPaused(collector::GarbageCollector* gc) { // Only pause if we have to do some verification. Thread* const self = Thread::Current(); - TimingLogger* const timings = &gc->GetTimings(); + TimingLogger* const timings = GetCurrentGcIteration()->GetTimings(); + TimingLogger::ScopedTiming t(__FUNCTION__, timings); if (verify_system_weaks_) { ReaderMutexLock mu2(self, *Locks::heap_bitmap_lock_); collector::MarkSweep* mark_sweep = down_cast<collector::MarkSweep*>(gc); mark_sweep->VerifySystemWeaks(); } if (verify_post_gc_rosalloc_) { - RosAllocVerification(timings, "PostGcRosAllocVerification"); + RosAllocVerification(timings, "(Paused)PostGcRosAllocVerification"); } if (verify_post_gc_heap_) { - TimingLogger::ScopedSplit split("PostGcVerifyHeapReferences", timings); + TimingLogger::ScopedTiming t("(Paused)PostGcVerifyHeapReferences", timings); ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); size_t failures = VerifyHeapReferences(); if (failures > 0) { @@ -2428,7 +2458,7 @@ void Heap::PostGcVerification(collector::GarbageCollector* gc) { } void Heap::RosAllocVerification(TimingLogger* timings, const char* name) { - TimingLogger::ScopedSplit split(name, timings); + TimingLogger::ScopedTiming t(name, timings); for (const auto& space : continuous_spaces_) { if (space->IsRosAllocSpace()) { VLOG(heap) << name << " : " << space->GetName(); @@ -2554,9 +2584,9 @@ void Heap::GrowForUtilization(collector::GarbageCollector* collector_ran) { // We also check that the bytes allocated aren't over the footprint limit in order to prevent a // pathological case where dead objects which aren't reclaimed by sticky could get accumulated // if the sticky GC throughput always remained >= the full/partial throughput. - if (collector_ran->GetEstimatedLastIterationThroughput() * kStickyGcThroughputAdjustment >= + if (current_gc_iteration_.GetEstimatedThroughput() * kStickyGcThroughputAdjustment >= non_sticky_collector->GetEstimatedMeanThroughput() && - non_sticky_collector->GetIterations() > 0 && + non_sticky_collector->NumberOfIterations() > 0 && bytes_allocated <= max_allowed_footprint_) { next_gc_type_ = collector::kGcTypeSticky; } else { @@ -2574,7 +2604,7 @@ void Heap::GrowForUtilization(collector::GarbageCollector* collector_ran) { if (IsGcConcurrent()) { // Calculate when to perform the next ConcurrentGC. // Calculate the estimated GC duration. - const double gc_duration_seconds = NsToMs(collector_ran->GetDurationNs()) / 1000.0; + const double gc_duration_seconds = NsToMs(current_gc_iteration_.GetDurationNs()) / 1000.0; // Estimate how many remaining bytes we will have when we need to start the next GC. size_t remaining_bytes = allocation_rate_ * gc_duration_seconds; remaining_bytes = std::min(remaining_bytes, kMaxConcurrentRemainingBytes); diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 9b493736dc..6d70a38765 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -27,6 +27,7 @@ #include "gc/accounting/atomic_stack.h" #include "gc/accounting/card_table.h" #include "gc/gc_cause.h" +#include "gc/collector/garbage_collector.h" #include "gc/collector/gc_type.h" #include "gc/collector_type.h" #include "globals.h" @@ -66,6 +67,7 @@ namespace accounting { namespace collector { class ConcurrentCopying; class GarbageCollector; + class MarkCompact; class MarkSweep; class SemiSpace; } // namespace collector @@ -316,6 +318,13 @@ class Heap { return discontinuous_spaces_; } + const collector::Iteration* GetCurrentGcIteration() const { + return ¤t_gc_iteration_; + } + collector::Iteration* GetCurrentGcIteration() { + return ¤t_gc_iteration_; + } + // Enable verification of object references when the runtime is sufficiently initialized. void EnableObjectValidation() { verify_object_mode_ = kVerifyObjectSupport; @@ -408,7 +417,10 @@ class Heap { // Implements java.lang.Runtime.freeMemory. size_t GetFreeMemory() const { - return max_allowed_footprint_ - num_bytes_allocated_.LoadSequentiallyConsistent(); + size_t byte_allocated = num_bytes_allocated_.LoadSequentiallyConsistent(); + // Make sure we don't get a negative number since the max allowed footprint is only updated + // after the GC. But we can still allocate even if bytes_allocated > max_allowed_footprint_. + return std::max(max_allowed_footprint_, byte_allocated) - byte_allocated; } // get the space that corresponds to an object's address. Current implementation searches all @@ -573,7 +585,7 @@ class Heap { } static bool IsMovingGc(CollectorType collector_type) { return collector_type == kCollectorTypeSS || collector_type == kCollectorTypeGSS || - collector_type == kCollectorTypeCC; + collector_type == kCollectorTypeCC || collector_type == kCollectorTypeMC; } bool ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -689,7 +701,7 @@ class Heap { void SwapStacks(Thread* self); // Clear cards and update the mod union table. - void ProcessCards(TimingLogger& timings, bool use_rem_sets); + void ProcessCards(TimingLogger* timings, bool use_rem_sets); // Signal the heap trim daemon that there is something to do, either a heap transition or heap // trim. @@ -848,6 +860,9 @@ class Heap { // Data structure GC overhead. Atomic<size_t> gc_memory_overhead_; + // Info related to the current or previous GC iteration. + collector::Iteration current_gc_iteration_; + // Heap verification flags. const bool verify_missing_card_marks_; const bool verify_system_weaks_; @@ -952,12 +967,14 @@ class Heap { std::vector<collector::GarbageCollector*> garbage_collectors_; collector::SemiSpace* semi_space_collector_; + collector::MarkCompact* mark_compact_collector_; collector::ConcurrentCopying* concurrent_copying_collector_; const bool running_on_valgrind_; const bool use_tlab_; friend class collector::GarbageCollector; + friend class collector::MarkCompact; friend class collector::MarkSweep; friend class collector::SemiSpace; friend class ReferenceQueue; diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc index 3ff9889e3c..e52bc1fd1e 100644 --- a/runtime/gc/reference_processor.cc +++ b/runtime/gc/reference_processor.cc @@ -110,6 +110,7 @@ void ReferenceProcessor::ProcessReferences(bool concurrent, TimingLogger* timing MarkObjectCallback* mark_object_callback, ProcessMarkStackCallback* process_mark_stack_callback, void* arg) { + TimingLogger::ScopedTiming t(concurrent ? __FUNCTION__ : "(Paused)ProcessReferences", timings); Thread* self = Thread::Current(); { MutexLock mu(self, lock_); @@ -118,10 +119,9 @@ void ReferenceProcessor::ProcessReferences(bool concurrent, TimingLogger* timing process_references_args_.arg_ = arg; CHECK_EQ(slow_path_enabled_, concurrent) << "Slow path must be enabled iff concurrent"; } - timings->StartSplit(concurrent ? "ProcessReferences" : "(Paused)ProcessReferences"); // Unless required to clear soft references with white references, preserve some white referents. if (!clear_soft_references) { - TimingLogger::ScopedSplit split(concurrent ? "ForwardSoftReferences" : + TimingLogger::ScopedTiming split(concurrent ? "ForwardSoftReferences" : "(Paused)ForwardSoftReferences", timings); if (concurrent) { StartPreservingReferences(self); @@ -138,7 +138,7 @@ void ReferenceProcessor::ProcessReferences(bool concurrent, TimingLogger* timing soft_reference_queue_.ClearWhiteReferences(&cleared_references_, is_marked_callback, arg); weak_reference_queue_.ClearWhiteReferences(&cleared_references_, is_marked_callback, arg); { - TimingLogger::ScopedSplit split(concurrent ? "EnqueueFinalizerReferences" : + TimingLogger::ScopedTiming t(concurrent ? "EnqueueFinalizerReferences" : "(Paused)EnqueueFinalizerReferences", timings); if (concurrent) { StartPreservingReferences(self); @@ -173,7 +173,6 @@ void ReferenceProcessor::ProcessReferences(bool concurrent, TimingLogger* timing DisableSlowPath(self); } } - timings->EndSplit(); } // Process the "referent" field in a java.lang.ref.Reference. If the referent has not yet been @@ -205,6 +204,10 @@ void ReferenceProcessor::DelayReferenceReferent(mirror::Class* klass, mirror::Re } } +void ReferenceProcessor::UpdateRoots(IsMarkedCallback* callback, void* arg) { + cleared_references_.UpdateRoots(callback, arg); +} + void ReferenceProcessor::EnqueueClearedReferences(Thread* self) { Locks::mutator_lock_->AssertNotHeld(self); if (!cleared_references_.IsEmpty()) { diff --git a/runtime/gc/reference_processor.h b/runtime/gc/reference_processor.h index ff7da52bdf..2771ea809b 100644 --- a/runtime/gc/reference_processor.h +++ b/runtime/gc/reference_processor.h @@ -59,6 +59,8 @@ class ReferenceProcessor { void DelayReferenceReferent(mirror::Class* klass, mirror::Reference* ref, IsHeapReferenceMarkedCallback* is_marked_callback, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void UpdateRoots(IsMarkedCallback* callback, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); private: class ProcessReferencesArgs { diff --git a/runtime/gc/reference_queue.cc b/runtime/gc/reference_queue.cc index 19476e62c2..c3931e8bdc 100644 --- a/runtime/gc/reference_queue.cc +++ b/runtime/gc/reference_queue.cc @@ -163,5 +163,11 @@ void ReferenceQueue::ForwardSoftReferences(IsHeapReferenceMarkedCallback* preser } while (LIKELY(ref != head)); } +void ReferenceQueue::UpdateRoots(IsMarkedCallback* callback, void* arg) { + if (list_ != nullptr) { + list_ = down_cast<mirror::Reference*>(callback(list_, arg)); + } +} + } // namespace gc } // namespace art diff --git a/runtime/gc/reference_queue.h b/runtime/gc/reference_queue.h index 8ef0d20925..cd814bbe57 100644 --- a/runtime/gc/reference_queue.h +++ b/runtime/gc/reference_queue.h @@ -83,12 +83,16 @@ class ReferenceQueue { mirror::Reference* GetList() { return list_; } + // Visits list_, currently only used for the mark compact GC. + void UpdateRoots(IsMarkedCallback* callback, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); private: // Lock, used for parallel GC reference enqueuing. It allows for multiple threads simultaneously // calling AtomicEnqueueIfNotEnqueued. Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - // The actual reference list. Not a root since it will be nullptr when the GC is not running. + // The actual reference list. Only a root for the mark compact GC since it will be null for other + // GC types. mirror::Reference* list_; }; diff --git a/runtime/gc/space/bump_pointer_space.h b/runtime/gc/space/bump_pointer_space.h index 9e61f300be..feee34f8bb 100644 --- a/runtime/gc/space/bump_pointer_space.h +++ b/runtime/gc/space/bump_pointer_space.h @@ -145,6 +145,12 @@ class BumpPointerSpace FINAL : public ContinuousMemMapAllocSpace { accounting::ContinuousSpaceBitmap::SweepCallback* GetSweepCallback() OVERRIDE; + // Record objects / bytes freed. + void RecordFree(int32_t objects, int32_t bytes) { + objects_allocated_.FetchAndSubSequentiallyConsistent(objects); + bytes_allocated_.FetchAndSubSequentiallyConsistent(bytes); + } + // Object alignment within the space. static constexpr size_t kAlignment = 8; diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc index 54a63f065d..abae8ff346 100644 --- a/runtime/gc/space/large_object_space.cc +++ b/runtime/gc/space/large_object_space.cc @@ -411,28 +411,24 @@ void LargeObjectSpace::SweepCallback(size_t num_ptrs, mirror::Object** ptrs, voi bitmap->Clear(ptrs[i]); } } - context->freed_objects += num_ptrs; - context->freed_bytes += space->FreeList(self, num_ptrs, ptrs); + context->freed.objects += num_ptrs; + context->freed.bytes += space->FreeList(self, num_ptrs, ptrs); } -void LargeObjectSpace::Sweep(bool swap_bitmaps, size_t* out_freed_objects, - size_t* out_freed_bytes) { +collector::ObjectBytePair LargeObjectSpace::Sweep(bool swap_bitmaps) { if (Begin() >= End()) { - return; + return collector::ObjectBytePair(0, 0); } accounting::LargeObjectBitmap* live_bitmap = GetLiveBitmap(); accounting::LargeObjectBitmap* mark_bitmap = GetMarkBitmap(); if (swap_bitmaps) { std::swap(live_bitmap, mark_bitmap); } - DCHECK(out_freed_objects != nullptr); - DCHECK(out_freed_bytes != nullptr); - SweepCallbackContext scc(swap_bitmaps, this); + AllocSpace::SweepCallbackContext scc(swap_bitmaps, this); accounting::LargeObjectBitmap::SweepWalk(*live_bitmap, *mark_bitmap, reinterpret_cast<uintptr_t>(Begin()), reinterpret_cast<uintptr_t>(End()), SweepCallback, &scc); - *out_freed_objects += scc.freed_objects; - *out_freed_bytes += scc.freed_bytes; + return scc.freed; } } // namespace space diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h index a84b43a8a1..01982d06ab 100644 --- a/runtime/gc/space/large_object_space.h +++ b/runtime/gc/space/large_object_space.h @@ -73,7 +73,7 @@ class LargeObjectSpace : public DiscontinuousSpace, public AllocSpace { return this; } - void Sweep(bool swap_bitmaps, size_t* out_freed_objects, size_t* out_freed_bytes); + collector::ObjectBytePair Sweep(bool swap_bitmaps); virtual bool CanMoveObjects() const OVERRIDE { return false; diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc index 57ed0bd35c..4d74f3c246 100644 --- a/runtime/gc/space/malloc_space.cc +++ b/runtime/gc/space/malloc_space.cc @@ -242,8 +242,8 @@ void MallocSpace::SweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* ar // Use a bulk free, that merges consecutive objects before freeing or free per object? // Documentation suggests better free performance with merging, but this may be at the expensive // of allocation. - context->freed_objects += num_ptrs; - context->freed_bytes += space->FreeList(self, num_ptrs, ptrs); + context->freed.objects += num_ptrs; + context->freed.bytes += space->FreeList(self, num_ptrs, ptrs); } } // namespace space diff --git a/runtime/gc/space/space.cc b/runtime/gc/space/space.cc index 4e2841691e..bff28f6d19 100644 --- a/runtime/gc/space/space.cc +++ b/runtime/gc/space/space.cc @@ -81,14 +81,12 @@ DiscontinuousSpace::DiscontinuousSpace(const std::string& name, CHECK(mark_bitmap_.get() != nullptr); } -void ContinuousMemMapAllocSpace::Sweep(bool swap_bitmaps, size_t* freed_objects, size_t* freed_bytes) { - DCHECK(freed_objects != nullptr); - DCHECK(freed_bytes != nullptr); +collector::ObjectBytePair ContinuousMemMapAllocSpace::Sweep(bool swap_bitmaps) { accounting::ContinuousSpaceBitmap* live_bitmap = GetLiveBitmap(); accounting::ContinuousSpaceBitmap* mark_bitmap = GetMarkBitmap(); // If the bitmaps are bound then sweeping this space clearly won't do anything. if (live_bitmap == mark_bitmap) { - return; + return collector::ObjectBytePair(0, 0); } SweepCallbackContext scc(swap_bitmaps, this); if (swap_bitmaps) { @@ -98,8 +96,7 @@ void ContinuousMemMapAllocSpace::Sweep(bool swap_bitmaps, size_t* freed_objects, accounting::ContinuousSpaceBitmap::SweepWalk( *live_bitmap, *mark_bitmap, reinterpret_cast<uintptr_t>(Begin()), reinterpret_cast<uintptr_t>(End()), GetSweepCallback(), reinterpret_cast<void*>(&scc)); - *freed_objects += scc.freed_objects; - *freed_bytes += scc.freed_bytes; + return scc.freed; } // Returns the old mark bitmap. @@ -136,9 +133,8 @@ void ContinuousMemMapAllocSpace::SwapBitmaps() { mark_bitmap_->SetName(temp_name); } -Space::SweepCallbackContext::SweepCallbackContext(bool swap_bitmaps, space::Space* space) - : swap_bitmaps(swap_bitmaps), space(space), self(Thread::Current()), freed_objects(0), - freed_bytes(0) { +AllocSpace::SweepCallbackContext::SweepCallbackContext(bool swap_bitmaps, space::Space* space) + : swap_bitmaps(swap_bitmaps), space(space), self(Thread::Current()) { } } // namespace space diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h index 8415fa18ad..8444a70b9c 100644 --- a/runtime/gc/space/space.h +++ b/runtime/gc/space/space.h @@ -23,6 +23,7 @@ #include "base/macros.h" #include "base/mutex.h" #include "gc/accounting/space_bitmap.h" +#include "gc/collector/garbage_collector.h" #include "globals.h" #include "image.h" #include "mem_map.h" @@ -172,16 +173,6 @@ class Space { std::string name_; protected: - struct SweepCallbackContext { - public: - SweepCallbackContext(bool swap_bitmaps, space::Space* space); - const bool swap_bitmaps; - space::Space* const space; - Thread* const self; - size_t freed_objects; - size_t freed_bytes; - }; - // When should objects within this space be reclaimed? Not constant as we vary it in the case // of Zygote forking. GcRetentionPolicy gc_retention_policy_; @@ -232,6 +223,14 @@ class AllocSpace { virtual void RevokeAllThreadLocalBuffers() = 0; protected: + struct SweepCallbackContext { + SweepCallbackContext(bool swap_bitmaps, space::Space* space); + const bool swap_bitmaps; + space::Space* const space; + Thread* const self; + collector::ObjectBytePair freed; + }; + AllocSpace() {} virtual ~AllocSpace() {} @@ -415,7 +414,7 @@ class ContinuousMemMapAllocSpace : public MemMapSpace, public AllocSpace { return mark_bitmap_.get(); } - void Sweep(bool swap_bitmaps, size_t* freed_objects, size_t* freed_bytes); + collector::ObjectBytePair Sweep(bool swap_bitmaps); virtual accounting::ContinuousSpaceBitmap::SweepCallback* GetSweepCallback() = 0; protected: diff --git a/runtime/globals.h b/runtime/globals.h index 58c2118e27..3a906f15f5 100644 --- a/runtime/globals.h +++ b/runtime/globals.h @@ -74,8 +74,11 @@ static constexpr bool kUsePortableCompiler = false; // Garbage collector constants. static constexpr bool kMovingCollector = true && !kUsePortableCompiler; +static constexpr bool kMarkCompactSupport = false && kMovingCollector; +// True if we allow moving field arrays, this can cause complication with mark compact. +static constexpr bool kMoveFieldArrays = !kMarkCompactSupport; // True if we allow moving classes. -static constexpr bool kMovingClasses = true; +static constexpr bool kMovingClasses = !kMarkCompactSupport; // True if we allow moving fields. static constexpr bool kMovingFields = false; // True if we allow moving methods. diff --git a/runtime/indirect_reference_table-inl.h b/runtime/indirect_reference_table-inl.h index b787233b37..f561643399 100644 --- a/runtime/indirect_reference_table-inl.h +++ b/runtime/indirect_reference_table-inl.h @@ -59,8 +59,7 @@ inline bool IndirectReferenceTable::GetChecked(IndirectRef iref) const { // Make sure that the entry at "idx" is correctly paired with "iref". inline bool IndirectReferenceTable::CheckEntry(const char* what, IndirectRef iref, int idx) const { - const mirror::Object* obj = table_[idx]; - IndirectRef checkRef = ToIndirectRef(obj, idx); + IndirectRef checkRef = ToIndirectRef(idx); if (UNLIKELY(checkRef != iref)) { LOG(ERROR) << "JNI ERROR (app bug): attempt to " << what << " stale " << kind_ << " " << iref diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc index 98e1d21d93..ad798ed60f 100644 --- a/runtime/indirect_reference_table.cc +++ b/runtime/indirect_reference_table.cc @@ -137,13 +137,13 @@ IndirectRef IndirectReferenceTable::Add(uint32_t cookie, mirror::Object* obj) { DCHECK_GE(pScan, table_ + prevState.parts.topIndex); } UpdateSlotAdd(obj, pScan - table_); - result = ToIndirectRef(obj, pScan - table_); + result = ToIndirectRef(pScan - table_); *pScan = obj; segment_state_.parts.numHoles--; } else { // Add to the end. UpdateSlotAdd(obj, topIndex); - result = ToIndirectRef(obj, topIndex); + result = ToIndirectRef(topIndex); table_[topIndex++] = obj; segment_state_.parts.topIndex = topIndex; } @@ -277,9 +277,6 @@ void IndirectReferenceTable::Dump(std::ostream& os) const { // while the read barrier won't. entries.push_back(obj); } else { - // We need a read barrier if weak globals. Since this is for - // debugging where performance isn't top priority, we - // unconditionally enable the read barrier, which is conservative. obj = ReadBarrier::BarrierForRoot<mirror::Object, kWithReadBarrier>(root); entries.push_back(obj); } diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h index 5b3ed685c7..b3a855dfb3 100644 --- a/runtime/indirect_reference_table.h +++ b/runtime/indirect_reference_table.h @@ -27,6 +27,7 @@ #include "mem_map.h" #include "object_callbacks.h" #include "offsets.h" +#include "read_barrier.h" namespace art { namespace mirror { @@ -215,6 +216,7 @@ class IrtIterator { } mirror::Object** operator*() { + // This does not have a read barrier as this is used to visit roots. return &table_[i_]; } @@ -298,6 +300,7 @@ class IndirectReferenceTable { return segment_state_.parts.topIndex; } + // Note IrtIterator does not have a read barrier as it's used to visit roots. IrtIterator begin() { return IrtIterator(table_, 0, Capacity()); } @@ -333,7 +336,7 @@ class IndirectReferenceTable { * The object pointer itself is subject to relocation in some GC * implementations, so we shouldn't really be using it here. */ - IndirectRef ToIndirectRef(const mirror::Object* /*o*/, uint32_t tableIndex) const { + IndirectRef ToIndirectRef(uint32_t tableIndex) const { DCHECK_LT(tableIndex, 65536U); uint32_t serialChunk = slot_data_[tableIndex].serial; uintptr_t uref = serialChunk << 20 | (tableIndex << 2) | kind_; @@ -368,9 +371,8 @@ class IndirectReferenceTable { std::unique_ptr<MemMap> table_mem_map_; // Mem map where we store the extended debugging info. std::unique_ptr<MemMap> slot_mem_map_; - // bottom of the stack. If a JNI weak global table, do not directly - // access the object references in this as they are weak roots. Use - // Get() that has a read barrier. + // bottom of the stack. Do not directly access the object references + // in this as they are roots. Use Get() that has a read barrier. mirror::Object** table_; /* bit mask, ORed into all irefs */ IndirectRefKind kind_; diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h index 1477324ca0..325b089a48 100644 --- a/runtime/jdwp/jdwp.h +++ b/runtime/jdwp/jdwp.h @@ -294,14 +294,14 @@ struct JdwpState { ObjectId threadId) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void CleanupMatchList(JdwpEvent** match_list, - int match_count) + size_t match_count) EXCLUSIVE_LOCKS_REQUIRED(event_list_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void EventFinish(ExpandBuf* pReq); void FindMatchingEvents(JdwpEventKind eventKind, - ModBasket* basket, + const ModBasket& basket, JdwpEvent** match_list, - int* pMatchCount) + size_t* pMatchCount) EXCLUSIVE_LOCKS_REQUIRED(event_list_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void UnregisterEvent(JdwpEvent* pEvent) diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc index cb2c420dbb..86c84e8b0f 100644 --- a/runtime/jdwp/jdwp_event.cc +++ b/runtime/jdwp/jdwp_event.cc @@ -397,7 +397,7 @@ static JdwpEvent** AllocMatchList(size_t event_count) { * Run through the list and remove any entries with an expired "count" mod * from the event list, then free the match list. */ -void JdwpState::CleanupMatchList(JdwpEvent** match_list, int match_count) { +void JdwpState::CleanupMatchList(JdwpEvent** match_list, size_t match_count) { JdwpEvent** ppEvent = match_list; while (match_count--) { @@ -405,7 +405,8 @@ void JdwpState::CleanupMatchList(JdwpEvent** match_list, int match_count) { for (int i = 0; i < pEvent->modCount; i++) { if (pEvent->mods[i].modKind == MK_COUNT && pEvent->mods[i].count.count == 0) { - VLOG(jdwp) << "##### Removing expired event"; + VLOG(jdwp) << StringPrintf("##### Removing expired event (requestId=%#" PRIx32 ")", + pEvent->requestId); UnregisterEvent(pEvent); EventFree(pEvent); break; @@ -445,7 +446,7 @@ static bool PatternMatch(const char* pattern, const std::string& target) { * If we find a Count mod before rejecting an event, we decrement it. We * need to do this even if later mods cause us to ignore the event. */ -static bool ModsMatch(JdwpEvent* pEvent, ModBasket* basket) +static bool ModsMatch(JdwpEvent* pEvent, const ModBasket& basket) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { JdwpEventMod* pMod = pEvent->mods; @@ -462,53 +463,53 @@ static bool ModsMatch(JdwpEvent* pEvent, ModBasket* basket) CHECK(false); // should not be getting these break; case MK_THREAD_ONLY: - if (pMod->threadOnly.threadId != basket->threadId) { + if (pMod->threadOnly.threadId != basket.threadId) { return false; } break; case MK_CLASS_ONLY: - if (!Dbg::MatchType(basket->classId, pMod->classOnly.refTypeId)) { + if (!Dbg::MatchType(basket.classId, pMod->classOnly.refTypeId)) { return false; } break; case MK_CLASS_MATCH: - if (!PatternMatch(pMod->classMatch.classPattern, basket->className)) { + if (!PatternMatch(pMod->classMatch.classPattern, basket.className)) { return false; } break; case MK_CLASS_EXCLUDE: - if (PatternMatch(pMod->classMatch.classPattern, basket->className)) { + if (PatternMatch(pMod->classMatch.classPattern, basket.className)) { return false; } break; case MK_LOCATION_ONLY: - if (pMod->locationOnly.loc != *basket->pLoc) { + if (pMod->locationOnly.loc != *basket.pLoc) { return false; } break; case MK_EXCEPTION_ONLY: - if (pMod->exceptionOnly.refTypeId != 0 && !Dbg::MatchType(basket->excepClassId, pMod->exceptionOnly.refTypeId)) { + if (pMod->exceptionOnly.refTypeId != 0 && !Dbg::MatchType(basket.excepClassId, pMod->exceptionOnly.refTypeId)) { return false; } - if ((basket->caught && !pMod->exceptionOnly.caught) || (!basket->caught && !pMod->exceptionOnly.uncaught)) { + if ((basket.caught && !pMod->exceptionOnly.caught) || (!basket.caught && !pMod->exceptionOnly.uncaught)) { return false; } break; case MK_FIELD_ONLY: - if (pMod->fieldOnly.fieldId != basket->fieldId) { + if (pMod->fieldOnly.fieldId != basket.fieldId) { return false; } - if (!Dbg::MatchType(basket->fieldTypeID, pMod->fieldOnly.refTypeId)) { + if (!Dbg::MatchType(basket.fieldTypeID, pMod->fieldOnly.refTypeId)) { return false; } break; case MK_STEP: - if (pMod->step.threadId != basket->threadId) { + if (pMod->step.threadId != basket.threadId) { return false; } break; case MK_INSTANCE_ONLY: - if (pMod->instanceOnly.objectId != basket->thisPtr) { + if (pMod->instanceOnly.objectId != basket.thisPtr) { return false; } break; @@ -530,19 +531,16 @@ static bool ModsMatch(JdwpEvent* pEvent, ModBasket* basket) * DO NOT call this multiple times for the same eventKind, as Count mods are * decremented during the scan. */ -void JdwpState::FindMatchingEvents(JdwpEventKind eventKind, ModBasket* basket, - JdwpEvent** match_list, int* pMatchCount) { +void JdwpState::FindMatchingEvents(JdwpEventKind eventKind, const ModBasket& basket, + JdwpEvent** match_list, size_t* pMatchCount) { /* start after the existing entries */ match_list += *pMatchCount; - JdwpEvent* pEvent = event_list_; - while (pEvent != NULL) { + for (JdwpEvent* pEvent = event_list_; pEvent != nullptr; pEvent = pEvent->next) { if (pEvent->eventKind == eventKind && ModsMatch(pEvent, basket)) { *match_list++ = pEvent; (*pMatchCount)++; } - - pEvent = pEvent->next; } } @@ -774,6 +772,22 @@ bool JdwpState::PostVMStart() { return true; } +static void LogMatchingEventsAndThread(JdwpEvent** match_list, size_t match_count, + const ModBasket& basket) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + for (size_t i = 0; i < match_count; ++i) { + JdwpEvent* pEvent = match_list[i]; + VLOG(jdwp) << "EVENT #" << i << ": " << pEvent->eventKind + << StringPrintf(" (requestId=%#" PRIx32 ")", pEvent->requestId); + } + std::string thread_name; + JdwpError error = Dbg::GetThreadName(basket.threadId, thread_name); + if (error != JDWP::ERR_NONE) { + thread_name = "<unknown>"; + } + VLOG(jdwp) << StringPrintf(" thread=%#" PRIx64, basket.threadId) << " " << thread_name; +} + /* * A location of interest has been reached. This handles: * Breakpoint @@ -829,39 +843,40 @@ bool JdwpState::PostLocationEvent(const JdwpLocation* pLoc, ObjectId thisPtr, in return false; } - int match_count = 0; + size_t match_count = 0; ExpandBuf* pReq = NULL; JdwpSuspendPolicy suspend_policy = SP_NONE; { MutexLock mu(Thread::Current(), event_list_lock_); JdwpEvent** match_list = AllocMatchList(event_list_size_); if ((eventFlags & Dbg::kBreakpoint) != 0) { - FindMatchingEvents(EK_BREAKPOINT, &basket, match_list, &match_count); + FindMatchingEvents(EK_BREAKPOINT, basket, match_list, &match_count); } if ((eventFlags & Dbg::kSingleStep) != 0) { - FindMatchingEvents(EK_SINGLE_STEP, &basket, match_list, &match_count); + FindMatchingEvents(EK_SINGLE_STEP, basket, match_list, &match_count); } if ((eventFlags & Dbg::kMethodEntry) != 0) { - FindMatchingEvents(EK_METHOD_ENTRY, &basket, match_list, &match_count); + FindMatchingEvents(EK_METHOD_ENTRY, basket, match_list, &match_count); } if ((eventFlags & Dbg::kMethodExit) != 0) { - FindMatchingEvents(EK_METHOD_EXIT, &basket, match_list, &match_count); - FindMatchingEvents(EK_METHOD_EXIT_WITH_RETURN_VALUE, &basket, match_list, &match_count); + FindMatchingEvents(EK_METHOD_EXIT, basket, match_list, &match_count); + FindMatchingEvents(EK_METHOD_EXIT_WITH_RETURN_VALUE, basket, match_list, &match_count); } if (match_count != 0) { - VLOG(jdwp) << "EVENT: " << match_list[0]->eventKind << "(" << match_count << " total) " - << basket.className << "." << Dbg::GetMethodName(pLoc->method_id) - << StringPrintf(" thread=%#" PRIx64 " dex_pc=%#" PRIx64 ")", - basket.threadId, pLoc->dex_pc); - suspend_policy = scanSuspendPolicy(match_list, match_count); - VLOG(jdwp) << " suspend_policy=" << suspend_policy; + + if (VLOG_IS_ON(jdwp)) { + LogMatchingEventsAndThread(match_list, match_count, basket); + VLOG(jdwp) << " location=" << *pLoc; + VLOG(jdwp) << StringPrintf(" this=%#" PRIx64, basket.thisPtr); + VLOG(jdwp) << " suspend_policy=" << suspend_policy; + } pReq = eventPrep(); expandBufAdd1(pReq, suspend_policy); expandBufAdd4BE(pReq, match_count); - for (int i = 0; i < match_count; i++) { + for (size_t i = 0; i < match_count; i++) { expandBufAdd1(pReq, match_list[i]->eventKind); expandBufAdd4BE(pReq, match_list[i]->requestId); expandBufAdd8BE(pReq, basket.threadId); @@ -892,6 +907,8 @@ bool JdwpState::PostFieldEvent(const JdwpLocation* pLoc, RefTypeId typeId, Field basket.fieldTypeID = typeId; basket.fieldId = fieldId; + DCHECK_EQ(fieldValue != nullptr, is_modification); + if (InvokeInProgress()) { VLOG(jdwp) << "Not posting field event during invoke"; return false; @@ -912,7 +929,7 @@ bool JdwpState::PostFieldEvent(const JdwpLocation* pLoc, RefTypeId typeId, Field return false; } - int match_count = 0; + size_t match_count = 0; ExpandBuf* pReq = NULL; JdwpSuspendPolicy suspend_policy = SP_NONE; { @@ -920,24 +937,29 @@ bool JdwpState::PostFieldEvent(const JdwpLocation* pLoc, RefTypeId typeId, Field JdwpEvent** match_list = AllocMatchList(event_list_size_); if (is_modification) { - FindMatchingEvents(EK_FIELD_MODIFICATION, &basket, match_list, &match_count); + FindMatchingEvents(EK_FIELD_MODIFICATION, basket, match_list, &match_count); } else { - FindMatchingEvents(EK_FIELD_ACCESS, &basket, match_list, &match_count); + FindMatchingEvents(EK_FIELD_ACCESS, basket, match_list, &match_count); } if (match_count != 0) { - VLOG(jdwp) << "EVENT: " << match_list[0]->eventKind << "(" << match_count << " total) " - << basket.className << "." << Dbg::GetMethodName(pLoc->method_id) - << StringPrintf(" thread=%#" PRIx64 " dex_pc=%#" PRIx64 ")", - basket.threadId, pLoc->dex_pc); - suspend_policy = scanSuspendPolicy(match_list, match_count); - VLOG(jdwp) << " suspend_policy=" << suspend_policy; + + if (VLOG_IS_ON(jdwp)) { + LogMatchingEventsAndThread(match_list, match_count, basket); + VLOG(jdwp) << " location=" << *pLoc; + VLOG(jdwp) << StringPrintf(" this=%#" PRIx64, basket.thisPtr); + VLOG(jdwp) << StringPrintf(" type=%#" PRIx64, basket.fieldTypeID) << " " + << Dbg::GetClassName(basket.fieldTypeID); + VLOG(jdwp) << StringPrintf(" field=%#" PRIx32, basket.fieldId) << " " + << Dbg::GetFieldName(basket.fieldId); + VLOG(jdwp) << " suspend_policy=" << suspend_policy; + } pReq = eventPrep(); expandBufAdd1(pReq, suspend_policy); expandBufAdd4BE(pReq, match_count); - for (int i = 0; i < match_count; i++) { + for (size_t i = 0; i < match_count; i++) { expandBufAdd1(pReq, match_list[i]->eventKind); expandBufAdd4BE(pReq, match_list[i]->requestId); expandBufAdd8BE(pReq, basket.threadId); @@ -984,30 +1006,31 @@ bool JdwpState::PostThreadChange(ObjectId threadId, bool start) { ExpandBuf* pReq = NULL; JdwpSuspendPolicy suspend_policy = SP_NONE; - int match_count = 0; + size_t match_count = 0; { // Don't allow the list to be updated while we scan it. MutexLock mu(Thread::Current(), event_list_lock_); JdwpEvent** match_list = AllocMatchList(event_list_size_); if (start) { - FindMatchingEvents(EK_THREAD_START, &basket, match_list, &match_count); + FindMatchingEvents(EK_THREAD_START, basket, match_list, &match_count); } else { - FindMatchingEvents(EK_THREAD_DEATH, &basket, match_list, &match_count); + FindMatchingEvents(EK_THREAD_DEATH, basket, match_list, &match_count); } if (match_count != 0) { - VLOG(jdwp) << "EVENT: " << match_list[0]->eventKind << "(" << match_count << " total) " - << StringPrintf("thread=%#" PRIx64, basket.threadId) << ")"; - suspend_policy = scanSuspendPolicy(match_list, match_count); - VLOG(jdwp) << " suspend_policy=" << suspend_policy; + + if (VLOG_IS_ON(jdwp)) { + LogMatchingEventsAndThread(match_list, match_count, basket); + VLOG(jdwp) << " suspend_policy=" << suspend_policy; + } pReq = eventPrep(); expandBufAdd1(pReq, suspend_policy); expandBufAdd4BE(pReq, match_count); - for (int i = 0; i < match_count; i++) { + for (size_t i = 0; i < match_count; i++) { expandBufAdd1(pReq, match_list[i]->eventKind); expandBufAdd4BE(pReq, match_list[i]->requestId); expandBufAdd8BE(pReq, basket.threadId); @@ -1072,33 +1095,35 @@ bool JdwpState::PostException(const JdwpLocation* pThrowLoc, return false; } - int match_count = 0; + size_t match_count = 0; ExpandBuf* pReq = NULL; JdwpSuspendPolicy suspend_policy = SP_NONE; { MutexLock mu(Thread::Current(), event_list_lock_); JdwpEvent** match_list = AllocMatchList(event_list_size_); - FindMatchingEvents(EK_EXCEPTION, &basket, match_list, &match_count); + FindMatchingEvents(EK_EXCEPTION, basket, match_list, &match_count); if (match_count != 0) { - VLOG(jdwp) << "EVENT: " << match_list[0]->eventKind << "(" << match_count << " total)" - << StringPrintf(" thread=%#" PRIx64, basket.threadId) - << StringPrintf(" exceptId=%#" PRIx64, exceptionId) - << " caught=" << basket.caught << ")" - << " throw: " << *pThrowLoc; - if (pCatchLoc->class_id == 0) { - VLOG(jdwp) << " catch: (not caught)"; - } else { - VLOG(jdwp) << " catch: " << *pCatchLoc; - } - suspend_policy = scanSuspendPolicy(match_list, match_count); - VLOG(jdwp) << " suspend_policy=" << suspend_policy; + + if (VLOG_IS_ON(jdwp)) { + LogMatchingEventsAndThread(match_list, match_count, basket); + VLOG(jdwp) << " throwLocation=" << *pThrowLoc; + if (pCatchLoc->class_id == 0) { + VLOG(jdwp) << " catchLocation=uncaught"; + } else { + VLOG(jdwp) << " catchLocation=" << *pCatchLoc; + } + VLOG(jdwp) << StringPrintf(" this=%#" PRIx64, basket.thisPtr); + VLOG(jdwp) << StringPrintf(" exceptionClass=%#" PRIx64, basket.excepClassId) << " " + << Dbg::GetClassName(basket.excepClassId); + VLOG(jdwp) << " suspend_policy=" << suspend_policy; + } pReq = eventPrep(); expandBufAdd1(pReq, suspend_policy); expandBufAdd4BE(pReq, match_count); - for (int i = 0; i < match_count; i++) { + for (size_t i = 0; i < match_count; i++) { expandBufAdd1(pReq, match_list[i]->eventKind); expandBufAdd4BE(pReq, match_list[i]->requestId); expandBufAdd8BE(pReq, basket.threadId); @@ -1142,17 +1167,19 @@ bool JdwpState::PostClassPrepare(JdwpTypeTag tag, RefTypeId refTypeId, const std ExpandBuf* pReq = NULL; JdwpSuspendPolicy suspend_policy = SP_NONE; - int match_count = 0; + size_t match_count = 0; { MutexLock mu(Thread::Current(), event_list_lock_); JdwpEvent** match_list = AllocMatchList(event_list_size_); - FindMatchingEvents(EK_CLASS_PREPARE, &basket, match_list, &match_count); + FindMatchingEvents(EK_CLASS_PREPARE, basket, match_list, &match_count); if (match_count != 0) { - VLOG(jdwp) << "EVENT: " << match_list[0]->eventKind << "(" << match_count << " total) " - << StringPrintf("thread=%#" PRIx64, basket.threadId) << ") " << signature; - suspend_policy = scanSuspendPolicy(match_list, match_count); - VLOG(jdwp) << " suspend_policy=" << suspend_policy; + + if (VLOG_IS_ON(jdwp)) { + LogMatchingEventsAndThread(match_list, match_count, basket); + VLOG(jdwp) << StringPrintf(" type=%#" PRIx64, basket.classId)<< " " << signature; + VLOG(jdwp) << " suspend_policy=" << suspend_policy; + } if (basket.threadId == debug_thread_id_) { /* @@ -1171,7 +1198,7 @@ bool JdwpState::PostClassPrepare(JdwpTypeTag tag, RefTypeId refTypeId, const std expandBufAdd1(pReq, suspend_policy); expandBufAdd4BE(pReq, match_count); - for (int i = 0; i < match_count; i++) { + for (size_t i = 0; i < match_count; i++) { expandBufAdd1(pReq, match_list[i]->eventKind); expandBufAdd4BE(pReq, match_list[i]->requestId); expandBufAdd8BE(pReq, basket.threadId); diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc index d637a945d1..29d3c8ab8e 100644 --- a/runtime/jdwp/object_registry.cc +++ b/runtime/jdwp/object_registry.cc @@ -115,12 +115,13 @@ void ObjectRegistry::Clear() { // Delete all the JNI references. JNIEnv* env = self->GetJniEnv(); for (const auto& pair : object_to_entry_) { - const ObjectRegistryEntry& entry = *pair.second; - if (entry.jni_reference_type == JNIWeakGlobalRefType) { - env->DeleteWeakGlobalRef(entry.jni_reference); + const ObjectRegistryEntry* entry = pair.second; + if (entry->jni_reference_type == JNIWeakGlobalRefType) { + env->DeleteWeakGlobalRef(entry->jni_reference); } else { - env->DeleteGlobalRef(entry.jni_reference); + env->DeleteGlobalRef(entry->jni_reference); } + delete entry; } // Clear the maps. object_to_entry_.clear(); diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 5606d47832..083f179f38 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -359,8 +359,9 @@ class SharedLibrary { jni_on_load_result_(kPending) { } - mirror::Object* GetClassLoader() { - return class_loader_; + mirror::Object* GetClassLoader() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::Object** root = &class_loader_; + return ReadBarrier::BarrierForRoot<mirror::Object, kWithReadBarrier>(root); } std::string GetPath() { @@ -591,7 +592,12 @@ class JNI { mirror::ArtMethod* m = soa.DecodeMethod(mid); CHECK(!kMovingMethods); jobject art_method = soa.AddLocalReference<jobject>(m); - jobject reflect_method = env->AllocObject(WellKnownClasses::java_lang_reflect_Method); + jobject reflect_method; + if (m->IsConstructor()) { + reflect_method = env->AllocObject(WellKnownClasses::java_lang_reflect_Constructor); + } else { + reflect_method = env->AllocObject(WellKnownClasses::java_lang_reflect_Method); + } if (env->ExceptionCheck()) { return nullptr; } @@ -2446,13 +2452,18 @@ class JNI { static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) { if (capacity < 0) { JniAbortF("NewDirectByteBuffer", "negative buffer capacity: %" PRId64, capacity); + return nullptr; } if (address == nullptr && capacity != 0) { JniAbortF("NewDirectByteBuffer", "non-zero capacity for nullptr pointer: %" PRId64, capacity); + return nullptr; } - // At the moment, the capacity is limited to 32 bits. - CHECK_LE(capacity, 0xffffffff); + // At the moment, the capacity of DirectByteBuffer is limited to a signed int. + if (capacity > INT_MAX) { + JniAbortF("NewDirectByteBuffer", "buffer capacity greater than maximum jint: %" PRId64, capacity); + return nullptr; + } jlong address_arg = reinterpret_cast<jlong>(address); jint capacity_arg = static_cast<jint>(capacity); @@ -3160,9 +3171,7 @@ mirror::Object* JavaVMExt::DecodeWeakGlobal(Thread* self, IndirectRef ref) { while (UNLIKELY(!allow_new_weak_globals_)) { weak_globals_add_condition_.WaitHoldingLocks(self); } - // The weak globals do need a read barrier as they are weak roots. - mirror::Object* obj = weak_globals_.Get<kWithReadBarrier>(ref); - return obj; + return weak_globals_.Get(ref); } void JavaVMExt::DumpReferenceTables(std::ostream& os) { diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index 5e46c57de1..d255ec8dff 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -380,19 +380,39 @@ TEST_F(JniInternalTest, FromReflectedField_ToReflectedField) { TEST_F(JniInternalTest, FromReflectedMethod_ToReflectedMethod) { jclass jlrMethod = env_->FindClass("java/lang/reflect/Method"); + ASSERT_NE(jlrMethod, nullptr); + jclass jlrConstructor = env_->FindClass("java/lang/reflect/Constructor"); + ASSERT_NE(jlrConstructor, nullptr); jclass c = env_->FindClass("java/lang/String"); ASSERT_NE(c, nullptr); - jmethodID mid = env_->GetMethodID(c, "length", "()I"); + + jmethodID mid = env_->GetMethodID(c, "<init>", "()V"); ASSERT_NE(mid, nullptr); - // Turn the mid into a java.lang.reflect.Method... + // Turn the mid into a java.lang.reflect.Constructor... jobject method = env_->ToReflectedMethod(c, mid, JNI_FALSE); - ASSERT_NE(c, nullptr); - ASSERT_TRUE(env_->IsInstanceOf(method, jlrMethod)); + ASSERT_NE(method, nullptr); + ASSERT_TRUE(env_->IsInstanceOf(method, jlrConstructor)); // ...and back again. jmethodID mid2 = env_->FromReflectedMethod(method); ASSERT_NE(mid2, nullptr); // Make sure we can actually use it. - jstring s = env_->NewStringUTF("poop"); + jstring s = reinterpret_cast<jstring>(env_->AllocObject(c)); + ASSERT_NE(s, nullptr); + env_->CallVoidMethod(s, mid2); + ASSERT_EQ(JNI_FALSE, env_->ExceptionCheck()); + + mid = env_->GetMethodID(c, "length", "()I"); + ASSERT_NE(mid, nullptr); + // Turn the mid into a java.lang.reflect.Method... + method = env_->ToReflectedMethod(c, mid, JNI_FALSE); + ASSERT_NE(method, nullptr); + ASSERT_TRUE(env_->IsInstanceOf(method, jlrMethod)); + // ...and back again. + mid2 = env_->FromReflectedMethod(method); + ASSERT_NE(mid2, nullptr); + // Make sure we can actually use it. + s = env_->NewStringUTF("poop"); + ASSERT_NE(s, nullptr); ASSERT_EQ(4, env_->CallIntMethod(s, mid2)); // Bad arguments. @@ -1515,6 +1535,12 @@ TEST_F(JniInternalTest, NewDirectBuffer_GetDirectBufferAddress_GetDirectBufferCa ASSERT_TRUE(env_->IsInstanceOf(buffer, buffer_class)); ASSERT_EQ(env_->GetDirectBufferAddress(buffer), bytes); ASSERT_EQ(env_->GetDirectBufferCapacity(buffer), static_cast<jlong>(sizeof(bytes))); + + { + CheckJniAbortCatcher check_jni_abort_catcher; + env_->NewDirectByteBuffer(bytes, static_cast<jlong>(INT_MAX) + 1); + check_jni_abort_catcher.Check("in call to NewDirectByteBuffer"); + } } TEST_F(JniInternalTest, MonitorEnterExit) { @@ -1568,7 +1594,6 @@ TEST_F(JniInternalTest, MonitorEnterExit) { CheckJniAbortCatcher check_jni_abort_catcher; env_->MonitorEnter(nullptr); check_jni_abort_catcher.Check("in call to MonitorEnter"); - env_->MonitorExit(nullptr); check_jni_abort_catcher.Check("in call to MonitorExit"); } diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index 81a86235ec..1074253fea 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -34,6 +34,13 @@ #ifdef USE_ASHMEM #include <cutils/ashmem.h> +#ifndef ANDROID_OS +#include <sys/resource.h> +#endif +#endif + +#ifndef MAP_ANONYMOUS +#define MAP_ANONYMOUS MAP_ANON #endif namespace art { @@ -65,7 +72,7 @@ std::ostream& operator<<(std::ostream& os, const std::multimap<void*, MemMap*>& std::multimap<void*, MemMap*> MemMap::maps_; -#if defined(__LP64__) && !defined(__x86_64__) +#if USE_ART_LOW_4G_ALLOCATOR // Handling mem_map in 32b address range for 64b architectures that do not support MAP_32BIT. // The regular start of memory allocations. The first 64KB is protected by SELinux. @@ -179,20 +186,32 @@ MemMap* MemMap::MapAnonymous(const char* name, byte* expected, size_t byte_count } size_t page_aligned_byte_count = RoundUp(byte_count, kPageSize); + int flags = MAP_PRIVATE | MAP_ANONYMOUS; + ScopedFd fd(-1); + #ifdef USE_ASHMEM - // android_os_Debug.cpp read_mapinfo assumes all ashmem regions associated with the VM are - // prefixed "dalvik-". - std::string debug_friendly_name("dalvik-"); - debug_friendly_name += name; - ScopedFd fd(ashmem_create_region(debug_friendly_name.c_str(), page_aligned_byte_count)); - if (fd.get() == -1) { - *error_msg = StringPrintf("ashmem_create_region failed for '%s': %s", name, strerror(errno)); - return nullptr; - } - int flags = MAP_PRIVATE; +#ifdef HAVE_ANDROID_OS + const bool use_ashmem = true; #else - ScopedFd fd(-1); - int flags = MAP_PRIVATE | MAP_ANONYMOUS; + // When not on Android ashmem is faked using files in /tmp. Ensure that such files won't + // fail due to ulimit restrictions. If they will then use a regular mmap. + struct rlimit rlimit_fsize; + CHECK_EQ(getrlimit(RLIMIT_FSIZE, &rlimit_fsize), 0); + const bool use_ashmem = (rlimit_fsize.rlim_cur == RLIM_INFINITY) || + (page_aligned_byte_count < rlimit_fsize.rlim_cur); +#endif + if (use_ashmem) { + // android_os_Debug.cpp read_mapinfo assumes all ashmem regions associated with the VM are + // prefixed "dalvik-". + std::string debug_friendly_name("dalvik-"); + debug_friendly_name += name; + fd.reset(ashmem_create_region(debug_friendly_name.c_str(), page_aligned_byte_count)); + if (fd.get() == -1) { + *error_msg = StringPrintf("ashmem_create_region failed for '%s': %s", name, strerror(errno)); + return nullptr; + } + flags = MAP_PRIVATE; + } #endif // We need to store and potentially set an error number for pretty printing of errors @@ -216,7 +235,7 @@ MemMap* MemMap::MapAnonymous(const char* name, byte* expected, size_t byte_count // A page allocator would be a useful abstraction here, as // 1) It is doubtful that MAP_32BIT on x86_64 is doing the right job for us // 2) The linear scheme, even with simple saving of the last known position, is very crude -#if defined(__LP64__) && !defined(__x86_64__) +#if USE_ART_LOW_4G_ALLOCATOR // MAP_32BIT only available on x86_64. void* actual = MAP_FAILED; if (low_4gb && expected == nullptr) { @@ -280,7 +299,7 @@ MemMap* MemMap::MapAnonymous(const char* name, byte* expected, size_t byte_count } #else -#ifdef __x86_64__ +#if defined(__LP64__) if (low_4gb && expected == nullptr) { flags |= MAP_32BIT; } diff --git a/runtime/mem_map.h b/runtime/mem_map.h index e42251ce57..defa6a52fd 100644 --- a/runtime/mem_map.h +++ b/runtime/mem_map.h @@ -30,6 +30,12 @@ namespace art { +#if defined(__LP64__) && (!defined(__x86_64__) || defined(__APPLE__)) +#define USE_ART_LOW_4G_ALLOCATOR 1 +#else +#define USE_ART_LOW_4G_ALLOCATOR 0 +#endif + #ifdef __linux__ static constexpr bool kMadviseZeroes = true; #else @@ -147,8 +153,8 @@ class MemMap { size_t base_size_; // Length of mapping. May be changed by RemapAtEnd (ie Zygote). int prot_; // Protection of the map. -#if defined(__LP64__) && !defined(__x86_64__) - static uintptr_t next_mem_pos_; // next memory location to check for low_4g extent +#if USE_ART_LOW_4G_ALLOCATOR + static uintptr_t next_mem_pos_; // Next memory location to check for low_4g extent. #endif // All the non-empty MemMaps. Use a multimap as we do a reserve-and-divide (eg ElfMap::Load()). diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 512a66f4d3..6205f70a10 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -505,8 +505,10 @@ inline Object* Class::AllocNonMovableObject(Thread* self) { template <bool kVisitClass, typename Visitor> inline void Class::VisitReferences(mirror::Class* klass, const Visitor& visitor) { - VisitInstanceFieldsReferences<kVisitClass>(klass, visitor); + // Visit the static fields first so that we don't overwrite the SFields / IFields instance + // fields. VisitStaticFieldsReferences<kVisitClass>(this, visitor); + VisitInstanceFieldsReferences<kVisitClass>(klass, visitor); } inline bool Class::IsArtFieldClass() const { diff --git a/runtime/mirror/iftable-inl.h b/runtime/mirror/iftable-inl.h index ec3e51495b..3f20bf4b45 100644 --- a/runtime/mirror/iftable-inl.h +++ b/runtime/mirror/iftable-inl.h @@ -25,8 +25,9 @@ namespace mirror { inline void IfTable::SetInterface(int32_t i, Class* interface) { DCHECK(interface != NULL); DCHECK(interface->IsInterface()); - DCHECK(Get((i * kMax) + kInterface) == NULL); - Set<false>((i * kMax) + kInterface, interface); + const size_t idx = i * kMax + kInterface; + DCHECK_EQ(Get(idx), static_cast<Object*>(nullptr)); + Set<false>(idx, interface); } } // namespace mirror diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index 567ce3e929..15ecd3c3f2 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -26,6 +26,7 @@ #include "class.h" #include "lock_word-inl.h" #include "monitor.h" +#include "object_array-inl.h" #include "read_barrier-inl.h" #include "runtime.h" #include "reference.h" @@ -667,10 +668,9 @@ inline void Object::VisitFieldsReferences(uint32_t ref_offsets, const Visitor& v mirror::ArtField* field = kIsStatic ? klass->GetStaticField(i) : klass->GetInstanceField(i); MemberOffset field_offset = field->GetOffset(); // TODO: Do a simpler check? - if (!kVisitClass && UNLIKELY(field_offset.Uint32Value() == ClassOffset().Uint32Value())) { - continue; + if (kVisitClass || field_offset.Uint32Value() != ClassOffset().Uint32Value()) { + visitor(this, field_offset, kIsStatic); } - visitor(this, field_offset, kIsStatic); } } } @@ -693,18 +693,16 @@ template <const bool kVisitClass, VerifyObjectFlags kVerifyFlags, typename Visit inline void Object::VisitReferences(const Visitor& visitor, const JavaLangRefVisitor& ref_visitor) { mirror::Class* klass = GetClass<kVerifyFlags>(); - if (klass->IsVariableSize()) { - if (klass->IsClassClass()) { - AsClass<kVerifyNone>()->VisitReferences<kVisitClass>(klass, visitor); - } else { - DCHECK(klass->IsArrayClass<kVerifyFlags>()); - if (klass->IsObjectArrayClass<kVerifyNone>()) { - AsObjectArray<mirror::Object, kVerifyNone>()->VisitReferences<kVisitClass>(visitor); - } else if (kVisitClass) { - visitor(this, ClassOffset(), false); - } + if (klass == Class::GetJavaLangClass()) { + AsClass<kVerifyNone>()->VisitReferences<kVisitClass>(klass, visitor); + } else if (klass->IsArrayClass()) { + if (klass->IsObjectArrayClass<kVerifyNone>()) { + AsObjectArray<mirror::Object, kVerifyNone>()->VisitReferences<kVisitClass>(visitor); + } else if (kVisitClass) { + visitor(this, ClassOffset(), false); } } else { + DCHECK(!klass->IsVariableSize()); VisitInstanceFieldsReferences<kVisitClass>(klass, visitor); if (UNLIKELY(klass->IsReferenceClass<kVerifyNone>())) { ref_visitor(klass, AsReference()); diff --git a/runtime/monitor.cc b/runtime/monitor.cc index a19445b189..999a9e504b 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -1115,20 +1115,29 @@ void MonitorList::SweepMonitorList(IsMarkedCallback* callback, void* arg) { } } +struct MonitorDeflateArgs { + MonitorDeflateArgs() : self(Thread::Current()), deflate_count(0) {} + Thread* const self; + size_t deflate_count; +}; + static mirror::Object* MonitorDeflateCallback(mirror::Object* object, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (Monitor::Deflate(reinterpret_cast<Thread*>(arg), object)) { + MonitorDeflateArgs* args = reinterpret_cast<MonitorDeflateArgs*>(arg); + if (Monitor::Deflate(args->self, object)) { DCHECK_NE(object->GetLockWord(true).GetState(), LockWord::kFatLocked); + ++args->deflate_count; // If we deflated, return nullptr so that the monitor gets removed from the array. return nullptr; } return object; // Monitor was not deflated. } -void MonitorList::DeflateMonitors() { - Thread* self = Thread::Current(); - Locks::mutator_lock_->AssertExclusiveHeld(self); - SweepMonitorList(MonitorDeflateCallback, reinterpret_cast<Thread*>(self)); +size_t MonitorList::DeflateMonitors() { + MonitorDeflateArgs args; + Locks::mutator_lock_->AssertExclusiveHeld(args.self); + SweepMonitorList(MonitorDeflateCallback, &args); + return args.deflate_count; } MonitorInfo::MonitorInfo(mirror::Object* obj) : owner_(NULL), entry_count_(0) { diff --git a/runtime/monitor.h b/runtime/monitor.h index a28823d184..d7552a3fd5 100644 --- a/runtime/monitor.h +++ b/runtime/monitor.h @@ -229,7 +229,8 @@ class MonitorList { LOCKS_EXCLUDED(monitor_list_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void DisallowNewMonitors() LOCKS_EXCLUDED(monitor_list_lock_); void AllowNewMonitors() LOCKS_EXCLUDED(monitor_list_lock_); - void DeflateMonitors() LOCKS_EXCLUDED(monitor_list_lock_) + // Returns how many monitors were deflated. + size_t DeflateMonitors() LOCKS_EXCLUDED(monitor_list_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); private: diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 9512a5a7f5..440d3d0b59 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -26,6 +26,7 @@ #include <unistd.h> #include "base/logging.h" +#include "base/stl_util.h" #include "class_linker.h" #include "common_throws.h" #include "dex_file-inl.h" @@ -106,34 +107,19 @@ static jlong DexFile_openDexFileNative(JNIEnv* env, jclass, jstring javaSourceNa return 0; } - uint32_t dex_location_checksum; - uint32_t* dex_location_checksum_pointer = &dex_location_checksum; + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + std::unique_ptr<std::vector<const DexFile*>> dex_files(new std::vector<const DexFile*>()); std::vector<std::string> error_msgs; - std::string error_msg; - if (!DexFile::GetChecksum(sourceName.c_str(), dex_location_checksum_pointer, &error_msg)) { - dex_location_checksum_pointer = NULL; - } - ClassLinker* linker = Runtime::Current()->GetClassLinker(); - const DexFile* dex_file; - if (outputName.c_str() == nullptr) { - // FindOrCreateOatFileForDexLocation can tolerate a missing dex_location_checksum - dex_file = linker->FindDexFileInOatFileFromDexLocation(sourceName.c_str(), - dex_location_checksum_pointer, - kRuntimeISA, - &error_msgs); + bool success = linker->OpenDexFilesFromOat(sourceName.c_str(), outputName.c_str(), &error_msgs, + dex_files.get()); + + if (success) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(dex_files.release())); } else { - // FindOrCreateOatFileForDexLocation requires the dex_location_checksum - if (dex_location_checksum_pointer == NULL) { - ScopedObjectAccess soa(env); - DCHECK(!error_msg.empty()); - ThrowIOException("%s", error_msg.c_str()); - return 0; - } - dex_file = linker->FindOrCreateOatFileForDexLocation(sourceName.c_str(), dex_location_checksum, - outputName.c_str(), &error_msgs); - } - if (dex_file == nullptr) { + // The vector should be empty after a failed loading attempt. + DCHECK_EQ(0U, dex_files->size()); + ScopedObjectAccess soa(env); CHECK(!error_msgs.empty()); // The most important message is at the end. So set up nesting by going forward, which will @@ -146,35 +132,41 @@ static jlong DexFile_openDexFileNative(JNIEnv* env, jclass, jstring javaSourceNa return 0; } - return static_cast<jlong>(reinterpret_cast<uintptr_t>(dex_file)); } -static const DexFile* toDexFile(jlong dex_file_address, JNIEnv* env) { - const DexFile* dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(dex_file_address)); - if (UNLIKELY(dex_file == nullptr)) { +static std::vector<const DexFile*>* toDexFiles(jlong dex_file_address, JNIEnv* env) { + std::vector<const DexFile*>* dex_files = reinterpret_cast<std::vector<const DexFile*>*>( + static_cast<uintptr_t>(dex_file_address)); + if (UNLIKELY(dex_files == nullptr)) { ScopedObjectAccess soa(env); ThrowNullPointerException(NULL, "dex_file == null"); } - return dex_file; + return dex_files; } static void DexFile_closeDexFile(JNIEnv* env, jclass, jlong cookie) { - const DexFile* dex_file; - dex_file = toDexFile(cookie, env); - if (dex_file == nullptr) { + std::unique_ptr<std::vector<const DexFile*>> dex_files(toDexFiles(cookie, env)); + if (dex_files.get() == nullptr) { return; } ScopedObjectAccess soa(env); - if (Runtime::Current()->GetClassLinker()->IsDexFileRegistered(*dex_file)) { - return; + + size_t index = 0; + for (const DexFile* dex_file : *dex_files) { + if (Runtime::Current()->GetClassLinker()->IsDexFileRegistered(*dex_file)) { + (*dex_files)[index] = nullptr; + } + index++; } - delete dex_file; + + STLDeleteElements(dex_files.get()); + // Unique_ptr will delete the vector itself. } static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader, jlong cookie) { - const DexFile* dex_file = toDexFile(cookie, env); - if (dex_file == NULL) { + std::vector<const DexFile*>* dex_files = toDexFiles(cookie, env); + if (dex_files == NULL) { VLOG(class_linker) << "Failed to find dex_file"; return NULL; } @@ -184,33 +176,60 @@ static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, j return NULL; } const std::string descriptor(DotToDescriptor(class_name.c_str())); - const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str()); - if (dex_class_def == NULL) { - VLOG(class_linker) << "Failed to find dex_class_def"; - return NULL; + + for (const DexFile* dex_file : *dex_files) { + const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str()); + if (dex_class_def != nullptr) { + ScopedObjectAccess soa(env); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + class_linker->RegisterDexFile(*dex_file); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader( + hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader))); + mirror::Class* result = class_linker->DefineClass(descriptor.c_str(), class_loader, *dex_file, + *dex_class_def); + if (result != nullptr) { + VLOG(class_linker) << "DexFile_defineClassNative returning " << result; + return soa.AddLocalReference<jclass>(result); + } + } } - ScopedObjectAccess soa(env); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - class_linker->RegisterDexFile(*dex_file); - StackHandleScope<1> hs(soa.Self()); - Handle<mirror::ClassLoader> class_loader( - hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader))); - mirror::Class* result = class_linker->DefineClass(descriptor.c_str(), class_loader, *dex_file, - *dex_class_def); - VLOG(class_linker) << "DexFile_defineClassNative returning " << result; - return soa.AddLocalReference<jclass>(result); + VLOG(class_linker) << "Failed to find dex_class_def"; + return nullptr; } +// Needed as a compare functor for sets of const char +struct CharPointerComparator { + bool operator()(const char *str1, const char *str2) const { + return strcmp(str1, str2) < 0; + } +}; + +// Note: this can be an expensive call, as we sort out duplicates in MultiDex files. static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jlong cookie) { jobjectArray result = nullptr; - const DexFile* dex_file = toDexFile(cookie, env); - if (dex_file != nullptr) { - result = env->NewObjectArray(dex_file->NumClassDefs(), WellKnownClasses::java_lang_String, - nullptr); - if (result != nullptr) { + std::vector<const DexFile*>* dex_files = toDexFiles(cookie, env); + + if (dex_files != nullptr) { + // Push all class descriptors into a set. Use set instead of unordered_set as we want to + // retrieve all in the end. + std::set<const char*, CharPointerComparator> descriptors; + 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); - std::string descriptor(DescriptorToDot(dex_file->GetClassDescriptor(class_def))); + const char* descriptor = dex_file->GetClassDescriptor(class_def); + descriptors.insert(descriptor); + } + } + + // Now create output array and copy the set into it. + result = env->NewObjectArray(descriptors.size(), WellKnownClasses::java_lang_String, nullptr); + if (result != nullptr) { + auto it = descriptors.begin(); + auto it_end = descriptors.end(); + jsize i = 0; + for (; it != it_end; it++, ++i) { + std::string descriptor(DescriptorToDot(*it)); ScopedLocalRef<jstring> jdescriptor(env, env->NewStringUTF(descriptor.c_str())); if (jdescriptor.get() == nullptr) { return nullptr; diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc index 6bbe642217..ee99e78067 100644 --- a/runtime/native/java_lang_System.cc +++ b/runtime/native/java_lang_System.cc @@ -147,23 +147,73 @@ static void System_arraycopy(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, dstObjArray->AssignableCheckingMemcpy(dstPos, srcObjArray, srcPos, count, true); } -static void System_arraycopyCharUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, - jobject javaDst, jint dstPos, jint count) { +// Template to convert general array to that of its specific primitive type. +template <typename T> +inline T* AsPrimitiveArray(mirror::Array* array) { + return down_cast<T*>(array); +} + +template <typename T, Primitive::Type kPrimType> +inline void System_arraycopyTUnchecked(JNIEnv* env, jobject javaSrc, jint srcPos, + jobject javaDst, jint dstPos, jint count) { ScopedFastNativeObjectAccess soa(env); mirror::Object* srcObject = soa.Decode<mirror::Object*>(javaSrc); mirror::Object* dstObject = soa.Decode<mirror::Object*>(javaDst); - DCHECK(srcObject != nullptr); DCHECK(dstObject != nullptr); mirror::Array* srcArray = srcObject->AsArray(); mirror::Array* dstArray = dstObject->AsArray(); - DCHECK_GE(srcPos, 0); - DCHECK_GE(dstPos, 0); DCHECK_GE(count, 0); - DCHECK_LE(srcPos + count, srcArray->GetLength()); - DCHECK_LE(dstPos + count, dstArray->GetLength()); DCHECK_EQ(srcArray->GetClass(), dstArray->GetClass()); - DCHECK_EQ(srcArray->GetClass()->GetComponentType()->GetPrimitiveType(), Primitive::kPrimChar); - dstArray->AsCharArray()->Memmove(dstPos, srcArray->AsCharArray(), srcPos, count); + DCHECK_EQ(srcArray->GetClass()->GetComponentType()->GetPrimitiveType(), kPrimType); + AsPrimitiveArray<T>(dstArray)->Memmove(dstPos, AsPrimitiveArray<T>(srcArray), srcPos, count); +} + +static void System_arraycopyCharUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, + jobject javaDst, jint dstPos, jint count) { + System_arraycopyTUnchecked<mirror::CharArray, Primitive::kPrimChar>(env, javaSrc, srcPos, + javaDst, dstPos, count); +} + +static void System_arraycopyByteUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, + jobject javaDst, jint dstPos, jint count) { + System_arraycopyTUnchecked<mirror::ByteArray, Primitive::kPrimByte>(env, javaSrc, srcPos, + javaDst, dstPos, count); +} + +static void System_arraycopyShortUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, + jobject javaDst, jint dstPos, jint count) { + System_arraycopyTUnchecked<mirror::ShortArray, Primitive::kPrimShort>(env, javaSrc, srcPos, + javaDst, dstPos, count); +} + +static void System_arraycopyIntUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, + jobject javaDst, jint dstPos, jint count) { + System_arraycopyTUnchecked<mirror::IntArray, Primitive::kPrimInt>(env, javaSrc, srcPos, + javaDst, dstPos, count); +} + +static void System_arraycopyLongUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, + jobject javaDst, jint dstPos, jint count) { + System_arraycopyTUnchecked<mirror::LongArray, Primitive::kPrimLong>(env, javaSrc, srcPos, + javaDst, dstPos, count); +} + +static void System_arraycopyFloatUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, + jobject javaDst, jint dstPos, jint count) { + System_arraycopyTUnchecked<mirror::FloatArray, Primitive::kPrimFloat>(env, javaSrc, srcPos, + javaDst, dstPos, count); +} + +static void System_arraycopyDoubleUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, + jobject javaDst, jint dstPos, jint count) { + System_arraycopyTUnchecked<mirror::DoubleArray, Primitive::kPrimDouble>(env, javaSrc, srcPos, + javaDst, dstPos, count); +} + +static void System_arraycopyBooleanUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, + jobject javaDst, jint dstPos, jint count) { + System_arraycopyTUnchecked<mirror::BooleanArray, Primitive::kPrimBoolean>(env, javaSrc, srcPos, + javaDst, dstPos, count); } static jint System_identityHashCode(JNIEnv* env, jclass, jobject javaObject) { @@ -178,6 +228,13 @@ static jint System_identityHashCode(JNIEnv* env, jclass, jobject javaObject) { static JNINativeMethod gMethods[] = { NATIVE_METHOD(System, arraycopy, "!(Ljava/lang/Object;ILjava/lang/Object;II)V"), NATIVE_METHOD(System, arraycopyCharUnchecked, "!([CI[CII)V"), + NATIVE_METHOD(System, arraycopyByteUnchecked, "!([BI[BII)V"), + NATIVE_METHOD(System, arraycopyShortUnchecked, "!([SI[SII)V"), + NATIVE_METHOD(System, arraycopyIntUnchecked, "!([II[III)V"), + NATIVE_METHOD(System, arraycopyLongUnchecked, "!([JI[JII)V"), + NATIVE_METHOD(System, arraycopyFloatUnchecked, "!([FI[FII)V"), + NATIVE_METHOD(System, arraycopyDoubleUnchecked, "!([DI[DII)V"), + NATIVE_METHOD(System, arraycopyBooleanUnchecked, "!([ZI[ZII)V"), NATIVE_METHOD(System, identityHashCode, "!(Ljava/lang/Object;)I"), }; diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index ef20d1ce22..e1e133f1be 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -113,6 +113,8 @@ 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; } @@ -565,8 +567,12 @@ bool ParsedOptions::Parse(const Runtime::Options& options, bool ignore_unrecogni } } else if (option == "-Xprofile-type:method") { profiler_options_.profile_type_ = kProfilerMethod; - } else if (option == "-Xprofile-type:dexpc") { - profiler_options_.profile_type_ = kProfilerMethodAndDexPC; + } else if (option == "-Xprofile-type:stack") { + profiler_options_.profile_type_ = kProfilerBoundedStack; + } else if (StartsWith(option, "-Xprofile-max-stack-depth:")) { + if (!ParseUnsignedInteger(option, ':', &profiler_options_.max_stack_depth_)) { + return false; + } } else if (StartsWith(option, "-implicit-checks:")) { std::string checks; if (!ParseStringAfterChar(option, ':', &checks)) { @@ -810,7 +816,8 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, " -Xprofile-start-immediately\n"); UsageMessage(stream, " -Xprofile-top-k-threshold:doublevalue\n"); UsageMessage(stream, " -Xprofile-top-k-change-threshold:doublevalue\n"); - UsageMessage(stream, " -Xprofile-type:{method,dexpc}\n"); + UsageMessage(stream, " -Xprofile-type:{method,stack}\n"); + UsageMessage(stream, " -Xprofile-max-stack-depth:integervalue\n"); UsageMessage(stream, " -Xcompiler:filename\n"); UsageMessage(stream, " -Xcompiler-option dex2oat-option\n"); UsageMessage(stream, " -Ximage-compiler-option dex2oat-option\n"); diff --git a/runtime/profiler.cc b/runtime/profiler.cc index 2cd876a5a3..cecd86f9c4 100644 --- a/runtime/profiler.cc +++ b/runtime/profiler.cc @@ -57,22 +57,66 @@ volatile bool BackgroundMethodSamplingProfiler::shutting_down_ = false; // wakelock or something to modify the run characteristics. This can be done when we // have some performance data after it's been used for a while. +// Walk through the method within depth of max_depth_ on the Java stack +class BoundedStackVisitor : public StackVisitor { + public: + BoundedStackVisitor(std::vector<std::pair<mirror::ArtMethod*, uint32_t>>* stack, + Thread* thread, uint32_t max_depth) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : StackVisitor(thread, NULL), stack_(stack), max_depth_(max_depth), depth_(0) { + } + + bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::ArtMethod* m = GetMethod(); + if (m->IsRuntimeMethod()) { + return true; + } + uint32_t dex_pc_ = GetDexPc(); + stack_->push_back(std::make_pair(m, dex_pc_)); + ++depth_; + if (depth_ < max_depth_) { + return true; + } else { + return false; + } + } + + private: + std::vector<std::pair<mirror::ArtMethod*, uint32_t>>* stack_; + const uint32_t max_depth_; + uint32_t depth_; +}; // This is called from either a thread list traversal or from a checkpoint. Regardless // of which caller, the mutator lock must be held. static void GetSample(Thread* thread, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { BackgroundMethodSamplingProfiler* profiler = reinterpret_cast<BackgroundMethodSamplingProfiler*>(arg); - uint32_t dex_pc; - mirror::ArtMethod* method = thread->GetCurrentMethod(&dex_pc); - if (false && method == nullptr) { - LOG(INFO) << "No current method available"; - std::ostringstream os; - thread->Dump(os); - std::string data(os.str()); - LOG(INFO) << data; - } - profiler->RecordMethod(method, dex_pc); + const ProfilerOptions profile_options = profiler->GetProfilerOptions(); + switch (profile_options.GetProfileType()) { + case kProfilerMethod: { + mirror::ArtMethod* method = thread->GetCurrentMethod(nullptr); + if (false && method == nullptr) { + LOG(INFO) << "No current method available"; + std::ostringstream os; + thread->Dump(os); + std::string data(os.str()); + LOG(INFO) << data; + } + profiler->RecordMethod(method); + break; + } + case kProfilerBoundedStack: { + std::vector<InstructionLocation> stack; + uint32_t max_depth = profile_options.GetMaxStackDepth(); + BoundedStackVisitor bounded_stack_visitor(&stack, thread, max_depth); + bounded_stack_visitor.WalkStack(); + profiler->RecordStack(stack); + break; + } + default: + LOG(INFO) << "This profile type is not implemented."; + } } // A closure that is called by the thread checkpoint code. @@ -359,13 +403,13 @@ BackgroundMethodSamplingProfiler::BackgroundMethodSamplingProfiler( // filtered_methods_.insert("void java.lang.Object.wait(long, int)"); } -// A method has been hit, record its invocation in the method map. -// The mutator_lock must be held (shared) when this is called. -void BackgroundMethodSamplingProfiler::RecordMethod(mirror::ArtMethod* method, uint32_t dex_pc) { +// Filter out methods the profiler doesn't want to record. +// We require mutator lock since some statistics will be updated here. +bool BackgroundMethodSamplingProfiler::ProcessMethod(mirror::ArtMethod* method) { if (method == nullptr) { profile_table_.NullMethod(); // Don't record a nullptr method. - return; + return false; } mirror::Class* cls = method->GetDeclaringClass(); @@ -373,7 +417,7 @@ void BackgroundMethodSamplingProfiler::RecordMethod(mirror::ArtMethod* method, u if (cls->GetClassLoader() == nullptr) { // Don't include things in the boot profile_table_.BootMethod(); - return; + return false; } } @@ -391,14 +435,27 @@ void BackgroundMethodSamplingProfiler::RecordMethod(mirror::ArtMethod* method, u // Don't include specific filtered methods. is_filtered = filtered_methods_.count(method_full_name) != 0; } + return !is_filtered; +} +// A method has been hit, record its invocation in the method map. +// The mutator_lock must be held (shared) when this is called. +void BackgroundMethodSamplingProfiler::RecordMethod(mirror::ArtMethod* method) { // Add to the profile table unless it is filtered out. - if (!is_filtered) { - if (options_.GetProfileType() == kProfilerMethod) { - profile_table_.Put(method); - } else if (options_.GetProfileType() == kProfilerMethodAndDexPC) { - profile_table_.PutDexPC(method, dex_pc); - } + if (ProcessMethod(method)) { + profile_table_.Put(method); + } +} + +// Record the current bounded stack into sampling results. +void BackgroundMethodSamplingProfiler::RecordStack(const std::vector<InstructionLocation>& stack) { + if (stack.size() == 0) { + return; + } + // Get the method on top of the stack. We use this method to perform filtering. + mirror::ArtMethod* method = stack.front().first; + if (ProcessMethod(method)) { + profile_table_.PutStack(stack); } } @@ -419,8 +476,9 @@ ProfileSampleResults::ProfileSampleResults(Mutex& lock) : lock_(lock), num_sampl num_boot_methods_(0) { for (int i = 0; i < kHashSize; i++) { table[i] = nullptr; - dex_table[i] = nullptr; } + method_context_table = nullptr; + stack_trie_root_ = nullptr; } ProfileSampleResults::~ProfileSampleResults() { @@ -444,27 +502,67 @@ void ProfileSampleResults::Put(mirror::ArtMethod* method) { num_samples_++; } -// Add a method with dex pc to the profile table -void ProfileSampleResults::PutDexPC(mirror::ArtMethod* method, uint32_t dex_pc) { +// Add a bounded stack to the profile table. Only the count of the method on +// top of the frame will be increased. +void ProfileSampleResults::PutStack(const std::vector<InstructionLocation>& stack) { MutexLock mu(Thread::Current(), lock_); - uint32_t index = Hash(method); - if (dex_table[index] == nullptr) { - dex_table[index] = new MethodDexPCMap(); - } - MethodDexPCMap::iterator i = dex_table[index]->find(method); - if (i == dex_table[index]->end()) { - DexPCCountMap* dex_pc_map = new DexPCCountMap(); - (*dex_pc_map)[dex_pc] = 1; - (*dex_table[index])[method] = dex_pc_map; - } else { - DexPCCountMap* dex_pc_count = i->second; - DexPCCountMap::iterator dex_pc_i = dex_pc_count->find(dex_pc); - if (dex_pc_i == dex_pc_count->end()) { - (*dex_pc_count)[dex_pc] = 1; + ScopedObjectAccess soa(Thread::Current()); + if (stack_trie_root_ == nullptr) { + // The root of the stack trie is a dummy node so that we don't have to maintain + // a collection of tries. + stack_trie_root_ = new StackTrieNode(); + } + + StackTrieNode* current = stack_trie_root_; + if (stack.size() == 0) { + current->IncreaseCount(); + return; + } + + for (std::vector<InstructionLocation>::const_reverse_iterator iter = stack.rbegin(); + iter != stack.rend(); ++iter) { + InstructionLocation inst_loc = *iter; + mirror::ArtMethod* method = inst_loc.first; + if (method == nullptr) { + // skip null method + continue; + } + uint32_t dex_pc = inst_loc.second; + uint32_t method_idx = method->GetDexMethodIndex(); + const DexFile* dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile(); + MethodReference method_ref(dex_file, method_idx); + StackTrieNode* child = current->FindChild(method_ref, dex_pc); + if (child != nullptr) { + current = child; } else { - dex_pc_i->second++; + uint32_t method_size = 0; + const DexFile::CodeItem* codeitem = method->GetCodeItem(); + if (codeitem != nullptr) { + method_size = codeitem->insns_size_in_code_units_; + } + StackTrieNode* new_node = new StackTrieNode(method_ref, dex_pc, method_size, current); + current->AppendChild(new_node); + current = new_node; } } + + if (current != stack_trie_root_ && current->GetCount() == 0) { + // Insert into method_context table; + if (method_context_table == nullptr) { + method_context_table = new MethodContextMap(); + } + MethodReference method = current->GetMethod(); + MethodContextMap::iterator i = method_context_table->find(method); + if (i == method_context_table->end()) { + TrieNodeSet* node_set = new TrieNodeSet(); + node_set->insert(current); + (*method_context_table)[method] = node_set; + } else { + TrieNodeSet* node_set = i->second; + node_set->insert(current); + } + } + current->IncreaseCount(); num_samples_++; } @@ -506,54 +604,64 @@ uint32_t ProfileSampleResults::Write(std::ostream& os, ProfileDataType type) { } } } - } else if (type == kProfilerMethodAndDexPC) { - for (int i = 0 ; i < kHashSize; i++) { - MethodDexPCMap *dex_map = dex_table[i]; - if (dex_map != nullptr) { - for (const auto &dex_pc_iter : *dex_map) { - mirror::ArtMethod *method = dex_pc_iter.first; - std::string method_name = PrettyMethod(method); - - const DexFile::CodeItem* codeitem = method->GetCodeItem(); - uint32_t method_size = 0; - if (codeitem != nullptr) { - method_size = codeitem->insns_size_in_code_units_; - } - DexPCCountMap* dex_pc_map = dex_pc_iter.second; - uint32_t total_count = 0; - for (const auto &dex_pc_i : *dex_pc_map) { - total_count += dex_pc_i.second; + } else if (type == kProfilerBoundedStack) { + if (method_context_table != nullptr) { + for (const auto &method_iter : *method_context_table) { + MethodReference method = method_iter.first; + TrieNodeSet* node_set = method_iter.second; + std::string method_name = PrettyMethod(method.dex_method_index, *(method.dex_file)); + uint32_t method_size = 0; + uint32_t total_count = 0; + PreviousContextMap new_context_map; + for (const auto &trie_node_i : *node_set) { + StackTrieNode* node = trie_node_i; + method_size = node->GetMethodSize(); + uint32_t count = node->GetCount(); + uint32_t dexpc = node->GetDexPC(); + total_count += count; + + StackTrieNode* current = node->GetParent(); + // We go backward on the trie to retrieve context and dex_pc until the dummy root. + // The format of the context is "method_1@pc_1@method_2@pc_2@..." + std::vector<std::string> context_vector; + while (current != nullptr && current->GetParent() != nullptr) { + context_vector.push_back(StringPrintf("%s@%u", + PrettyMethod(current->GetMethod().dex_method_index, *(current->GetMethod().dex_file)).c_str(), + current->GetDexPC())); + current = current->GetParent(); } + std::string context_sig = Join(context_vector, '@'); + new_context_map[std::make_pair(dexpc, context_sig)] = count; + } - PreviousProfile::iterator pi = previous_.find(method_name); - if (pi != previous_.end()) { - total_count += pi->second.count_; - DexPCCountMap* previous_dex_pc_map = pi->second.dex_pc_map_; - if (previous_dex_pc_map != nullptr) { - for (const auto &dex_pc_i : *previous_dex_pc_map) { - uint32_t dex_pc = dex_pc_i.first; - uint32_t count = dex_pc_i.second; - DexPCCountMap::iterator di = dex_pc_map->find(dex_pc); - if (di == dex_pc_map->end()) { - (*dex_pc_map)[dex_pc] = count; - } else { - di->second += count; - } + PreviousProfile::iterator pi = previous_.find(method_name); + if (pi != previous_.end()) { + total_count += pi->second.count_; + PreviousContextMap* previous_context_map = pi->second.context_map_; + if (previous_context_map != nullptr) { + for (const auto &context_i : *previous_context_map) { + uint32_t count = context_i.second; + PreviousContextMap::iterator ci = new_context_map.find(context_i.first); + if (ci == new_context_map.end()) { + new_context_map[context_i.first] = count; + } else { + ci->second += count; } } - delete previous_dex_pc_map; - previous_.erase(pi); - } - std::vector<std::string> dex_pc_count_vector; - for (const auto &dex_pc_i : *dex_pc_map) { - dex_pc_count_vector.push_back(StringPrintf("%u:%u", dex_pc_i.first, dex_pc_i.second)); } - // We write out profile data with dex pc information in the following format: - // "method/total_count/size/[pc_1:count_1,pc_2:count_2,...]". - os << StringPrintf("%s/%u/%u/[%s]\n", method_name.c_str(), total_count, - method_size, Join(dex_pc_count_vector, ',').c_str()); - ++num_methods; + delete previous_context_map; + previous_.erase(pi); + } + // We write out profile data with dex pc and context information in the following format: + // "method/total_count/size/[pc_1:count_1:context_1#pc_2:count_2:context_2#...]". + std::vector<std::string> context_count_vector; + for (const auto &context_i : new_context_map) { + context_count_vector.push_back(StringPrintf("%u:%u:%s", context_i.first.first, + context_i.second, context_i.first.second.c_str())); } + os << StringPrintf("%s/%u/%u/[%s]\n", method_name.c_str(), total_count, + method_size, Join(context_count_vector, '#').c_str()); + ++num_methods; } } } @@ -562,15 +670,16 @@ uint32_t ProfileSampleResults::Write(std::ostream& os, ProfileDataType type) { for (const auto &pi : previous_) { if (type == kProfilerMethod) { os << StringPrintf("%s/%u/%u\n", pi.first.c_str(), pi.second.count_, pi.second.method_size_); - } else if (type == kProfilerMethodAndDexPC) { + } else if (type == kProfilerBoundedStack) { os << StringPrintf("%s/%u/%u/[", pi.first.c_str(), pi.second.count_, pi.second.method_size_); - DexPCCountMap* previous_dex_pc_map = pi.second.dex_pc_map_; - if (previous_dex_pc_map != nullptr) { - std::vector<std::string> dex_pc_count_vector; - for (const auto &dex_pc_i : *previous_dex_pc_map) { - dex_pc_count_vector.push_back(StringPrintf("%u:%u", dex_pc_i.first, dex_pc_i.second)); + PreviousContextMap* previous_context_map = pi.second.context_map_; + if (previous_context_map != nullptr) { + std::vector<std::string> context_count_vector; + for (const auto &context_i : *previous_context_map) { + context_count_vector.push_back(StringPrintf("%u:%u:%s", context_i.first.first, + context_i.second, context_i.first.second.c_str())); } - os << Join(dex_pc_count_vector, ','); + os << Join(context_count_vector, '#'); } os << "]\n"; } @@ -586,18 +695,21 @@ void ProfileSampleResults::Clear() { for (int i = 0; i < kHashSize; i++) { delete table[i]; table[i] = nullptr; - if (dex_table[i] != nullptr) { - for (auto &di : *dex_table[i]) { - delete di.second; - di.second = nullptr; - } + } + if (stack_trie_root_ != nullptr) { + stack_trie_root_->DeleteChildren(); + delete stack_trie_root_; + stack_trie_root_ = nullptr; + if (method_context_table != nullptr) { + delete method_context_table; + method_context_table = nullptr; } - delete dex_table[i]; - dex_table[i] = nullptr; } for (auto &pi : previous_) { - delete pi.second.dex_pc_map_; - pi.second.dex_pc_map_ = nullptr; + if (pi.second.context_map_ != nullptr) { + delete pi.second.context_map_; + pi.second.context_map_ = nullptr; + } } previous_.clear(); } @@ -658,21 +770,30 @@ void ProfileSampleResults::ReadPrevious(int fd, ProfileDataType type) { std::string methodname = info[0]; uint32_t total_count = atoi(info[1].c_str()); uint32_t size = atoi(info[2].c_str()); - DexPCCountMap* dex_pc_map = nullptr; - if (type == kProfilerMethodAndDexPC && info.size() == 4) { - dex_pc_map = new DexPCCountMap(); - std::string dex_pc_counts_str = info[3].substr(1, info[3].size() - 2); - std::vector<std::string> dex_pc_count_pairs; - Split(dex_pc_counts_str, ',', dex_pc_count_pairs); - for (uint32_t i = 0; i < dex_pc_count_pairs.size(); ++i) { - std::vector<std::string> dex_pc_count; - Split(dex_pc_count_pairs[i], ':', dex_pc_count); - uint32_t dex_pc = atoi(dex_pc_count[0].c_str()); - uint32_t count = atoi(dex_pc_count[1].c_str()); - (*dex_pc_map)[dex_pc] = count; + PreviousContextMap* context_map = nullptr; + if (type == kProfilerBoundedStack && info.size() == 4) { + context_map = new PreviousContextMap(); + std::string context_counts_str = info[3].substr(1, info[3].size() - 2); + std::vector<std::string> context_count_pairs; + Split(context_counts_str, '#', context_count_pairs); + for (uint32_t i = 0; i < context_count_pairs.size(); ++i) { + std::vector<std::string> context_count; + Split(context_count_pairs[i], ':', context_count); + if (context_count.size() == 2) { + // Handles the situtation when the profile file doesn't contain context information. + uint32_t dexpc = atoi(context_count[0].c_str()); + uint32_t count = atoi(context_count[1].c_str()); + (*context_map)[std::make_pair(dexpc, "")] = count; + } else { + // Handles the situtation when the profile file contains context information. + uint32_t dexpc = atoi(context_count[0].c_str()); + uint32_t count = atoi(context_count[1].c_str()); + std::string context = context_count[2]; + (*context_map)[std::make_pair(dexpc, context)] = count; + } } } - previous_[methodname] = PreviousValue(total_count, size, dex_pc_map); + previous_[methodname] = PreviousValue(total_count, size, context_map); } } @@ -772,4 +893,24 @@ bool ProfileFile::GetTopKSamples(std::set<std::string>& topKSamples, double topK return true; } +StackTrieNode* StackTrieNode::FindChild(MethodReference method, uint32_t dex_pc) { + if (children_.size() == 0) { + return nullptr; + } + // Create a dummy node for searching. + StackTrieNode* node = new StackTrieNode(method, dex_pc, 0, nullptr); + std::set<StackTrieNode*, StackTrieNodeComparator>::iterator i = children_.find(node); + delete node; + return (i == children_.end()) ? nullptr : *i; +} + +void StackTrieNode::DeleteChildren() { + for (auto &child : children_) { + if (child != nullptr) { + child->DeleteChildren(); + delete child; + } + } +} + } // namespace art diff --git a/runtime/profiler.h b/runtime/profiler.h index 396dd23fe3..ae51c87ce9 100644 --- a/runtime/profiler.h +++ b/runtime/profiler.h @@ -31,6 +31,7 @@ #include "profiler_options.h" #include "os.h" #include "safe_map.h" +#include "method_reference.h" namespace art { @@ -40,6 +41,57 @@ namespace mirror { } // namespace mirror class Thread; +typedef std::pair<mirror::ArtMethod*, uint32_t> InstructionLocation; + +// This class stores the sampled bounded stacks in a trie structure. A path of the trie represents +// a particular context with the method on top of the stack being a leaf or an internal node of the +// trie rather than the root. +class StackTrieNode { + public: + StackTrieNode(MethodReference method, uint32_t dex_pc, uint32_t method_size, + StackTrieNode* parent) : + parent_(parent), method_(method), dex_pc_(dex_pc), + count_(0), method_size_(method_size) { + } + StackTrieNode() : parent_(nullptr), method_(nullptr, 0), + dex_pc_(0), count_(0), method_size_(0) { + } + StackTrieNode* GetParent() { return parent_; } + MethodReference GetMethod() { return method_; } + uint32_t GetCount() { return count_; } + uint32_t GetDexPC() { return dex_pc_; } + uint32_t GetMethodSize() { return method_size_; } + void AppendChild(StackTrieNode* child) { children_.insert(child); } + StackTrieNode* FindChild(MethodReference method, uint32_t dex_pc); + void DeleteChildren(); + void IncreaseCount() { ++count_; } + + private: + // Comparator for stack trie node. + struct StackTrieNodeComparator { + bool operator()(StackTrieNode* node1, StackTrieNode* node2) const { + MethodReference mr1 = node1->GetMethod(); + MethodReference mr2 = node2->GetMethod(); + if (mr1.dex_file == mr2.dex_file) { + if (mr1.dex_method_index == mr2.dex_method_index) { + return node1->GetDexPC() < node2->GetDexPC(); + } else { + return mr1.dex_method_index < mr2.dex_method_index; + } + } else { + return mr1.dex_file < mr2.dex_file; + } + } + }; + + std::set<StackTrieNode*, StackTrieNodeComparator> children_; + StackTrieNode* parent_; + MethodReference method_; + uint32_t dex_pc_; + uint32_t count_; + uint32_t method_size_; +}; + // // This class holds all the results for all runs of the profiler. It also // counts the number of null methods (where we can't determine the method) and @@ -53,7 +105,7 @@ class ProfileSampleResults { ~ProfileSampleResults(); void Put(mirror::ArtMethod* method); - void PutDexPC(mirror::ArtMethod* method, uint32_t pc); + void PutStack(const std::vector<InstructionLocation>& stack_dump); uint32_t Write(std::ostream &os, ProfileDataType type); void ReadPrevious(int fd, ProfileDataType type); void Clear(); @@ -72,18 +124,21 @@ class ProfileSampleResults { typedef std::map<mirror::ArtMethod*, uint32_t> Map; // Map of method vs its count. Map *table[kHashSize]; - typedef std::map<uint32_t, uint32_t> DexPCCountMap; // Map of dex pc vs its count - // Map of method vs dex pc counts in the method. - typedef std::map<mirror::ArtMethod*, DexPCCountMap*> MethodDexPCMap; - MethodDexPCMap *dex_table[kHashSize]; + typedef std::set<StackTrieNode*> TrieNodeSet; + // Map of method hit by profiler vs the set of stack trie nodes for this method. + typedef std::map<MethodReference, TrieNodeSet*, MethodReferenceComparator> MethodContextMap; + MethodContextMap *method_context_table; + StackTrieNode* stack_trie_root_; // Root of the trie that stores sampled stack information. + // Map from <pc, context> to counts. + typedef std::map<std::pair<uint32_t, std::string>, uint32_t> PreviousContextMap; struct PreviousValue { - PreviousValue() : count_(0), method_size_(0), dex_pc_map_(nullptr) {} - PreviousValue(uint32_t count, uint32_t method_size, DexPCCountMap* dex_pc_map) - : count_(count), method_size_(method_size), dex_pc_map_(dex_pc_map) {} + PreviousValue() : count_(0), method_size_(0), context_map_(nullptr) {} + PreviousValue(uint32_t count, uint32_t method_size, PreviousContextMap* context_map) + : count_(count), method_size_(method_size), context_map_(context_map) {} uint32_t count_; uint32_t method_size_; - DexPCCountMap* dex_pc_map_; + PreviousContextMap* context_map_; }; typedef std::map<std::string, PreviousValue> PreviousProfile; @@ -121,7 +176,10 @@ class BackgroundMethodSamplingProfiler { static void Stop() LOCKS_EXCLUDED(Locks::profiler_lock_, wait_lock_); static void Shutdown() LOCKS_EXCLUDED(Locks::profiler_lock_); - void RecordMethod(mirror::ArtMethod *method, uint32_t pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void RecordMethod(mirror::ArtMethod *method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void RecordStack(const std::vector<InstructionLocation>& stack) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool ProcessMethod(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const ProfilerOptions& GetProfilerOptions() const { return options_; } Barrier& GetBarrier() { return *profiler_barrier_; diff --git a/runtime/profiler_options.h b/runtime/profiler_options.h index 0b6300330f..e3ef69739b 100644 --- a/runtime/profiler_options.h +++ b/runtime/profiler_options.h @@ -24,7 +24,7 @@ namespace art { enum ProfileDataType { kProfilerMethod, // Method only - kProfilerMethodAndDexPC, // Method with Dex PC + kProfilerBoundedStack, // Methods with Dex PC on top of the stack }; class ProfilerOptions { @@ -38,6 +38,7 @@ class ProfilerOptions { static constexpr double kDefaultTopKThreshold = 90.0; static constexpr double kDefaultChangeInTopKThreshold = 10.0; static constexpr ProfileDataType kDefaultProfileData = kProfilerMethod; + static constexpr uint32_t kDefaultMaxStackDepth = 3; ProfilerOptions() : enabled_(kDefaultEnabled), @@ -48,7 +49,8 @@ class ProfilerOptions { start_immediately_(kDefaultStartImmediately), top_k_threshold_(kDefaultTopKThreshold), top_k_change_threshold_(kDefaultChangeInTopKThreshold), - profile_type_(kDefaultProfileData) {} + profile_type_(kDefaultProfileData), + max_stack_depth_(kDefaultMaxStackDepth) {} ProfilerOptions(bool enabled, uint32_t period_s, @@ -58,7 +60,8 @@ class ProfilerOptions { bool start_immediately, double top_k_threshold, double top_k_change_threshold, - ProfileDataType profile_type): + ProfileDataType profile_type, + uint32_t max_stack_depth): enabled_(enabled), period_s_(period_s), duration_s_(duration_s), @@ -67,7 +70,8 @@ class ProfilerOptions { start_immediately_(start_immediately), top_k_threshold_(top_k_threshold), top_k_change_threshold_(top_k_change_threshold), - profile_type_(profile_type) {} + profile_type_(profile_type), + max_stack_depth_(max_stack_depth) {} bool IsEnabled() const { return enabled_; @@ -105,6 +109,10 @@ class ProfilerOptions { return profile_type_; } + uint32_t GetMaxStackDepth() const { + return max_stack_depth_; + } + private: friend std::ostream & operator<<(std::ostream &os, const ProfilerOptions& po) { os << "enabled=" << po.enabled_ @@ -115,7 +123,8 @@ class ProfilerOptions { << ", start_immediately=" << po.start_immediately_ << ", top_k_threshold=" << po.top_k_threshold_ << ", top_k_change_threshold=" << po.top_k_change_threshold_ - << ", profile_type=" << po.profile_type_; + << ", profile_type=" << po.profile_type_ + << ", max_stack_depth=" << po.max_stack_depth_; return os; } @@ -139,6 +148,8 @@ class ProfilerOptions { double top_k_change_threshold_; // The type of profile data dumped to the disk. ProfileDataType profile_type_; + // The max depth of the stack collected by the profiler + uint32_t max_stack_depth_; }; } // namespace art diff --git a/runtime/read_barrier.h b/runtime/read_barrier.h index 451d13c5db..ed5db4e4a5 100644 --- a/runtime/read_barrier.h +++ b/runtime/read_barrier.h @@ -33,11 +33,15 @@ namespace mirror { class ReadBarrier { public: + // It's up to the implementation whether the given field gets + // updated whereas the return value must be an updated reference. template <typename MirrorType, ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ALWAYS_INLINE static MirrorType* Barrier( mirror::Object* obj, MemberOffset offset, mirror::HeapReference<MirrorType>* ref_addr) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // It's up to the implementation whether the given root gets updated + // whereas the return value must be an updated reference. template <typename MirrorType, ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ALWAYS_INLINE static MirrorType* BarrierForRoot(MirrorType** root) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc index 11527fa2fe..cd35863543 100644 --- a/runtime/reference_table.cc +++ b/runtime/reference_table.cc @@ -24,6 +24,7 @@ #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/string-inl.h" +#include "read_barrier.h" #include "thread.h" #include "utils.h" @@ -51,7 +52,9 @@ void ReferenceTable::Add(mirror::Object* obj) { void ReferenceTable::Remove(mirror::Object* obj) { // We iterate backwards on the assumption that references are LIFO. for (int i = entries_.size() - 1; i >= 0; --i) { - if (entries_[i] == obj) { + mirror::Object* entry = + ReadBarrier::BarrierForRoot<mirror::Object, kWithReadBarrier>(&entries_[i]); + if (entry == obj) { entries_.erase(entries_.begin() + i); return; } @@ -140,12 +143,12 @@ size_t ReferenceTable::Size() const { return entries_.size(); } -void ReferenceTable::Dump(std::ostream& os) const { +void ReferenceTable::Dump(std::ostream& os) { os << name_ << " reference table dump:\n"; Dump(os, entries_); } -void ReferenceTable::Dump(std::ostream& os, const Table& entries) { +void ReferenceTable::Dump(std::ostream& os, Table& entries) { if (entries.empty()) { os << " (empty)\n"; return; @@ -160,7 +163,8 @@ void ReferenceTable::Dump(std::ostream& os, const Table& entries) { } os << " Last " << (count - first) << " entries (of " << count << "):\n"; for (int idx = count - 1; idx >= first; --idx) { - mirror::Object* ref = entries[idx]; + mirror::Object* ref = + ReadBarrier::BarrierForRoot<mirror::Object, kWithReadBarrier>(&entries[idx]); if (ref == NULL) { continue; } @@ -194,7 +198,12 @@ void ReferenceTable::Dump(std::ostream& os, const Table& entries) { } // Make a copy of the table and sort it. - Table sorted_entries(entries.begin(), entries.end()); + Table sorted_entries; + for (size_t i = 0; i < entries.size(); ++i) { + mirror::Object* entry = + ReadBarrier::BarrierForRoot<mirror::Object, kWithReadBarrier>(&entries[i]); + sorted_entries.push_back(entry); + } std::sort(sorted_entries.begin(), sorted_entries.end(), ObjectComparator()); // Remove any uninteresting stuff from the list. The sort moved them all to the end. diff --git a/runtime/reference_table.h b/runtime/reference_table.h index 45309c9d99..1cd0999f26 100644 --- a/runtime/reference_table.h +++ b/runtime/reference_table.h @@ -39,19 +39,19 @@ class ReferenceTable { ReferenceTable(const char* name, size_t initial_size, size_t max_size); ~ReferenceTable(); - void Add(mirror::Object* obj); + void Add(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void Remove(mirror::Object* obj); + void Remove(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); size_t Size() const; - void Dump(std::ostream& os) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void VisitRoots(RootCallback* visitor, void* arg, uint32_t tid, RootType root_type); private: typedef std::vector<mirror::Object*> Table; - static void Dump(std::ostream& os, const Table& entries) + static void Dump(std::ostream& os, Table& entries) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); friend class IndirectReferenceTable; // For Dump. diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h index 7ce68c624b..d69162334d 100644 --- a/runtime/scoped_thread_state_change.h +++ b/runtime/scoped_thread_state_change.h @@ -146,7 +146,8 @@ class ScopedObjectAccessAlreadyRunnable { Locks::mutator_lock_->AssertSharedHeld(Self()); DCHECK(IsRunnable()); // Don't work with raw objects in non-runnable states. CHECK(!kMovingFields); - return reinterpret_cast<mirror::ArtField*>(fid); + mirror::ArtField* field = reinterpret_cast<mirror::ArtField*>(fid); + return ReadBarrier::BarrierForRoot<mirror::ArtField, kWithReadBarrier>(&field); } jfieldID EncodeField(mirror::ArtField* field) const @@ -162,7 +163,8 @@ class ScopedObjectAccessAlreadyRunnable { Locks::mutator_lock_->AssertSharedHeld(Self()); DCHECK(IsRunnable()); // Don't work with raw objects in non-runnable states. CHECK(!kMovingMethods); - return reinterpret_cast<mirror::ArtMethod*>(mid); + mirror::ArtMethod* method = reinterpret_cast<mirror::ArtMethod*>(mid); + return ReadBarrier::BarrierForRoot<mirror::ArtMethod, kWithReadBarrier>(&method); } jmethodID EncodeMethod(mirror::ArtMethod* method) const diff --git a/runtime/thread.cc b/runtime/thread.cc index 3f8f4a3dc0..ca8c2d7bf3 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -974,9 +974,14 @@ void Thread::DumpStack(std::ostream& os) const { // TODO: we call this code when dying but may not have suspended the thread ourself. The // IsSuspended check is therefore racy with the use for dumping (normally we inhibit // the race with the thread_suspend_count_lock_). - // No point dumping for an abort in debug builds where we'll hit the not suspended check in stack. - bool dump_for_abort = (gAborting > 0) && !kIsDebugBuild; - if (this == Thread::Current() || IsSuspended() || dump_for_abort) { + bool dump_for_abort = (gAborting > 0); + bool safe_to_dump = (this == Thread::Current() || IsSuspended()); + if (!kIsDebugBuild) { + // We always want to dump the stack for an abort, however, there is no point dumping another + // thread's stack in debug builds where we'll hit the not suspended check in the stack walk. + safe_to_dump = (safe_to_dump || dump_for_abort); + } + if (safe_to_dump) { // If we're currently in native code, dump that stack before dumping the managed stack. if (dump_for_abort || ShouldShowNativeStack(this)) { DumpKernelStack(os, GetTid(), " kernel: ", false); @@ -1298,9 +1303,7 @@ mirror::Object* Thread::DecodeJObject(jobject obj) const { } } else if (kind == kGlobal) { JavaVMExt* const vm = Runtime::Current()->GetJavaVM(); - // Strong global references do not need a read barrier. - result = vm->globals.SynchronizedGet<kWithoutReadBarrier>( - const_cast<Thread*>(this), &vm->globals_lock, ref); + result = vm->globals.SynchronizedGet(const_cast<Thread*>(this), &vm->globals_lock, ref); } else { DCHECK_EQ(kind, kWeakGlobal); result = Runtime::Current()->GetJavaVM()->DecodeWeakGlobal(const_cast<Thread*>(this), ref); diff --git a/runtime/utils.cc b/runtime/utils.cc index f60f795e18..d038571e96 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -468,11 +468,12 @@ std::string PrettySize(int64_t byte_count) { negative_str, byte_count / kBytesPerUnit[i], kUnitStrings[i]); } -std::string PrettyDuration(uint64_t nano_duration) { +std::string PrettyDuration(uint64_t nano_duration, size_t max_fraction_digits) { if (nano_duration == 0) { return "0"; } else { - return FormatDuration(nano_duration, GetAppropriateTimeUnit(nano_duration)); + return FormatDuration(nano_duration, GetAppropriateTimeUnit(nano_duration), + max_fraction_digits); } } @@ -509,45 +510,43 @@ uint64_t GetNsToTimeUnitDivisor(TimeUnit time_unit) { return 0; } -std::string FormatDuration(uint64_t nano_duration, TimeUnit time_unit) { - const char* unit = NULL; +std::string FormatDuration(uint64_t nano_duration, TimeUnit time_unit, + size_t max_fraction_digits) { + const char* unit = nullptr; uint64_t divisor = GetNsToTimeUnitDivisor(time_unit); - uint32_t zero_fill = 1; switch (time_unit) { case kTimeUnitSecond: unit = "s"; - zero_fill = 9; break; case kTimeUnitMillisecond: unit = "ms"; - zero_fill = 6; break; case kTimeUnitMicrosecond: unit = "us"; - zero_fill = 3; break; case kTimeUnitNanosecond: unit = "ns"; - zero_fill = 0; break; } - - uint64_t whole_part = nano_duration / divisor; + const uint64_t whole_part = nano_duration / divisor; uint64_t fractional_part = nano_duration % divisor; if (fractional_part == 0) { return StringPrintf("%" PRIu64 "%s", whole_part, unit); } else { - while ((fractional_part % 1000) == 0) { - zero_fill -= 3; - fractional_part /= 1000; - } - if (zero_fill == 3) { - return StringPrintf("%" PRIu64 ".%03" PRIu64 "%s", whole_part, fractional_part, unit); - } else if (zero_fill == 6) { - return StringPrintf("%" PRIu64 ".%06" PRIu64 "%s", whole_part, fractional_part, unit); - } else { - return StringPrintf("%" PRIu64 ".%09" PRIu64 "%s", whole_part, fractional_part, unit); + static constexpr size_t kMaxDigits = 30; + size_t avail_digits = kMaxDigits; + char fraction_buffer[kMaxDigits]; + char* ptr = fraction_buffer; + uint64_t multiplier = 10; + // This infinite loops if fractional part is 0. + while (avail_digits > 1 && fractional_part * multiplier < divisor) { + multiplier *= 10; + *ptr++ = '0'; + avail_digits--; } + snprintf(ptr, avail_digits, "%" PRIu64, fractional_part); + fraction_buffer[std::min(kMaxDigits - 1, max_fraction_digits)] = '\0'; + return StringPrintf("%" PRIu64 ".%s%s", whole_part, fraction_buffer, unit); } } @@ -1235,6 +1234,7 @@ std::string GetSystemImageFilename(const char* location, const InstructionSet is std::string DexFilenameToOdexFilename(const std::string& location, const InstructionSet isa) { // location = /foo/bar/baz.jar // odex_location = /foo/bar/<isa>/baz.odex + CHECK_GE(location.size(), 4U) << location; // must be at least .123 std::string odex_location(location); InsertIsaDirectory(isa, &odex_location); diff --git a/runtime/utils.h b/runtime/utils.h index 6d52459ec8..68ea47541b 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -265,10 +265,11 @@ std::string PrettySize(int64_t size_in_bytes); // Returns a human-readable time string which prints every nanosecond while trying to limit the // number of trailing zeros. Prints using the largest human readable unit up to a second. // e.g. "1ms", "1.000000001s", "1.001us" -std::string PrettyDuration(uint64_t nano_duration); +std::string PrettyDuration(uint64_t nano_duration, size_t max_fraction_digits = 3); // Format a nanosecond time to specified units. -std::string FormatDuration(uint64_t nano_duration, TimeUnit time_unit); +std::string FormatDuration(uint64_t nano_duration, TimeUnit time_unit, + size_t max_fraction_digits); // Get the appropriate unit for a nanosecond duration. TimeUnit GetAppropriateTimeUnit(uint64_t nano_duration); @@ -400,6 +401,7 @@ std::string GetSystemImageFilename(const char* location, InstructionSet isa); // Returns an .odex file name next adjacent to the dex location. // For example, for "/foo/bar/baz.jar", return "/foo/bar/<isa>/baz.odex". +// Note: does not support multidex location strings. std::string DexFilenameToOdexFilename(const std::string& location, InstructionSet isa); // Check whether the given magic matches a known file type. diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc index 4a1e477bbd..7cd5980c44 100644 --- a/runtime/utils_test.cc +++ b/runtime/utils_test.cc @@ -171,14 +171,15 @@ TEST_F(UtilsTest, PrettyDuration) { EXPECT_EQ("10s", PrettyDuration(10 * one_sec)); EXPECT_EQ("100s", PrettyDuration(100 * one_sec)); EXPECT_EQ("1.001s", PrettyDuration(1 * one_sec + one_ms)); - EXPECT_EQ("1.000001s", PrettyDuration(1 * one_sec + one_us)); - EXPECT_EQ("1.000000001s", PrettyDuration(1 * one_sec + 1)); + EXPECT_EQ("1.000001s", PrettyDuration(1 * one_sec + one_us, 6)); + EXPECT_EQ("1.000000001s", PrettyDuration(1 * one_sec + 1, 9)); + EXPECT_EQ("1.000s", PrettyDuration(1 * one_sec + one_us, 3)); EXPECT_EQ("1ms", PrettyDuration(1 * one_ms)); EXPECT_EQ("10ms", PrettyDuration(10 * one_ms)); EXPECT_EQ("100ms", PrettyDuration(100 * one_ms)); EXPECT_EQ("1.001ms", PrettyDuration(1 * one_ms + one_us)); - EXPECT_EQ("1.000001ms", PrettyDuration(1 * one_ms + 1)); + EXPECT_EQ("1.000001ms", PrettyDuration(1 * one_ms + 1, 6)); EXPECT_EQ("1us", PrettyDuration(1 * one_us)); EXPECT_EQ("10us", PrettyDuration(10 * one_us)); diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index c9c3bbabdf..89cfcdd1de 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -717,13 +717,28 @@ bool MethodVerifier::VerifyInstruction(const Instruction* inst, uint32_t code_of case Instruction::kVerifySwitchTargets: result = result && CheckSwitchTargets(code_offset); break; + case Instruction::kVerifyVarArgNonZero: + // Fall-through. case Instruction::kVerifyVarArg: { + if (inst->GetVerifyExtraFlags() == Instruction::kVerifyVarArgNonZero && inst->VRegA() <= 0) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid arg count (" << inst->VRegA() << ") in " + "non-range invoke"; + return false; + } uint32_t args[Instruction::kMaxVarArgRegs]; inst->GetVarArgs(args); result = result && CheckVarArgRegs(inst->VRegA(), args); break; } + case Instruction::kVerifyVarArgRangeNonZero: + // Fall-through. case Instruction::kVerifyVarArgRange: + if (inst->GetVerifyExtraFlags() == Instruction::kVerifyVarArgRangeNonZero && + inst->VRegA() <= 0) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid arg count (" << inst->VRegA() << ") in " + "range invoke"; + return false; + } result = result && CheckVarArgRangeRegs(inst->VRegA(), inst->VRegC()); break; case Instruction::kVerifyError: @@ -1219,6 +1234,12 @@ bool MethodVerifier::SetTypesFromSignature() { break; case 'J': case 'D': { + if (cur_arg + 1 >= expected_args) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected " << expected_args + << " args, found more (" << descriptor << ")"; + return false; + } + const RegType& lo_half = descriptor[0] == 'J' ? reg_types_.LongLo() : reg_types_.DoubleLo(); const RegType& hi_half = descriptor[0] == 'J' ? reg_types_.LongHi() : reg_types_.DoubleHi(); reg_line->SetRegisterTypeWide(arg_start + cur_arg, lo_half, hi_half); diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc index d21f39bf67..556056ce04 100644 --- a/runtime/verifier/register_line.cc +++ b/runtime/verifier/register_line.cc @@ -55,7 +55,7 @@ bool RegisterLine::SetRegisterType(uint32_t vdst, const RegType& new_type) { bool RegisterLine::SetRegisterTypeWide(uint32_t vdst, const RegType& new_type1, const RegType& new_type2) { - DCHECK_LT(vdst, num_regs_); + DCHECK_LT(vdst + 1, num_regs_); if (!new_type1.CheckWidePair(new_type2)) { verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Invalid wide pair '" << new_type1 << "' '" << new_type2 << "'"; diff --git a/sigchainlib/Android.mk b/sigchainlib/Android.mk index cb1778dc8e..8e25339d9e 100644 --- a/sigchainlib/Android.mk +++ b/sigchainlib/Android.mk @@ -16,7 +16,7 @@ LOCAL_PATH:= $(call my-dir) -include art/build/Android.common.mk +include art/build/Android.common_build.mk include $(CLEAR_VARS) LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) @@ -24,6 +24,7 @@ LOCAL_MODULE_TAGS := optional LOCAL_CFLAGS += $(ART_TARGET_CFLAGS) LOCAL_SRC_FILES := sigchain.cc LOCAL_MODULE:= libsigchain -LOCAL_SHARED_LIBRARIES += liblog libdl -LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk +LOCAL_SHARED_LIBRARIES := liblog libdl +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common_build.mk include $(BUILD_SHARED_LIBRARY) diff --git a/test/003-omnibus-opcodes/build b/test/003-omnibus-opcodes/build index 9dff837ab6..f909fb2219 100644 --- a/test/003-omnibus-opcodes/build +++ b/test/003-omnibus-opcodes/build @@ -22,5 +22,5 @@ ${JAVAC} -d classes `find src -name '*.java'` rm classes/UnresClass.class ${JAVAC} -d classes `find src2 -name '*.java'` -${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes +${DX} -JXmx256m --debug --dex --output=classes.dex classes zip $TEST_NAME.jar classes.dex diff --git a/test/056-const-string-jumbo/build b/test/056-const-string-jumbo/build index a12c9d32b5..ef286d140e 100644 --- a/test/056-const-string-jumbo/build +++ b/test/056-const-string-jumbo/build @@ -42,5 +42,5 @@ function writeFile(name, start, end) { mkdir classes ${JAVAC} -d classes src/*.java -${DX} -JXmx500m --debug --dex --no-optimize --positions=none --no-locals --dump-to=classes.lst --output=classes.dex classes +${DX} -JXmx500m --debug --dex --no-optimize --positions=none --no-locals --output=classes.dex classes zip $TEST_NAME.jar classes.dex diff --git a/test/112-double-math/expected.txt b/test/112-double-math/expected.txt new file mode 100644 index 0000000000..1e10a952bb --- /dev/null +++ b/test/112-double-math/expected.txt @@ -0,0 +1 @@ +cond_neg_double PASSED diff --git a/test/112-double-math/info.txt b/test/112-double-math/info.txt new file mode 100644 index 0000000000..a32f4e0d85 --- /dev/null +++ b/test/112-double-math/info.txt @@ -0,0 +1 @@ +This checks the neg_double bytecode. diff --git a/test/112-double-math/src/Main.java b/test/112-double-math/src/Main.java new file mode 100644 index 0000000000..8172dfab5a --- /dev/null +++ b/test/112-double-math/src/Main.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2007 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 double cond_neg_double(double value, boolean cond) { + return cond ? -value : value; + } + + public static void main(String args[]) { + double result = cond_neg_double(-1.0d, true); + + if (Double.doubleToRawLongBits(result) == 0x3ff0000000000000L) { + System.out.println("cond_neg_double PASSED"); + } else { + System.out.println("cond_neg_double FAILED " + result); + } + } +} diff --git a/test/113-multidex/build b/test/113-multidex/build new file mode 100644 index 0000000000..ec8706ea02 --- /dev/null +++ b/test/113-multidex/build @@ -0,0 +1,32 @@ +#!/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 + +mkdir classes + +# All except Main +${JAVAC} -d classes `find src -name '*.java'` +rm classes/Main.class +${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes + +# Only Main +${JAVAC} -d classes `find src -name '*.java'` +rm classes/Second.class classes/FillerA.class classes/FillerB.class classes/Inf*.class +${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex classes + +zip $TEST_NAME.jar classes.dex classes2.dex diff --git a/test/113-multidex/expected.txt b/test/113-multidex/expected.txt new file mode 100644 index 0000000000..603e911806 --- /dev/null +++ b/test/113-multidex/expected.txt @@ -0,0 +1,12 @@ +FillerA +Second +Second::zcall +Second::zcall1 +Second::zcall2 +Second::zcall3 +Second::zcall4 +Second::zcall5 +Second::zcall6 +Second::zcall7 +Second::zcall8 +Second::zcall9 diff --git a/test/113-multidex/info.txt b/test/113-multidex/info.txt new file mode 100644 index 0000000000..d0a4ac1ba3 --- /dev/null +++ b/test/113-multidex/info.txt @@ -0,0 +1,2 @@ +Test whether we can run code from an application split into multiple dex files (similar to +MultiDex). diff --git a/test/113-multidex/src/FillerA.java b/test/113-multidex/src/FillerA.java new file mode 100644 index 0000000000..d169018d78 --- /dev/null +++ b/test/113-multidex/src/FillerA.java @@ -0,0 +1,23 @@ +/* + * 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. + */ + +public class FillerA { + public void methodA() { + } + + public void methodB() { + } +} diff --git a/test/113-multidex/src/FillerB.java b/test/113-multidex/src/FillerB.java new file mode 100644 index 0000000000..ec3ac9d7d1 --- /dev/null +++ b/test/113-multidex/src/FillerB.java @@ -0,0 +1,23 @@ +/* + * 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. + */ + +public class FillerB { + public void methodC() { + } + + public void methodD() { + } +} diff --git a/test/113-multidex/src/Inf1.java b/test/113-multidex/src/Inf1.java new file mode 100644 index 0000000000..3deb6b4430 --- /dev/null +++ b/test/113-multidex/src/Inf1.java @@ -0,0 +1,28 @@ +/* + * 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. + */ + +public interface Inf1 { + public void zcall(); + public void zcall1(); + public void zcall2(); + public void zcall3(); + public void zcall4(); + public void zcall5(); + public void zcall6(); + public void zcall7(); + public void zcall8(); + public void zcall9(); +}
\ No newline at end of file diff --git a/test/113-multidex/src/Inf2.java b/test/113-multidex/src/Inf2.java new file mode 100644 index 0000000000..ac09509482 --- /dev/null +++ b/test/113-multidex/src/Inf2.java @@ -0,0 +1,28 @@ +/* + * 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. + */ + +public interface Inf2 { + public void zcall(); + public void zcall1(); + public void zcall2(); + public void zcall3(); + public void zcall4(); + public void zcall5(); + public void zcall6(); + public void zcall7(); + public void zcall8(); + public void zcall9(); +}
\ No newline at end of file diff --git a/test/113-multidex/src/Inf3.java b/test/113-multidex/src/Inf3.java new file mode 100644 index 0000000000..d6c377b0a1 --- /dev/null +++ b/test/113-multidex/src/Inf3.java @@ -0,0 +1,28 @@ +/* + * 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. + */ + +public interface Inf3 { + public void zcall(); + public void zcall1(); + public void zcall2(); + public void zcall3(); + public void zcall4(); + public void zcall5(); + public void zcall6(); + public void zcall7(); + public void zcall8(); + public void zcall9(); +}
\ No newline at end of file diff --git a/test/113-multidex/src/Inf4.java b/test/113-multidex/src/Inf4.java new file mode 100644 index 0000000000..a1801b9045 --- /dev/null +++ b/test/113-multidex/src/Inf4.java @@ -0,0 +1,28 @@ +/* + * 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. + */ + +public interface Inf4 { + public void zcall(); + public void zcall1(); + public void zcall2(); + public void zcall3(); + public void zcall4(); + public void zcall5(); + public void zcall6(); + public void zcall7(); + public void zcall8(); + public void zcall9(); +}
\ No newline at end of file diff --git a/test/113-multidex/src/Inf5.java b/test/113-multidex/src/Inf5.java new file mode 100644 index 0000000000..e8115ce636 --- /dev/null +++ b/test/113-multidex/src/Inf5.java @@ -0,0 +1,28 @@ +/* + * 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. + */ + +public interface Inf5 { + public void zcall(); + public void zcall1(); + public void zcall2(); + public void zcall3(); + public void zcall4(); + public void zcall5(); + public void zcall6(); + public void zcall7(); + public void zcall8(); + public void zcall9(); +}
\ No newline at end of file diff --git a/test/113-multidex/src/Inf6.java b/test/113-multidex/src/Inf6.java new file mode 100644 index 0000000000..554bdb856e --- /dev/null +++ b/test/113-multidex/src/Inf6.java @@ -0,0 +1,28 @@ +/* + * 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. + */ + +public interface Inf6 { + public void zcall(); + public void zcall1(); + public void zcall2(); + public void zcall3(); + public void zcall4(); + public void zcall5(); + public void zcall6(); + public void zcall7(); + public void zcall8(); + public void zcall9(); +}
\ No newline at end of file diff --git a/test/113-multidex/src/Inf7.java b/test/113-multidex/src/Inf7.java new file mode 100644 index 0000000000..19827757a0 --- /dev/null +++ b/test/113-multidex/src/Inf7.java @@ -0,0 +1,28 @@ +/* + * 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. + */ + +public interface Inf7 { + public void zcall(); + public void zcall1(); + public void zcall2(); + public void zcall3(); + public void zcall4(); + public void zcall5(); + public void zcall6(); + public void zcall7(); + public void zcall8(); + public void zcall9(); +}
\ No newline at end of file diff --git a/test/113-multidex/src/Inf8.java b/test/113-multidex/src/Inf8.java new file mode 100644 index 0000000000..87296db514 --- /dev/null +++ b/test/113-multidex/src/Inf8.java @@ -0,0 +1,28 @@ +/* + * 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. + */ + +public interface Inf8 { + public void zcall(); + public void zcall1(); + public void zcall2(); + public void zcall3(); + public void zcall4(); + public void zcall5(); + public void zcall6(); + public void zcall7(); + public void zcall8(); + public void zcall9(); +}
\ No newline at end of file diff --git a/test/113-multidex/src/Main.java b/test/113-multidex/src/Main.java new file mode 100644 index 0000000000..1c74220525 --- /dev/null +++ b/test/113-multidex/src/Main.java @@ -0,0 +1,35 @@ +/* + * 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. + */ + +public class Main { + static public void main(String[] args) throws Exception { + System.out.println(new FillerA().getClass().getName()); + + Inf1 second = new Second(); + System.out.println(second.getClass().getName()); + second.zcall(); + second.zcall1(); + second.zcall2(); + second.zcall3(); + second.zcall4(); + second.zcall5(); + second.zcall6(); + second.zcall7(); + second.zcall8(); + second.zcall9(); + } + +} diff --git a/test/113-multidex/src/Second.java b/test/113-multidex/src/Second.java new file mode 100644 index 0000000000..d0c2535930 --- /dev/null +++ b/test/113-multidex/src/Second.java @@ -0,0 +1,57 @@ +/* + * 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. + */ + +public class Second implements Inf1, Inf2, Inf3, Inf4, Inf5, Inf6, Inf7, Inf8 { + public void zcall() { + System.out.println("Second::zcall"); + } + + public void zcall1() { + System.out.println("Second::zcall1"); + } + + public void zcall2() { + System.out.println("Second::zcall2"); + } + + public void zcall3() { + System.out.println("Second::zcall3"); + } + + public void zcall4() { + System.out.println("Second::zcall4"); + } + + public void zcall5() { + System.out.println("Second::zcall5"); + } + + public void zcall6() { + System.out.println("Second::zcall6"); + } + + public void zcall7() { + System.out.println("Second::zcall7"); + } + + public void zcall8() { + System.out.println("Second::zcall8"); + } + + public void zcall9() { + System.out.println("Second::zcall9"); + } +} diff --git a/test/302-float-conversion/expected.txt b/test/302-float-conversion/expected.txt index 7d5c1eba62..04230761af 100644 --- a/test/302-float-conversion/expected.txt +++ b/test/302-float-conversion/expected.txt @@ -1,2 +1,3 @@ Iteration Result is as expected inter4:2.0 +max_long:9223372036854775807 diff --git a/test/302-float-conversion/src/Main.java b/test/302-float-conversion/src/Main.java index afc5e976d9..27331353f0 100644 --- a/test/302-float-conversion/src/Main.java +++ b/test/302-float-conversion/src/Main.java @@ -21,6 +21,7 @@ public class Main { public static void main(String args[]) { test1(); test2(); + test3(); } public static void test1() { @@ -55,4 +56,9 @@ public class Main { System.out.println("inter4:" + inter4); } + public static void test3() { + double d = Long.MAX_VALUE; + System.out.println("max_long:" + (long)d); + } + } diff --git a/test/303-verification-stress/build b/test/303-verification-stress/build index 2ef9beafd1..c1935d2e9b 100644 --- a/test/303-verification-stress/build +++ b/test/303-verification-stress/build @@ -24,5 +24,5 @@ gcc -o classes-gen classes-gen.c mkdir classes ${JAVAC} -d classes src/*.java -${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes +${DX} --debug --dex --output=classes.dex classes zip $TEST_NAME.jar classes.dex diff --git a/test/700-LoadArgRegs/expected.txt b/test/700-LoadArgRegs/expected.txt index 4908e5bf78..4977df691e 100644 --- a/test/700-LoadArgRegs/expected.txt +++ b/test/700-LoadArgRegs/expected.txt @@ -73,3 +73,4 @@ a, b, c, d, e, f, g -81, -82, -83, -84, -85, -86, -87, -88 -91, -92, -93, -94, -95, -96, -97, -98, -99 -1, -91, -92, -93, -94, -95, -96, -97, -98, -99 +1, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 2, 3, 4, 5, 6 diff --git a/test/700-LoadArgRegs/src/Main.java b/test/700-LoadArgRegs/src/Main.java index 281ab166e4..0e6de737b3 100644 --- a/test/700-LoadArgRegs/src/Main.java +++ b/test/700-LoadArgRegs/src/Main.java @@ -270,8 +270,11 @@ public class Main { // testL2x(100100100100011L, 100100100100011L); } - static public void main(String[] args) throws Exception { + static void testMore(int i1, double d1, double d2, double d3, double d4, double d5, double d6, double d7, double d8, double d9, int i2, int i3, int i4, int i5, int i6) { + System.out.println(i1+", "+d1+", "+d2+", "+d3+", "+d4+", "+d5+", "+d6+", "+d7+", "+d8+", "+d9+", "+i2+", "+i3+", "+i4+", "+i5+", "+i6); + } + static public void main(String[] args) throws Exception { testI(); testB(); testO(); @@ -284,5 +287,6 @@ public class Main { testLL(); + testMore(1, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 2, 3, 4, 5, 6); } } diff --git a/build/Android.libarttest.mk b/test/Android.libarttest.mk index 76e5af0e85..bf3e2aa655 100644 --- a/build/Android.libarttest.mk +++ b/test/Android.libarttest.mk @@ -14,16 +14,20 @@ # limitations under the License. # +LOCAL_PATH := $(call my-dir) + +include art/build/Android.common_build.mk + LIBARTTEST_COMMON_SRC_FILES := \ - test/JniTest/jni_test.cc \ - test/SignalTest/signaltest.cc \ - test/ReferenceMap/stack_walk_refmap_jni.cc \ - test/StackWalk/stack_walk_jni.cc \ - test/UnsafeTest/unsafe_test.cc + JniTest/jni_test.cc \ + SignalTest/signaltest.cc \ + ReferenceMap/stack_walk_refmap_jni.cc \ + StackWalk/stack_walk_jni.cc \ + UnsafeTest/unsafe_test.cc -ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TEST_OUT)/$(TARGET_ARCH)/libarttest.so +ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so ifdef TARGET_2ND_ARCH - ART_TARGET_LIBARTTEST_$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TEST_OUT)/$(TARGET_2ND_ARCH)/libarttest.so + ART_TARGET_LIBARTTEST_$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_2ND_ARCH)/libarttest.so endif # $(1): target or host @@ -45,17 +49,17 @@ define build-libarttest LOCAL_SRC_FILES := $(LIBARTTEST_COMMON_SRC_FILES) LOCAL_SHARED_LIBRARIES += libartd LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime - LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/build/Android.common.mk - LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/build/Android.libarttest.mk + LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk + LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.libarttest.mk include external/libcxx/libcxx.mk ifeq ($$(art_target_or_host),target) - $(call set-target-local-clang-vars) - $(call set-target-local-cflags-vars,debug) + $(call set-target-local-clang-vars) + $(call set-target-local-cflags-vars,debug) LOCAL_SHARED_LIBRARIES += libdl libcutils LOCAL_STATIC_LIBRARIES := libgtest LOCAL_MULTILIB := both - LOCAL_MODULE_PATH_32 := $(ART_TEST_OUT)/$(ART_TARGET_ARCH_32) - LOCAL_MODULE_PATH_64 := $(ART_TEST_OUT)/$(ART_TARGET_ARCH_64) + LOCAL_MODULE_PATH_32 := $(ART_TARGET_TEST_OUT)/$(ART_TARGET_ARCH_32) + LOCAL_MODULE_PATH_64 := $(ART_TARGET_TEST_OUT)/$(ART_TARGET_ARCH_64) LOCAL_MODULE_TARGET_ARCH := $(ART_SUPPORTED_ARCH) include $(BUILD_SHARED_LIBRARY) else # host @@ -67,8 +71,12 @@ define build-libarttest LOCAL_LDLIBS += -lrt endif LOCAL_IS_HOST_MODULE := true + LOCAL_MULTILIB := both include $(BUILD_HOST_SHARED_LIBRARY) endif + + # Clear locally used variables. + art_target_or_host := endef ifeq ($(ART_BUILD_TARGET),true) @@ -77,3 +85,7 @@ endif ifeq ($(ART_BUILD_HOST),true) $(eval $(call build-libarttest,host)) endif + +# Clear locally used variables. +LOCAL_PATH := +LIBARTTEST_COMMON_SRC_FILES := diff --git a/test/Android.mk b/test/Android.mk deleted file mode 100644 index 789744968a..0000000000 --- a/test/Android.mk +++ /dev/null @@ -1,220 +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. -# - -LOCAL_PATH := $(call my-dir) - -include art/build/Android.common.mk - -######################################################################## - -# subdirectories which are used as inputs for gtests -TEST_DEX_DIRECTORIES := \ - AbstractMethod \ - AllFields \ - ExceptionHandle \ - GetMethodSignature \ - Interfaces \ - Main \ - MyClass \ - MyClassNatives \ - Nested \ - NonStaticLeafMethods \ - ProtoCompare \ - ProtoCompare2 \ - StaticLeafMethods \ - Statics \ - StaticsFromCode \ - Transaction \ - XandY - -# subdirectories of which are used with test-art-target-oat -# Declare the simplest tests (Main, HelloWorld) first, the rest are alphabetical -TEST_OAT_DIRECTORIES := \ - Main \ - HelloWorld \ - InterfaceTest \ - JniTest \ - SignalTest \ - NativeAllocations \ - ParallelGC \ - ReferenceMap \ - StackWalk \ - ThreadStress \ - UnsafeTest - -# TODO: Enable when the StackWalk2 tests are passing -# StackWalk2 \ - -ART_TEST_TARGET_DEX_FILES := -ART_TEST_TARGET_DEX_FILES$(ART_PHONY_TEST_TARGET_SUFFIX) := -ART_TEST_TARGET_DEX_FILES$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) := -ART_TEST_HOST_DEX_FILES := - -# $(1): module prefix -# $(2): input test directory -# $(3): target output module path (default module path is used on host) -define build-art-test-dex - ifeq ($(ART_BUILD_TARGET),true) - include $(CLEAR_VARS) - LOCAL_MODULE := $(1)-$(2) - LOCAL_MODULE_TAGS := tests - LOCAL_SRC_FILES := $(call all-java-files-under, $(2)) - LOCAL_JAVA_LIBRARIES := $(TARGET_CORE_JARS) - LOCAL_NO_STANDARD_LIBRARIES := true - LOCAL_MODULE_PATH := $(3) - LOCAL_DEX_PREOPT_IMAGE_LOCATION := $(TARGET_CORE_IMG_OUT) - LOCAL_DEX_PREOPT := false - LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk - LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk - include $(BUILD_JAVA_LIBRARY) - - ART_TEST_TARGET_DEX_FILES += $$(LOCAL_INSTALLED_MODULE) - ART_TEST_TARGET_DEX_FILES$(ART_PHONY_TEST_TARGET_SUFFIX) += $$(LOCAL_INSTALLED_MODULE) - endif - - ifeq ($(ART_BUILD_HOST),true) - include $(CLEAR_VARS) - LOCAL_MODULE := $(1)-$(2) - LOCAL_SRC_FILES := $(call all-java-files-under, $(2)) - LOCAL_JAVA_LIBRARIES := $(HOST_CORE_JARS) - LOCAL_NO_STANDARD_LIBRARIES := true - LOCAL_DEX_PREOPT_IMAGE := $(HOST_CORE_IMG_LOCATION) - LOCAL_DEX_PREOPT := false - LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk - LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk - include $(BUILD_HOST_DALVIK_JAVA_LIBRARY) - ART_TEST_HOST_DEX_FILES += $$(LOCAL_INSTALLED_MODULE) - endif -endef -$(foreach dir,$(TEST_DEX_DIRECTORIES), $(eval $(call build-art-test-dex,art-test-dex,$(dir),$(ART_NATIVETEST_OUT)))) -$(foreach dir,$(TEST_OAT_DIRECTORIES), $(eval $(call build-art-test-dex,oat-test-dex,$(dir),$(ART_TEST_OUT)))) - -# Used outside the art project to get a list of the current tests -ART_TEST_DEX_MAKE_TARGETS := $(addprefix art-test-dex-, $(TEST_DEX_DIRECTORIES)) -ART_TEST_OAT_MAKE_TARGETS := $(addprefix oat-test-dex-, $(TEST_OAT_DIRECTORIES)) - -######################################################################## - -ART_TEST_TARGET_OAT_TARGETS$(ART_PHONY_TEST_TARGET_SUFFIX) := -ART_TEST_TARGET_OAT_TARGETS$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) := -ART_TEST_HOST_OAT_DEFAULT_TARGETS := -ART_TEST_HOST_OAT_INTERPRETER_TARGETS := - -define declare-test-art-oat-targets-impl -.PHONY: test-art-target-oat-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX) -test-art-target-oat-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX): $(ART_TEST_OUT)/oat-test-dex-$(1).jar test-art-target-sync - adb shell touch $(ART_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@ - adb shell rm $(ART_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@ - adb shell sh -c "/system/bin/dalvikvm$($(2)ART_PHONY_TEST_TARGET_SUFFIX) $(DALVIKVM_FLAGS) -XXlib:libartd.so -Ximage:$(ART_TEST_DIR)/core.art -classpath $(ART_TEST_DIR)/oat-test-dex-$(1).jar -Djava.library.path=$(ART_TEST_DIR)/$(TARGET_$(2)ARCH) $(1) && touch $(ART_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@" - $(hide) (adb pull $(ART_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@ /tmp/ && echo $$@ PASSED) || (echo $$@ FAILED && exit 1) - $(hide) rm /tmp/$$@ -endef - -# $(1): directory -# $(2): arguments -define declare-test-art-oat-targets - ifdef TARGET_2ND_ARCH - $(call declare-test-art-oat-targets-impl,$(1),2ND_) - - # Bind the primary to the non-suffix rule - ifneq ($(ART_PHONY_TEST_TARGET_SUFFIX),) -test-art-target-oat-$(1): test-art-target-oat-$(1)$(ART_PHONY_TEST_TARGET_SUFFIX) - endif - endif - $(call declare-test-art-oat-targets-impl,$(1),) - -$(HOST_OUT_JAVA_LIBRARIES)/$(ART_HOST_ARCH)/oat-test-dex-$(1).odex: $(HOST_OUT_JAVA_LIBRARIES)/oat-test-dex-$(1).jar $(HOST_CORE_IMG_OUT) | $(DEX2OATD) - $(DEX2OATD) $(DEX2OAT_FLAGS) --runtime-arg -Xms16m --runtime-arg -Xmx16m --boot-image=$(HOST_CORE_IMG_LOCATION) --dex-file=$$(realpath $$<) --oat-file=$$@ --instruction-set=$(ART_HOST_ARCH) --host --android-root=$(HOST_OUT) - -.PHONY: test-art-host-oat-default-$(1) -test-art-host-oat-default-$(1): $(HOST_OUT_JAVA_LIBRARIES)/$(ART_HOST_ARCH)/oat-test-dex-$(1).odex test-art-host-dependencies - mkdir -p /tmp/android-data/test-art-host-oat-default-$(1) - ANDROID_DATA=/tmp/android-data/test-art-host-oat-default-$(1) \ - ANDROID_ROOT=$(HOST_OUT) \ - LD_LIBRARY_PATH=$(HOST_LIBRARY_PATH) \ - $(HOST_OUT_EXECUTABLES)/dalvikvm $(DALVIKVM_FLAGS) -XXlib:libartd$(HOST_SHLIB_SUFFIX) -Ximage:$(HOST_CORE_IMG_LOCATION) -classpath $(HOST_OUT_JAVA_LIBRARIES)/oat-test-dex-$(1).jar -Djava.library.path=$(HOST_LIBRARY_PATH) $(1) $(2) \ - && echo test-art-host-oat-default-$(1) PASSED || (echo test-art-host-oat-default-$(1) FAILED && exit 1) - $(hide) rm -r /tmp/android-data/test-art-host-oat-default-$(1) - -.PHONY: test-art-host-oat-interpreter-$(1) -test-art-host-oat-interpreter-$(1): $(HOST_OUT_JAVA_LIBRARIES)/$(ART_HOST_ARCH)/oat-test-dex-$(1).odex test-art-host-dependencies - mkdir -p /tmp/android-data/test-art-host-oat-interpreter-$(1) - ANDROID_DATA=/tmp/android-data/test-art-host-oat-interpreter-$(1) \ - ANDROID_ROOT=$(HOST_OUT) \ - LD_LIBRARY_PATH=$(HOST_LIBRARY_PATH) \ - $(HOST_OUT_EXECUTABLES)/dalvikvm -XXlib:libartd$(HOST_SHLIB_SUFFIX) -Ximage:$(HOST_CORE_IMG_LOCATION) $(DALVIKVM_FLAGS) -Xint -classpath $(HOST_OUT_JAVA_LIBRARIES)/oat-test-dex-$(1).jar -Djava.library.path=$(HOST_LIBRARY_PATH) $(1) $(2) \ - && echo test-art-host-oat-interpreter-$(1) PASSED || (echo test-art-host-oat-interpreter-$(1) FAILED && exit 1) - $(hide) rm -r /tmp/android-data/test-art-host-oat-interpreter-$(1) - -.PHONY: test-art-host-oat-$(1) -test-art-host-oat-$(1): test-art-host-oat-default-$(1) test-art-host-oat-interpreter-$(1) - -.PHONY: test-art-oat-$(1) -test-art-oat-$(1): test-art-host-oat-$(1) test-art-target-oat-$(1) - -ART_TEST_TARGET_OAT_TARGETS$(ART_PHONY_TEST_TARGET_SUFFIX) += test-art-target-oat-$(1)$(ART_PHONY_TEST_TARGET_SUFFIX) -ifdef TARGET_2ND_ARCH - ART_TEST_TARGET_OAT_TARGETS$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) += test-art-target-oat-$(1)$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) -endif -ART_TEST_HOST_OAT_DEFAULT_TARGETS += test-art-host-oat-default-$(1) -ART_TEST_HOST_OAT_INTERPRETER_TARGETS += test-art-host-oat-interpreter-$(1) -endef -$(foreach dir,$(TEST_OAT_DIRECTORIES), $(eval $(call declare-test-art-oat-targets,$(dir)))) - -######################################################################## - -TEST_ART_RUN_TEST_MAKE_TARGETS := -art_run_tests_dir := $(call intermediates-dir-for,PACKAGING,art-run-tests)/DATA - -# Helper to create individual build targets for tests. -# Must be called with $(eval) -# $(1): the test number -define declare-make-art-run-test -dmart_target := $(art_run_tests_dir)/art-run-tests/$(1)/touch -$$(dmart_target): $(DX) $(HOST_OUT_EXECUTABLES)/jasmin - $(hide) rm -rf $$(dir $$@) && mkdir -p $$(dir $$@) - $(hide) DX=$(abspath $(DX)) JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) $(LOCAL_PATH)/run-test --build-only --output-path $$(abspath $$(dir $$@)) $(1) - $(hide) touch $$@ - - -TEST_ART_RUN_TEST_MAKE_TARGETS += $$(dmart_target) -dmart_target := -endef - -# Expand all tests. -TEST_ART_RUN_TESTS := $(wildcard $(LOCAL_PATH)/[0-9]*) -TEST_ART_RUN_TESTS := $(subst $(LOCAL_PATH)/,, $(TEST_ART_RUN_TESTS)) -TEST_ART_TIMING_SENSITIVE_RUN_TESTS := 053-wait-some 055-enum-performance -ifdef dist_goal # disable timing sensitive tests on "dist" builds. - $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), \ - $(info Skipping $(test)) \ - $(eval TEST_ART_RUN_TESTS := $(filter-out $(test), $(TEST_ART_RUN_TESTS)))) -endif -$(foreach test, $(TEST_ART_RUN_TESTS), $(eval $(call declare-make-art-run-test,$(test)))) - -include $(CLEAR_VARS) -LOCAL_MODULE_TAGS := tests -LOCAL_MODULE := art-run-tests -LOCAL_ADDITIONAL_DEPENDENCIES := $(TEST_ART_RUN_TEST_MAKE_TARGETS) -# The build system use this flag to pick up files generated by declare-make-art-run-test. -LOCAL_PICKUP_FILES := $(art_run_tests_dir) - -include $(BUILD_PHONY_PACKAGE) - -# clear temp vars -TEST_ART_RUN_TEST_MAKE_TARGETS := -declare-make-art-run-test := - -######################################################################## diff --git a/test/Android.oat.mk b/test/Android.oat.mk new file mode 100644 index 0000000000..b11efb4ed6 --- /dev/null +++ b/test/Android.oat.mk @@ -0,0 +1,453 @@ +# 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. +# + +LOCAL_PATH := $(call my-dir) +LOCAL_PID := $(shell echo $$PPID) + +include art/build/Android.common_test.mk + +######################################################################## + +# Subdirectories in art/test which contain dex files used as inputs for oat tests. Declare the +# simplest tests (Main, HelloWorld) first, the rest are alphabetical. +TEST_OAT_DIRECTORIES := \ + Main \ + HelloWorld \ + InterfaceTest \ + JniTest \ + SignalTest \ + NativeAllocations \ + ParallelGC \ + ReferenceMap \ + StackWalk \ + ThreadStress \ + UnsafeTest + +# TODO: Enable when the StackWalk2 tests are passing +# StackWalk2 \ + +# Create build rules for each dex file recording the dependency. +$(foreach dir,$(TEST_OAT_DIRECTORIES), $(eval $(call build-art-test-dex,art-oat-test,$(dir), \ + $(ART_TARGET_TEST_OUT),$(LOCAL_PATH)/Android.oat.mk,ART_OAT_TEST_$(dir)_DEX))) + +######################################################################## + +include $(LOCAL_PATH)/Android.libarttest.mk + +ART_TEST_TARGET_OAT_DEFAULT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_OAT_DEFAULT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_OAT_DEFAULT_RULES := +ART_TEST_TARGET_OAT_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_OAT_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_OAT_OPTIMIZING_RULES := +ART_TEST_TARGET_OAT_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_OAT_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_OAT_INTERPRETER_RULES := +ART_TEST_TARGET_OAT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_OAT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_OAT_RULES := + +# We need dex2oat and dalvikvm on the target as well as the core image. +TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUT) $(2ND_TARGET_CORE_IMG_OUT) $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so +ifdef TARGET_2ND_ARCH +TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_2ND_ARCH)/libarttest.so +endif + +# Define rule to run an individual oat test on the host. Output from the test is written to the +# host in /tmp/android-data in a directory named after test's rule name (its target) and the parent +# process' PID (ie the PID of make). On failure the output is dumped to the console. To test for +# success on the target device a file is created following a successful test and this is pulled +# onto the host. If the pull fails then the file wasn't created because the test failed. +# $(1): directory - the name of the test we're building such as HelloWorld. +# $(2): 2ND_ or undefined - used to differentiate between the primary and secondary architecture. +# $(3): the target (rule name), e.g. test-art-target-oat-default-HelloWorld64 +# $(4): -Xint or undefined - do we want to run with the interpreter or default. +define define-test-art-oat-rule-target + # Add the test dependencies to test-art-target-sync, which will be a prerequisite for the test + # to ensure files are pushed to the device. + TEST_ART_TARGET_SYNC_DEPS += $$(ART_OAT_TEST_$(1)_DEX) + +.PHONY: $(3) +$(3): test-art-target-sync + $(hide) mkdir -p $(ART_HOST_TEST_DIR)/android-data-$$@ + $(hide) echo Running: $$@ + $(hide) adb shell touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$(LOCAL_PID) + $(hide) adb shell rm $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$(LOCAL_PID) + $(hide) $$(call ART_TEST_SKIP,$$@) && \ + adb shell sh -c "/system/bin/dalvikvm$($(2)ART_PHONY_TEST_TARGET_SUFFIX) \ + $(DALVIKVM_FLAGS) $(4) -XXlib:libartd.so -Ximage:$(ART_TARGET_TEST_DIR)/core.art \ + -classpath $(ART_TARGET_TEST_DIR)/art-oat-test-$(1).jar \ + -Djava.library.path=$(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH) $(1) \ + && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$(LOCAL_PID)" \ + > $(ART_HOST_TEST_DIR)/android-data-$$@/output.txt 2>&1 && \ + (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$(LOCAL_PID) $(ART_HOST_TEST_DIR)/android-data-$$@ \ + && $$(call ART_TEST_PASSED,$$@)) \ + || (([ ! -f $(ART_HOST_TEST_DIR)/android-data-$$@/output.txt ] || \ + cat $(ART_HOST_TEST_DIR)/android-data-$$@/output.txt) && $$(call ART_TEST_FAILED,$$@)) + $$(hide) (echo $(MAKECMDGOALS) | grep -q $$@ && \ + echo "run-test run as top-level target, removing test directory $(ART_HOST_TEST_DIR)" && \ + rm -r $(ART_HOST_TEST_DIR)) || true + +endef # define-test-art-oat-rule-target + +# Define rules to run oat tests on the target. +# $(1): directory - the name of the test we're building such as HelloWorld. +# $(2): 2ND_ or undefined - used to differentiate between the primary and secondary architecture. +define define-test-art-oat-rules-target + # Define a phony rule to run a target oat test using the default compiler. + default_test_rule := test-art-target-oat-default-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX) + $(call define-test-art-oat-rule-target,$(1),$(2),$$(default_test_rule),) + + ART_TEST_TARGET_OAT_DEFAULT$$($(2)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += $$(default_test_rule) + ART_TEST_TARGET_OAT_DEFAULT_RULES += $$(default_test_rule) + ART_TEST_TARGET_OAT_DEFAULT_$(1)_RULES += $$(default_test_rule) + + optimizing_test_rule := test-art-target-oat-optimizing-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX) + $(call define-test-art-oat-rule-target,$(1),$(2),$$(optimizing_test_rule), \ + -Xcompiler-option --compiler-backend=Optimizing) + + ART_TEST_TARGET_OAT_OPTIMIZING$$($(2)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += $$(optimizing_test_rule) + ART_TEST_TARGET_OAT_OPTIMIZING_RULES += $$(optimizing_test_rule) + ART_TEST_TARGET_OAT_OPTIMIZING_$(1)_RULES += $$(optimizing_test_rule) + + # Define a phony rule to run a target oat test using the interpeter. + interpreter_test_rule := test-art-target-oat-interpreter-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX) + $(call define-test-art-oat-rule-target,$(1),$(2),$$(interpreter_test_rule),-Xint) + + ART_TEST_TARGET_OAT_INTERPRETER$$($(2)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += $$(interpreter_test_rule) + ART_TEST_TARGET_OAT_INTERPRETER_RULES += $$(interpreter_test_rule) + ART_TEST_TARGET_OAT_INTERPRETER_$(1)_RULES += $$(interpreter_test_rule) + + # Define a phony rule to run both the default and interpreter variants. + all_test_rule := test-art-target-oat-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX) +.PHONY: $$(all_test_rule) +$$(all_test_rule): $$(default_test_rule) $$(optimizing_test_rule) $$(interpreter_test_rule) + $(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) + + ART_TEST_TARGET_OAT$$($(2)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += $$(all_test_rule) + ART_TEST_TARGET_OAT_RULES += $$(all_test_rule) + ART_TEST_TARGET_OAT_$(1)_RULES += $$(all_test_rule) + + # Clear locally defined variables. + interpreter_test_rule := + default_test_rule := + optimizing_test_rule := + all_test_rule := +endef # define-test-art-oat-rules-target + +ART_TEST_HOST_OAT_DEFAULT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_OAT_DEFAULT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_OAT_DEFAULT_RULES := +ART_TEST_HOST_OAT_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_OAT_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_OAT_OPTIMIZING_RULES := +ART_TEST_HOST_OAT_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_OAT_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_OAT_INTERPRETER_RULES := +ART_TEST_HOST_OAT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_OAT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_OAT_RULES := + +# All tests require the host executables, libarttest and the core images. +ART_TEST_HOST_OAT_DEPENDENCIES := \ + $(ART_HOST_EXECUTABLES) \ + $(ART_HOST_LIBRARY_PATH)/libarttest$(ART_HOST_SHLIB_EXTENSION) \ + $(ART_HOST_LIBRARY_PATH)/libjavacore$(ART_HOST_SHLIB_EXTENSION) \ + $(HOST_CORE_IMG_OUT) + +ifneq ($(HOST_PREFER_32_BIT),true) +ART_TEST_HOST_OAT_DEPENDENCIES += \ + $(2ND_ART_HOST_LIBRARY_PATH)/libarttest$(ART_HOST_SHLIB_EXTENSION) \ + $(2ND_ART_HOST_LIBRARY_PATH)/libjavacore$(ART_HOST_SHLIB_EXTENSION) \ + $(2ND_HOST_CORE_IMG_OUT) +endif + +# Define rule to run an individual oat test on the host. Output from the test is written to the +# host in /tmp/android-data in a directory named after test's rule name (its target) and the parent +# process' PID (ie the PID of make). On failure the output is dumped to the console. +# $(1): directory - the name of the test we're building such as HelloWorld. +# $(2): 2ND_ or undefined - used to differentiate between the primary and secondary architecture. +# $(3): the target (rule name), e.g. test-art-host-oat-default-HelloWorld64 +# $(4): argument to dex2oat +# $(5): argument to runtime, e.g. -Xint or undefined +define define-test-art-oat-rule-host + # Remove the leading / from /tmp for the test directory. + dex_file := $$(subst /tmp,tmp,$(ART_HOST_TEST_DIR))/android-data-$(3)/oat-test-dex-$(1).jar + oat_file := $(ART_HOST_TEST_DIR)/android-data-$(3)/dalvik-cache/$$($(2)HOST_ARCH)/$$(subst /,@,$$(dex_file))@classes.dex +$(3): PRIVATE_DEX_FILE := /$$(dex_file) +$(3): PRIVATE_OAT_FILE := $$(oat_file) +.PHONY: $(3) +$(3): $$(ART_OAT_TEST_$(1)_DEX) $(ART_TEST_HOST_OAT_DEPENDENCIES) + $(hide) mkdir -p $(ART_HOST_TEST_DIR)/android-data-$$@/dalvik-cache/$$($(2)HOST_ARCH) + $(hide) cp $$(realpath $$<) $(ART_HOST_TEST_DIR)/android-data-$$@/oat-test-dex-$(1).jar + $(hide) $(DEX2OATD) $(DEX2OAT_FLAGS) --runtime-arg -Xms16m --runtime-arg -Xmx16m $(4) \ + --boot-image=$$(HOST_CORE_IMG_LOCATION) \ + --dex-file=$$(PRIVATE_DEX_FILE) --oat-file=$$(PRIVATE_OAT_FILE) \ + --instruction-set=$($(2)ART_HOST_ARCH) --host --android-root=$(HOST_OUT) \ + || $$(call ART_TEST_FAILED,$$@) + $(hide) $$(call ART_TEST_SKIP,$$@) && \ + ANDROID_DATA=$(ART_HOST_TEST_DIR)/android-data-$$@/ \ + ANDROID_ROOT=$(HOST_OUT) \ + ANDROID_LOG_TAGS='*:d' \ + LD_LIBRARY_PATH=$$($(2)ART_HOST_OUT_SHARED_LIBRARIES) \ + $(HOST_OUT_EXECUTABLES)/dalvikvm$$($(2)ART_PHONY_TEST_HOST_SUFFIX) $(DALVIKVM_FLAGS) $(5) \ + -XXlib:libartd$(HOST_SHLIB_SUFFIX) -Ximage:$$(HOST_CORE_IMG_LOCATION) \ + -classpath $(ART_HOST_TEST_DIR)/android-data-$$@/oat-test-dex-$(1).jar \ + -Djava.library.path=$$($(2)ART_HOST_OUT_SHARED_LIBRARIES) $(1) \ + > $(ART_HOST_TEST_DIR)/android-data-$$@/output.txt 2>&1 \ + && $$(call ART_TEST_PASSED,$$@) \ + || (([ ! -f $(ART_HOST_TEST_DIR)/android-data-$$@/output.txt ] || \ + cat $(ART_HOST_TEST_DIR)/android-data-$$@/output.txt) && $$(call ART_TEST_FAILED,$$@)) + $$(hide) (echo $(MAKECMDGOALS) | grep -q $$@ && \ + echo "run-test run as top-level target, removing test directory $(ART_HOST_TEST_DIR)" && \ + rm -r $(ART_HOST_TEST_DIR)) || true +endef # define-test-art-oat-rule-host + +# Define rules to run oat tests on the host. +# $(1): directory - the name of the test we're building such as HelloWorld. +# $(2): 2ND_ or undefined - used to differentiate between the primary and secondary architecture. +define define-test-art-oat-rules-host + # Create a rule to run the host oat test with the default compiler. + default_test_rule := test-art-host-oat-default-$(1)$$($(2)ART_PHONY_TEST_HOST_SUFFIX) + $(call define-test-art-oat-rule-host,$(1),$(2),$$(default_test_rule),,) + + ART_TEST_HOST_OAT_DEFAULT$$($(2)ART_PHONY_TEST_HOST_SUFFIX)_RULES += $$(default_test_rule) + ART_TEST_HOST_OAT_DEFAULT_RULES += $$(default_test_rule) + ART_TEST_HOST_OAT_DEFAULT_$(1)_RULES += $$(default_test_rule) + + # Create a rule to run the host oat test with the optimizing compiler. + optimizing_test_rule := test-art-host-oat-optimizing-$(1)$$($(2)ART_PHONY_TEST_HOST_SUFFIX) + $(call define-test-art-oat-rule-host,$(1),$(2),$$(optimizing_test_rule),--compiler-backend=Optimizing,) + + ART_TEST_HOST_OAT_OPTIMIZING$$($(2)ART_PHONY_TEST_HOST_SUFFIX)_RULES += $$(optimizing_test_rule) + ART_TEST_HOST_OAT_OPTIMIZING_RULES += $$(optimizing_test_rule) + ART_TEST_HOST_OAT_OPTIMIZING_$(1)_RULES += $$(optimizing_test_rule) + + # Create a rule to run the host oat test with the interpreter. + interpreter_test_rule := test-art-host-oat-interpreter-$(1)$$($(2)ART_PHONY_TEST_HOST_SUFFIX) + $(call define-test-art-oat-rule-host,$(1),$(2),$$(interpreter_test_rule),--compiler-filter=interpret-only,-Xint) + + ART_TEST_HOST_OAT_INTERPRETER$$($(2)ART_PHONY_TEST_HOST_SUFFIX)_RULES += $$(interpreter_test_rule) + ART_TEST_HOST_OAT_INTERPRETER_RULES += $$(interpreter_test_rule) + ART_TEST_HOST_OAT_INTERPRETER_$(1)_RULES += $$(interpreter_test_rule) + + # Define a phony rule to run both the default and interpreter variants. + all_test_rule := test-art-host-oat-$(1)$$($(2)ART_PHONY_TEST_HOST_SUFFIX) +.PHONY: $$(all_test_rule) +$$(all_test_rule): $$(default_test_rule) $$(interpreter_test_rule) $$(optimizing_test_rule) + $(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) + + ART_TEST_HOST_OAT$$($(2)ART_PHONY_TEST_HOST_SUFFIX)_RULES += $$(all_test_rule) + ART_TEST_HOST_OAT_RULES += $$(all_test_rule) + ART_TEST_HOST_OAT_$(1)_RULES += $$(all_test_rule) + + # Clear locally defined variables. + default_test_rule := + optimizing_test_rule := + interpreter_test_rule := + all_test_rule := +endef # define-test-art-oat-rules-host + +# For a given test create all the combinations of host/target, compiler and suffix such as: +# test-art-host-oat-HelloWord or test-art-target-oat-interpreter-HelloWorld64 +# $(1): test name, e.g. HelloWorld +# $(2): host or target +# $(3): HOST or TARGET +# $(4): undefined, -default, -optimizing or -interpreter +# $(5): undefined, _DEFAULT, _OPTIMIZING or _INTERPRETER +define define-test-art-oat-combination-for-test + ifeq ($(2),host) + ifneq ($(3),HOST) + $$(error argument mismatch $(2) and ($3)) + endif + else + ifneq ($(2),target) + $$(error found $(2) expected host or target) + endif + ifneq ($(3),TARGET) + $$(error argument mismatch $(2) and ($3)) + endif + endif + + rule_name := test-art-$(2)-oat$(4)-$(1) + dependencies := $$(ART_TEST_$(3)_OAT$(5)_$(1)_RULES) + + ifeq ($$(dependencies),) + ifneq ($(4),-optimizing) + $$(error $$(rule_name) has no dependencies) + endif + endif + +.PHONY: $$(rule_name) +$$(rule_name): $$(dependencies) + $(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) + + # Clear locally defined variables. + rule_name := + dependencies := +endef # define-test-art-oat-combination + +# Define target and host oat test rules for the differing multilib flavors and default vs +# interpreter runs. The format of the generated rules (for running an individual test) is: +# test-art-(host|target)-oat-(default|interpreter)-${directory}(32|64) +# The rules are appended to various lists to enable shorter phony build rules to be built. +# $(1): directory +define define-test-art-oat-rules + # Define target tests. + ART_TEST_TARGET_OAT_DEFAULT_$(1)_RULES := + ART_TEST_TARGET_OAT_OPTIMIZING_$(1)_RULES := + ART_TEST_TARGET_OAT_INTERPRETER_$(1)_RULES := + ART_TEST_TARGET_OAT_$(1)_RULES := + $(call define-test-art-oat-rules-target,$(1),) + ifdef TARGET_2ND_ARCH + $(call define-test-art-oat-rules-target,$(1),2ND_) + endif + $(call define-test-art-oat-combination-for-test,$(1),target,TARGET,,)) + $(call define-test-art-oat-combination-for-test,$(1),target,TARGET,-default,_DEFAULT)) + $(call define-test-art-oat-combination-for-test,$(1),target,TARGET,-optimizing,_OPTIMIZING)) + $(call define-test-art-oat-combination-for-test,$(1),target,TARGET,-interpreter,_INTERPRETER)) + + # Define host tests. + ART_TEST_HOST_OAT_DEFAULT_$(1)_RULES := + ART_TEST_HOST_OAT_OPTIMIZING_$(1)_RULES := + ART_TEST_HOST_OAT_INTERPRETER_$(1)_RULES := + ART_TEST_HOST_OAT_$(1)_RULES := + $(call define-test-art-oat-rules-host,$(1),) + ifneq ($(HOST_PREFER_32_BIT),true) + $(call define-test-art-oat-rules-host,$(1),2ND_) + endif + $(call define-test-art-oat-combination-for-test,$(1),host,HOST,,) + $(call define-test-art-oat-combination-for-test,$(1),host,HOST,-default,_DEFAULT) + $(call define-test-art-oat-combination-for-test,$(1),host,HOST,-optimizing,_OPTIMIZING) + $(call define-test-art-oat-combination-for-test,$(1),host,HOST,-interpreter,_INTERPRETER) + + # Clear locally defined variables. + ART_TEST_TARGET_OAT_DEFAULT_$(1)_RULES := + ART_TEST_TARGET_OAT_OPTIMIZING_$(1)_RULES := + ART_TEST_TARGET_OAT_INTERPRETER_$(1)_RULES := + ART_TEST_TARGET_OAT_$(1)_RULES := + ART_TEST_HOST_OAT_DEFAULT_$(1)_RULES := + ART_TEST_HOST_OAT_OPTIMIZING_$(1)_RULES := + ART_TEST_HOST_OAT_INTERPRETER_$(1)_RULES := + ART_TEST_HOST_OAT_$(1)_RULES := +endef # define-test-art-oat-rules +$(foreach dir,$(TEST_OAT_DIRECTORIES), $(eval $(call define-test-art-oat-rules,$(dir)))) + +# Define all the combinations of host/target, compiler and suffix such as: +# test-art-host-oat or test-art-target-oat-interpreter64 +# $(1): host or target +# $(2): HOST or TARGET +# $(3): undefined, -default, -optimizing or -interpreter +# $(4): undefined, _DEFAULT, _OPTIMIZING or _INTERPRETER +# $(5): undefined, 32 or 64 +define define-test-art-oat-combination + ifeq ($(1),host) + ifneq ($(2),HOST) + $$(error argument mismatch $(1) and ($2)) + endif + else + ifneq ($(1),target) + $$(error found $(1) expected host or target) + endif + ifneq ($(2),TARGET) + $$(error argument mismatch $(1) and ($2)) + endif + endif + + rule_name := test-art-$(1)-oat$(3)$(5) + dependencies := $$(ART_TEST_$(2)_OAT$(4)$(5)_RULES) + + ifeq ($$(dependencies),) + ifneq ($(3),-optimizing) + $$(error $$(rule_name) has no dependencies) + endif + endif + +.PHONY: $$(rule_name) +$$(rule_name): $$(dependencies) + $(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) + + # Clear locally defined variables. + rule_name := + dependencies := + +endef # define-test-art-oat-combination + +$(eval $(call define-test-art-oat-combination,target,TARGET,,,)) +$(eval $(call define-test-art-oat-combination,target,TARGET,-default,_DEFAULT,)) +$(eval $(call define-test-art-oat-combination,target,TARGET,-optimizing,_OPTIMIZING,)) +$(eval $(call define-test-art-oat-combination,target,TARGET,-interpreter,_INTERPRETER,)) +$(eval $(call define-test-art-oat-combination,target,TARGET,,,$(ART_PHONY_TEST_TARGET_SUFFIX))) +$(eval $(call define-test-art-oat-combination,target,TARGET,-default,_DEFAULT,$(ART_PHONY_TEST_TARGET_SUFFIX))) +$(eval $(call define-test-art-oat-combination,target,TARGET,-optimizing,_OPTIMIZING,$(ART_PHONY_TEST_TARGET_SUFFIX))) +$(eval $(call define-test-art-oat-combination,target,TARGET,-interpreter,_INTERPRETER,$(ART_PHONY_TEST_TARGET_SUFFIX))) +ifdef TARGET_2ND_ARCH +$(eval $(call define-test-art-oat-combination,target,TARGET,,,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX))) +$(eval $(call define-test-art-oat-combination,target,TARGET,-default,_DEFAULT,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX))) +$(eval $(call define-test-art-oat-combination,target,TARGET,-optimizing,_OPTIMIZING,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX))) +$(eval $(call define-test-art-oat-combination,target,TARGET,-interpreter,_INTERPRETER,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX))) +endif + +$(eval $(call define-test-art-oat-combination,host,HOST,,,)) +$(eval $(call define-test-art-oat-combination,host,HOST,-default,_DEFAULT,)) +$(eval $(call define-test-art-oat-combination,host,HOST,-optimizing,_OPTIMIZING,)) +$(eval $(call define-test-art-oat-combination,host,HOST,-interpreter,_INTERPRETER,)) +$(eval $(call define-test-art-oat-combination,host,HOST,,,$(ART_PHONY_TEST_HOST_SUFFIX))) +$(eval $(call define-test-art-oat-combination,host,HOST,-default,_DEFAULT,$(ART_PHONY_TEST_HOST_SUFFIX))) +$(eval $(call define-test-art-oat-combination,host,HOST,-optimizing,_OPTIMIZING,$(ART_PHONY_TEST_HOST_SUFFIX))) +$(eval $(call define-test-art-oat-combination,host,HOST,-interpreter,_INTERPRETER,$(ART_PHONY_TEST_HOST_SUFFIX))) +ifneq ($(HOST_PREFER_32_BIT),true) +$(eval $(call define-test-art-oat-combination,host,HOST,,,$(2ND_ART_PHONY_TEST_HOST_SUFFIX))) +$(eval $(call define-test-art-oat-combination,host,HOST,-default,_DEFAULT,$(2ND_ART_PHONY_TEST_HOST_SUFFIX))) +$(eval $(call define-test-art-oat-combination,host,HOST,-optimizing,_OPTIMIZING,$(2ND_ART_PHONY_TEST_HOST_SUFFIX))) +$(eval $(call define-test-art-oat-combination,host,HOST,-interpreter,_INTERPRETER,$(2ND_ART_PHONY_TEST_HOST_SUFFIX))) +endif + +# Clear locally defined variables. +define-test-art-oat-rule-target := +define-test-art-oat-rules-target := +define-test-art-oat-rule-host := +define-test-art-oat-rules-host := +define-test-art-oat-combination-for-test := +define-test-art-oat-combination := +ART_TEST_TARGET_OAT_DEFAULT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_OAT_DEFAULT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_OAT_DEFAULT_RULES := +ART_TEST_TARGET_OAT_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_OAT_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_OAT_OPTIMIZING_RULES := +ART_TEST_TARGET_OAT_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_OAT_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_OAT_INTERPRETER_RULES := +ART_TEST_TARGET_OAT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_OAT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_OAT_RULES := +ART_TEST_HOST_OAT_DEFAULT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_OAT_DEFAULT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_OAT_DEFAULT_RULES := +ART_TEST_HOST_OAT_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_OAT_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_OAT_OPTIMIZING_RULES := +ART_TEST_HOST_OAT_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_OAT_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_OAT_INTERPRETER_RULES := +ART_TEST_HOST_OAT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_OAT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_OAT_RULES := +ART_TEST_HOST_OAT_DEPENDENCIES := +$(foreach dir,$(TEST_OAT_DIRECTORIES), $(eval ART_OAT_TEST_$(dir)_DEX :=)) +TEST_OAT_DIRECTORIES := +LOCAL_PID := +LOCAL_PATH := diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk new file mode 100644 index 0000000000..13e967cca5 --- /dev/null +++ b/test/Android.run-test.mk @@ -0,0 +1,343 @@ +# 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. +# + +LOCAL_PATH := $(call my-dir) + +include art/build/Android.common_test.mk + +# List of all tests of the form 003-omnibus-opcodes. +TEST_ART_RUN_TESTS := $(wildcard $(LOCAL_PATH)/[0-9]*) +TEST_ART_RUN_TESTS := $(subst $(LOCAL_PATH)/,, $(TEST_ART_RUN_TESTS)) + +# Tests that are timing sensitive and flaky on heavily loaded systems. +TEST_ART_TIMING_SENSITIVE_RUN_TESTS := \ + test-art-host-run-test-default-053-wait-some32 \ + test-art-host-run-test-default-053-wait-some64 \ + test-art-host-run-test-interpreter-053-wait-some32 \ + test-art-host-run-test-interpreter-053-wait-some64 \ + test-art-host-run-test-optimizing-053-wait-some32 \ + test-art-host-run-test-optimizing-053-wait-some64 \ + test-art-host-run-test-default-055-enum-performance32 \ + test-art-host-run-test-default-055-enum-performance64 \ + test-art-host-run-test-interpreter-055-enum-performance32 \ + test-art-host-run-test-interpreter-055-enum-performance64 \ + test-art-host-run-test-optimizing-055-enum-performance32 \ + test-art-host-run-test-optimizing-055-enum-performance64 + + # disable timing sensitive tests on "dist" builds. +ifdef dist_goal + ART_TEST_KNOWN_BROKEN += $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS) +endif + +# The path where build only targets will be output, e.g. +# out/target/product/generic_x86_64/obj/PACKAGING/art-run-tests_intermediates/DATA +art_run_tests_dir := $(call intermediates-dir-for,PACKAGING,art-run-tests)/DATA + +# A generated list of prerequisites that call 'run-test --build-only', the actual prerequisite is +# an empty file touched in the intermediate directory. +TEST_ART_RUN_TEST_BUILD_RULES := + +# Helper to create individual build targets for tests. Must be called with $(eval). +# $(1): the test number +define define-build-art-run-test + dmart_target := $(art_run_tests_dir)/art-run-tests/$(1)/touch +$$(dmart_target): $(DX) $(HOST_OUT_EXECUTABLES)/jasmin + $(hide) rm -rf $$(dir $$@) && mkdir -p $$(dir $$@) + $(hide) DX=$(abspath $(DX)) JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) \ + $(LOCAL_PATH)/run-test --build-only --output-path $$(abspath $$(dir $$@)) $(1) + $(hide) touch $$@ + + TEST_ART_RUN_TEST_BUILD_RULES += $$(dmart_target) + dmart_target := +endef +$(foreach test, $(TEST_ART_RUN_TESTS), $(eval $(call define-build-art-run-test,$(test)))) + +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := tests +LOCAL_MODULE := art-run-tests +LOCAL_ADDITIONAL_DEPENDENCIES := $(TEST_ART_RUN_TEST_BUILD_RULES) +# The build system use this flag to pick up files generated by declare-make-art-run-test. +LOCAL_PICKUP_FILES := $(art_run_tests_dir) + +include $(BUILD_PHONY_PACKAGE) + +# Clear temp vars. +TEST_ART_RUN_TEST_BUILD_RULES := +art_run_tests_dir := +define-build-art-run-test := + +######################################################################## + +ART_TEST_TARGET_RUN_TEST_ALL_RULES := +ART_TEST_TARGET_RUN_TEST_DEFAULT_RULES := +ART_TEST_TARGET_RUN_TEST_INTERPRETER_RULES := +ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RULES := +ART_TEST_TARGET_RUN_TEST_ALL$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_RUN_TEST_DEFAULT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_HOST_RUN_TEST_ALL_RULES := +ART_TEST_HOST_RUN_TEST_DEFAULT_RULES := +ART_TEST_HOST_RUN_TEST_INTERPRETER_RULES := +ART_TEST_HOST_RUN_TEST_OPTIMIZING_RULES := +ART_TEST_HOST_RUN_TEST_DEFAULT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := + +# We need dex2oat and dalvikvm on the target as well as the core image. +TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUT) $(2ND_TARGET_CORE_IMG_OUT) + +# All tests require the host executables and the core images. +ART_TEST_HOST_RUN_TEST_DEPENDENCIES := \ + $(ART_HOST_EXECUTABLES) \ + $(ART_HOST_LIBRARY_PATH)/libjavacore$(ART_HOST_SHLIB_EXTENSION) \ + $(HOST_CORE_IMG_OUT) + +ifneq ($(HOST_PREFER_32_BIT),true) +ART_TEST_HOST_RUN_TEST_DEPENDENCIES += \ + $(2ND_ART_HOST_LIBRARY_PATH)/libjavacore$(ART_HOST_SHLIB_EXTENSION) \ + $(2ND_HOST_CORE_IMG_OUT) +endif + +# For a given test create all the combinations of host/target, compiler and suffix such as: +# test-art-host-run-test-optimizing-003-omnibus-opcodes32 +# $(1): test name, e.g. 003-omnibus-opcodes +# $(2): host or target +# $(3): default, optimizing or interpreter +# $(4): 32 or 64 +define define-test-art-run-test + run_test_options := $(addprefix --runtime-option ,$(DALVIKVM_FLAGS)) + uc_host_or_target := + prereq_rule := + ifeq ($(2),host) + uc_host_or_target := HOST + run_test_options += --host + prereq_rule := $(ART_TEST_HOST_RUN_TEST_DEPENDENCIES) + else + ifeq ($(2),target) + uc_host_or_target := TARGET + prereq_rule := test-art-target-sync + else + $$(error found $(2) expected host or target) + endif + endif + uc_compiler := + ifeq ($(3),optimizing) + uc_compiler := OPTIMIZING + run_test_options += -Xcompiler-option --compiler-backend=Optimizing + else + ifeq ($(3),interpreter) + uc_compiler := INTERPRETER + run_test_options += --interpreter + else + ifeq ($(3),default) + uc_compiler := DEFAULT + else + $$(error found $(3) expected optimizing, interpreter or default) + endif + endif + endif + ifeq ($(4),64) + run_test_options += --64 + else + ifneq ($(4),32) + $$(error found $(4) expected 32 or 64) + endif + endif + run_test_rule_name := test-art-$(2)-run-test-$(3)-$(1)$(4) + run_test_options := --output-path $(ART_HOST_TEST_DIR)/run-test-output/$$(run_test_rule_name) \ + $$(run_test_options) +$$(run_test_rule_name): PRIVATE_RUN_TEST_OPTIONS := $$(run_test_options) +.PHONY: $$(run_test_rule_name) +$$(run_test_rule_name): $(DX) $(HOST_OUT_EXECUTABLES)/jasmin $$(prereq_rule) + $(hide) $$(call ART_TEST_SKIP,$$@) && \ + DX=$(abspath $(DX)) JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) \ + art/test/run-test $$(PRIVATE_RUN_TEST_OPTIONS) $(1) \ + && $$(call ART_TEST_PASSED,$$@) || $$(call ART_TEST_FAILED,$$@) + $$(hide) (echo $(MAKECMDGOALS) | grep -q $$@ && \ + echo "run-test run as top-level target, removing test directory $(ART_HOST_TEST_DIR)" && \ + rm -r $(ART_HOST_TEST_DIR)) || true + + ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)$(4)_RULES += $$(run_test_rule_name) + ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)_RULES += $$(run_test_rule_name) + ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)_$(1)_RULES += $$(run_test_rule_name) + ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)_RULES += $$(run_test_rule_name) + ART_TEST_$$(uc_host_or_target)_RUN_TEST_$(1)_RULES += $$(run_test_rule_name) + ART_TEST_$$(uc_host_or_target)_RUN_TEST_ALL_RULES += $$(run_test_rule_name) + ART_TEST_$$(uc_host_or_target)_RUN_TEST_ALL$(4)_RULES += $$(run_test_rule_name) + + # Clear locally defined variables. + run_test_options := + run_test_rule_name := + uc_host_or_target := + prereq_rule := + uc_compiler := +endef # define-test-art-run-test + +# Define a phony rule whose purpose is to test its prerequisites. +# $(1): rule name, e.g. test-art-host-run-test32 +# $(2): list of prerequisites +define define-test-art-run-test-group-rule +.PHONY: $(1) +$(1): $(2) + $(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) + +endef # define-test-art-run-test-group-rule + +# Create rules for a group of run tests. +# $(1): test name, e.g. 003-omnibus-opcodes +# $(2): host or target +define define-test-art-run-test-group + group_uc_host_or_target := + ifeq ($(2),host) + group_uc_host_or_target := HOST + else + ifeq ($(2),target) + group_uc_host_or_target := TARGET + else + $$(error found $(2) expected host or target) + endif + endif + + ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_DEFAULT_$(1)_RULES := + ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_INTERPRETER_$(1)_RULES := + ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_OPTIMIZING_$(1)_RULES := + ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)_RULES := + $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX))) + $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX))) + $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX))) + do_second := false + ifeq ($(2),host) + ifneq ($$(HOST_PREFER_32_BIT),true) + do_second := true + endif + else + ifdef TARGET_2ND_ARCH + do_second := true + endif + endif + ifeq (true,$$(do_second)) + $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX))) + $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX))) + $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX))) + endif + + $$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-default-$(1), \ + $$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_DEFAULT_$(1)_RULES))) + $$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-interpreter-$(1), \ + $$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_INTERPRETER_$(1)_RULES))) + $$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-optimizing-$(1), \ + $$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_OPTIMIZING_$(1)_RULES))) + $$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-$(1), \ + $$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)_RULES))) + + # Clear locally defined variables. + ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_DEFAULT_$(1)_RULES := + ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_INTERPRETER_$(1)_RULES := + ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_OPTIMIZING_$(1)_RULES := + ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)_RULES := + group_uc_host_or_target := + do_second := +endef # define-test-art-run-test-group + +$(foreach test, $(TEST_ART_RUN_TESTS), $(eval $(call define-test-art-run-test-group,$(test),target))) +$(foreach test, $(TEST_ART_RUN_TESTS), $(eval $(call define-test-art-run-test-group,$(test),host))) + +$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test, \ + $(ART_TEST_TARGET_RUN_TEST_ALL_RULES))) +$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default, \ + $(ART_TEST_TARGET_RUN_TEST_DEFAULT_RULES))) +$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter, \ + $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_RULES))) +$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing, \ + $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RULES))) +$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test$(ART_PHONY_TEST_TARGET_SUFFIX), \ + $(ART_TEST_TARGET_RUN_TEST_ALL$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES))) +$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default$(ART_PHONY_TEST_TARGET_SUFFIX), \ + $(ART_TEST_TARGET_RUN_TEST_DEFAULT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES))) +$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter$(ART_PHONY_TEST_TARGET_SUFFIX), \ + $(ART_TEST_TARGET_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES))) +$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing$(ART_PHONY_TEST_TARGET_SUFFIX), \ + $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES))) +ifdef TARGET_2ND_ARCH + $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \ + $(ART_TEST_TARGET_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES))) + $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \ + $(ART_TEST_TARGET_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES))) + $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \ + $(ART_TEST_TARGET_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES))) + $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \ + $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES))) +endif + +$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test, \ + $(ART_TEST_HOST_RUN_TEST_ALL_RULES))) +$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default, \ + $(ART_TEST_HOST_RUN_TEST_DEFAULT_RULES))) +$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter, \ + $(ART_TEST_HOST_RUN_TEST_INTERPRETER_RULES))) +$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing, \ + $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_RULES))) +$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test$(ART_PHONY_TEST_HOST_SUFFIX), \ + $(ART_TEST_HOST_RUN_TEST_ALL$(ART_PHONY_TEST_HOST_SUFFIX)_RULES))) +$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default$(ART_PHONY_TEST_HOST_SUFFIX), \ + $(ART_TEST_HOST_RUN_TEST_DEFAULT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES))) +$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter$(ART_PHONY_TEST_HOST_SUFFIX), \ + $(ART_TEST_HOST_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES))) +$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing$(ART_PHONY_TEST_HOST_SUFFIX), \ + $(ART_TEST_HOST_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES))) +ifneq ($(HOST_PREFER_32_BIT),true) + $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \ + $(ART_TEST_HOST_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES))) + $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \ + $(ART_TEST_HOST_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES))) + $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \ + $(ART_TEST_HOST_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES))) + $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \ + $(ART_TEST_HOST_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES))) +endif + +define-test-art-run-test := +define-test-art-run-test-group-rule := +define-test-art-run-test-group := +ART_TEST_TARGET_RUN_TEST_ALL_RULES := +ART_TEST_TARGET_RUN_TEST_DEFAULT_RULES := +ART_TEST_TARGET_RUN_TEST_INTERPRETER_RULES := +ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RULES := +ART_TEST_TARGET_RUN_TEST_ALL$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_RUN_TEST_DEFAULT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_HOST_RUN_TEST_ALL_RULES := +ART_TEST_HOST_RUN_TEST_DEFAULT_RULES := +ART_TEST_HOST_RUN_TEST_INTERPRETER_RULES := +ART_TEST_HOST_RUN_TEST_OPTIMIZING_RULES := +ART_TEST_HOST_RUN_TEST_DEFAULT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := diff --git a/test/etc/host-run-test-jar b/test/etc/host-run-test-jar index f6729745aa..4265f1cbe8 100755 --- a/test/etc/host-run-test-jar +++ b/test/etc/host-run-test-jar @@ -18,6 +18,7 @@ INVOKE_WITH="" DEV_MODE="n" QUIET="n" FLAGS="" +exe="${ANDROID_HOST_OUT}/bin/dalvikvm32" while true; do if [ "x$1" = "x--quiet" ]; then @@ -63,6 +64,9 @@ while true; do elif [ "x$1" = "x--interpreter" ]; then INTERPRETER="y" shift + elif [ "x$1" = "x--64" ]; then + exe="${ANDROID_HOST_OUT}/bin/dalvikvm64" + shift elif [ "x$1" = "x--no-verify" ]; then VERIFY="n" shift @@ -103,8 +107,6 @@ export ANDROID_ROOT="${ANDROID_HOST_OUT}" export LD_LIBRARY_PATH="${ANDROID_ROOT}/lib" export DYLD_LIBRARY_PATH="${ANDROID_ROOT}/lib" -exe="${ANDROID_ROOT}/bin/dalvikvm" - if [ "$DEBUGGER" = "y" ]; then PORT=8000 msg "Waiting for jdb to connect:" diff --git a/test/run-test b/test/run-test index 34b06cc3a1..d1c5bb2360 100755 --- a/test/run-test +++ b/test/run-test @@ -298,6 +298,17 @@ chmod 755 "$run" export TEST_NAME=`basename ${test_dir}` +# To cause tests to fail fast, limit the file sizes created by dx, dex2oat and ART output to 2MB. +file_size_limit=2048 +if echo "$test_dir" | grep 089; then + file_size_limit=5120 +elif echo "$test_dir" | grep 083; then + file_size_limit=5120 +fi +if ! ulimit "$file_size_limit"; then + echo "ulimit file size setting failed" +fi + good="no" if [ "$dev_mode" = "yes" ]; then "./${build}" 2>&1 @@ -376,7 +387,7 @@ fi echo '#################### info' cat "${td_info}" | sed 's/^/# /g' echo '#################### diffs' - diff --strip-trailing-cr -u "$expected" "$output" + diff --strip-trailing-cr -u "$expected" "$output" | tail -n 500 echo '####################' echo ' ' fi diff --git a/tools/generate-operator-out.py b/tools/generate-operator-out.py index 56b8674942..6baa6e3048 100755 --- a/tools/generate-operator-out.py +++ b/tools/generate-operator-out.py @@ -23,11 +23,12 @@ import string import sys -_ENUM_START_RE = re.compile(r'\benum\b\s+(\S+)\s+:?.*\{') +_ENUM_START_RE = re.compile(r'\benum\b\s+(class\s+)?(\S+)\s+:?.*\{(\s+// private)?') _ENUM_VALUE_RE = re.compile(r'([A-Za-z0-9_]+)(.*)') _ENUM_END_RE = re.compile(r'^\s*\};$') _ENUMS = {} _NAMESPACES = {} +_ENUM_CLASSES = {} def Confused(filename, line_number, line): sys.stderr.write('%s:%d: confused by:\n%s\n' % (filename, line_number, line)) @@ -38,7 +39,9 @@ def Confused(filename, line_number, line): def ProcessFile(filename): lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') in_enum = False + is_enum_class = False line_number = 0 + namespaces = [] enclosing_classes = [] @@ -51,11 +54,18 @@ def ProcessFile(filename): m = _ENUM_START_RE.search(raw_line) if m: # Yes, so add an empty entry to _ENUMS for this enum. - enum_name = m.group(1) + + # Except when it's private + if m.group(3) is not None: + continue + + is_enum_class = m.group(1) is not None + enum_name = m.group(2) if len(enclosing_classes) > 0: enum_name = '::'.join(enclosing_classes) + '::' + enum_name _ENUMS[enum_name] = [] _NAMESPACES[enum_name] = '::'.join(namespaces) + _ENUM_CLASSES[enum_name] = is_enum_class in_enum = True continue @@ -139,7 +149,10 @@ def ProcessFile(filename): Confused(filename, line_number, raw_line) if len(enclosing_classes) > 0: - enum_value = '::'.join(enclosing_classes) + '::' + enum_value + if is_enum_class: + enum_value = enum_name + '::' + enum_value + else: + enum_value = '::'.join(enclosing_classes) + '::' + enum_value _ENUMS[enum_name].append((enum_value, enum_text)) @@ -170,7 +183,8 @@ def main(): print ' switch (rhs) {' for (enum_value, enum_text) in _ENUMS[enum_name]: print ' case %s: os << "%s"; break;' % (enum_value, enum_text) - print ' default: os << "%s[" << static_cast<int>(rhs) << "]"; break;' % enum_name + if not _ENUM_CLASSES[enum_name]: + print ' default: os << "%s[" << static_cast<int>(rhs) << "]"; break;' % enum_name print ' }' print ' return os;' print '}' |