diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0d8663fab1a..4d1e8a8be3d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -327,8 +327,8 @@ jobs: uses: ./.github/workflows/build-macos.yml with: platform: macos-x64 - runs-on: 'macos-13' - xcode-toolset-version: '14.3.1' + runs-on: 'macos-15-intel' + xcode-toolset-version: '16.4' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }} @@ -340,8 +340,8 @@ jobs: uses: ./.github/workflows/build-macos.yml with: platform: macos-aarch64 - runs-on: 'macos-14' - xcode-toolset-version: '15.4' + runs-on: 'macos-15' + xcode-toolset-version: '16.4' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }} @@ -432,9 +432,9 @@ jobs: with: platform: macos-aarch64 bootjdk-platform: macos-aarch64 - runs-on: macos-14 + runs-on: macos-15 dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }} - xcode-toolset-version: '15.4' + xcode-toolset-version: '16.4' debug-suffix: -debug test-windows-x64: diff --git a/make/StaticLibs.gmk b/make/StaticLibs.gmk index 80d6b4538c8..490180eb649 100644 --- a/make/StaticLibs.gmk +++ b/make/StaticLibs.gmk @@ -116,6 +116,9 @@ else ifeq ($(call isTargetOs, aix), true) $(eval STATIC_LIB_EXPORT_FILES += $(lib).exp) \ ) STATIC_LIBS := -Wl,-bexpfull $(STATIC_LIB_FILES) $(addprefix -Wl$(COMMA)-bE:, $(STATIC_LIB_EXPORT_FILES)) + ifeq ($(DEBUG_LEVEL), slowdebug) + STATIC_LIBS += -Wl,-bbigtoc + endif else $(error Unsupported platform) endif diff --git a/make/ToolsJdk.gmk b/make/ToolsJdk.gmk index ae0dd069c80..629cadbf83a 100644 --- a/make/ToolsJdk.gmk +++ b/make/ToolsJdk.gmk @@ -63,7 +63,7 @@ TOOL_GENERATECURRENCYDATA = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_ TOOL_TZDB = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ build.tools.tzdb.TzdbZoneRulesCompiler -TOOL_BLOCKED_CERTS = $(JAVA_SMALL) -Xlog:disable -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ +TOOL_BLOCKED_CERTS = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ --add-exports java.base/sun.security.util=ALL-UNNAMED \ build.tools.blockedcertsconverter.BlockedCertsConverter diff --git a/make/autoconf/basic_tools.m4 b/make/autoconf/basic_tools.m4 index 5367db46679..8e42f9205a4 100644 --- a/make/autoconf/basic_tools.m4 +++ b/make/autoconf/basic_tools.m4 @@ -363,7 +363,7 @@ AC_DEFUN_ONCE([BASIC_SETUP_COMPLEX_TOOLS], # Check if it's a GNU date compatible version AC_MSG_CHECKING([if date is a GNU compatible version]) - check_date=`$DATE --version 2>&1 | $GREP "GNU\|BusyBox"` + check_date=`$DATE --version 2>&1 | $GREP "GNU\|BusyBox\|uutils"` if test "x$check_date" != x; then AC_MSG_RESULT([yes]) IS_GNU_DATE=yes diff --git a/make/autoconf/boot-jdk.m4 b/make/autoconf/boot-jdk.m4 index 1dd768b2ae1..b3dbc292919 100644 --- a/make/autoconf/boot-jdk.m4 +++ b/make/autoconf/boot-jdk.m4 @@ -408,27 +408,6 @@ AC_DEFUN_ONCE([BOOTJDK_SETUP_BOOT_JDK], AC_MSG_CHECKING([if Boot JDK is 32 or 64 bits]) AC_MSG_RESULT([$BOOT_JDK_BITS]) - # Try to enable CDS - AC_MSG_CHECKING([for local Boot JDK Class Data Sharing (CDS)]) - BOOT_JDK_CDS_ARCHIVE=$CONFIGURESUPPORT_OUTPUTDIR/classes.jsa - UTIL_ADD_JVM_ARG_IF_OK([-XX:+UnlockDiagnosticVMOptions -XX:-VerifySharedSpaces -XX:SharedArchiveFile=$BOOT_JDK_CDS_ARCHIVE],boot_jdk_cds_args,[$JAVA]) - - if test "x$boot_jdk_cds_args" != x; then - # Try creating a CDS archive - $JAVA $boot_jdk_cds_args -Xshare:dump > /dev/null 2>&1 - if test $? -eq 0; then - BOOTJDK_USE_LOCAL_CDS=true - AC_MSG_RESULT([yes, created]) - else - # Generation failed, don't use CDS. - BOOTJDK_USE_LOCAL_CDS=false - AC_MSG_RESULT([no, creation failed]) - fi - else - BOOTJDK_USE_LOCAL_CDS=false - AC_MSG_RESULT([no, -XX:SharedArchiveFile not supported]) - fi - BOOTJDK_SETUP_CLASSPATH ]) @@ -444,13 +423,8 @@ AC_DEFUN_ONCE([BOOTJDK_SETUP_BOOT_JDK_ARGUMENTS], # Force en-US environment UTIL_ADD_JVM_ARG_IF_OK([-Duser.language=en -Duser.country=US],boot_jdk_jvmargs,[$JAVA]) - if test "x$BOOTJDK_USE_LOCAL_CDS" = xtrue; then - # Use our own CDS archive - UTIL_ADD_JVM_ARG_IF_OK([$boot_jdk_cds_args -Xshare:auto],boot_jdk_jvmargs,[$JAVA]) - else - # Otherwise optimistically use the system-wide one, if one is present - UTIL_ADD_JVM_ARG_IF_OK([-Xshare:auto],boot_jdk_jvmargs,[$JAVA]) - fi + UTIL_ADD_JVM_ARG_IF_OK([-Xlog:all=off:stdout],boot_jdk_jvmargs,[$JAVA]) + UTIL_ADD_JVM_ARG_IF_OK([-Xlog:all=warning:stderr],boot_jdk_jvmargs,[$JAVA]) # Finally append user provided options to allow them to override. UTIL_ADD_JVM_ARG_IF_OK([$USER_BOOT_JDK_OPTIONS],boot_jdk_jvmargs,[$JAVA]) diff --git a/make/autoconf/bootcycle-spec.gmk.template b/make/autoconf/bootcycle-spec.gmk.template index 8b6035606a5..d17a95e190a 100644 --- a/make/autoconf/bootcycle-spec.gmk.template +++ b/make/autoconf/bootcycle-spec.gmk.template @@ -44,7 +44,3 @@ JAVAC_CMD := $(FIXPATH) $(BOOT_JDK)/bin/javac JAR_CMD := $(FIXPATH) $(BOOT_JDK)/bin/jar # The bootcycle JVM arguments may differ from the original boot jdk. JAVA_FLAGS_BIG := @BOOTCYCLE_JVM_ARGS_BIG@ -# Any CDS settings generated for the bootjdk are invalid in the bootcycle build. -# By filtering out those JVM args, the bootcycle JVM will use its default -# settings for CDS. -JAVA_FLAGS := $(filter-out -XX:SharedArchiveFile% -Xshare%, $(JAVA_FLAGS)) diff --git a/make/autoconf/lib-tests.m4 b/make/autoconf/lib-tests.m4 index 23f3d443a6c..31d48055984 100644 --- a/make/autoconf/lib-tests.m4 +++ b/make/autoconf/lib-tests.m4 @@ -28,7 +28,7 @@ ################################################################################ # Minimum supported versions -JTREG_MINIMUM_VERSION=8 +JTREG_MINIMUM_VERSION=8.1 GTEST_MINIMUM_VERSION=1.14.0 ################################################################################ diff --git a/make/conf/github-actions.conf b/make/conf/github-actions.conf index 438e4b3ce8d..74a830cbcc2 100644 --- a/make/conf/github-actions.conf +++ b/make/conf/github-actions.conf @@ -26,7 +26,7 @@ # Versions and download locations for dependencies used by GitHub Actions (GHA) GTEST_VERSION=1.14.0 -JTREG_VERSION=8+2 +JTREG_VERSION=8.1+1 LINUX_X64_BOOT_JDK_EXT=tar.gz LINUX_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk24/1f9ff9062db4449d8ca828c504ffae90/36/GPL/openjdk-24_linux-x64_bin.tar.gz diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index 7ed72005ced..9706321d7b6 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -1174,9 +1174,9 @@ var getJibProfilesDependencies = function (input, common) { jtreg: { server: "jpg", product: "jtreg", - version: "8", - build_number: "2", - file: "bundles/jtreg-8+2.zip", + version: "8.1", + build_number: "1", + file: "bundles/jtreg-8.1+1.zip", environment_name: "JT_HOME", environment_path: input.get("jtreg", "home_path") + "/bin", configure_args: "--with-jtreg=" + input.get("jtreg", "home_path"), diff --git a/make/data/charsetmapping/IBM930.c2b b/make/data/charsetmapping/IBM930.c2b index 88754763fe3..72107424104 100644 --- a/make/data/charsetmapping/IBM930.c2b +++ b/make/data/charsetmapping/IBM930.c2b @@ -32,11 +32,6 @@ 547d 92ca 53da 9b7e 446e f86f -# -# we should use this one instead of the 4260<-ff0d -#4260 2212 -4260 ff0d -# 426A 00A6 43A1 301C 444A 2014 diff --git a/make/data/charsetmapping/IBM930.map b/make/data/charsetmapping/IBM930.map index 4b9dad9526b..7939e795bdf 100644 --- a/make/data/charsetmapping/IBM930.map +++ b/make/data/charsetmapping/IBM930.map @@ -25,13 +25,6 @@ # 4260 <--> 2212 # 426A <--> 00A6 # -# Warning: -# "our old" implementation seems agree with above "new" mappings -# except the entries 4260 <-> 2212. To keep the "compatbility" -# with the "old" implementation, I changed the entries "temporarily" -# 4260 <-> 2212 -# 4260 <- ff0d -# 00 0000 01 0001 02 0002 @@ -407,8 +400,7 @@ FF 009F 425D FF09 425E FF1B 425F FFE2 -#4260 FF0D -4260 2212 +4260 FF0D 4261 FF0F 426A FFE4 426B FF0C diff --git a/make/devkit/Makefile b/make/devkit/Makefile index d2167bf33fa..30e0dce0839 100644 --- a/make/devkit/Makefile +++ b/make/devkit/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -57,61 +57,61 @@ COMMA := , -os := $(shell uname -o) -cpu := $(shell uname -p) +OS := $(shell uname -o) +CPU := $(shell uname -m) # Figure out what platform this is building on. -me := $(cpu)-$(if $(findstring Linux,$(os)),linux-gnu) +ME := $(CPU)-$(if $(findstring Linux,$(OS)),linux-gnu) -$(info Building on platform $(me)) +$(info Building on platform $(ME)) # # By default just build for the current platform, which is assumed to be Linux # ifeq ($(TARGETS), ) - platforms := $(me) - host_platforms := $(platforms) + PLATFORMS := $(ME) + HOST_PLATFORMS := $(PLATFORMS) else - platforms := $(subst $(COMMA), , $(TARGETS)) - host_platforms := $(me) + PLATFORMS := $(subst $(COMMA), , $(TARGETS)) + HOST_PLATFORMS := $(ME) endif -target_platforms := $(platforms) -$(info host_platforms $(host_platforms)) -$(info target_platforms $(target_platforms)) +TARGET_PLATFORMS := $(PLATFORMS) +$(info HOST_PLATFORMS $(HOST_PLATFORMS)) +$(info TARGET_PLATFORMS $(TARGET_PLATFORMS)) -all compile : $(platforms) +all compile : $(PLATFORMS) ifeq ($(SKIP_ME), ) - $(foreach p,$(filter-out $(me),$(platforms)),$(eval $(p) : $$(me))) + $(foreach p,$(filter-out $(ME),$(PLATFORMS)),$(eval $(p) : $$(ME))) endif OUTPUT_ROOT = $(abspath ../../build/devkit) RESULT = $(OUTPUT_ROOT)/result -submakevars = HOST=$@ BUILD=$(me) RESULT=$(RESULT) OUTPUT_ROOT=$(OUTPUT_ROOT) +SUBMAKEVARS = HOST=$@ BUILD=$(ME) RESULT=$(RESULT) OUTPUT_ROOT=$(OUTPUT_ROOT) -$(host_platforms) : +$(HOST_PLATFORMS) : @echo 'Building compilers for $@' - @echo 'Targets: $(target_platforms)' - for p in $(filter $@, $(target_platforms)) $(filter-out $@, $(target_platforms)); do \ - $(MAKE) -f Tools.gmk download-rpms $(submakevars) \ + @echo 'Targets: $(TARGET_PLATFORMS)' + for p in $(filter $@, $(TARGET_PLATFORMS)) $(filter-out $@, $(TARGET_PLATFORMS)); do \ + $(MAKE) -f Tools.gmk download-rpms $(SUBMAKEVARS) \ TARGET=$$p PREFIX=$(RESULT)/$@-to-$$p && \ - $(MAKE) -f Tools.gmk all $(submakevars) \ + $(MAKE) -f Tools.gmk all $(SUBMAKEVARS) \ TARGET=$$p PREFIX=$(RESULT)/$@-to-$$p && \ - $(MAKE) -f Tools.gmk ccache $(submakevars) \ + $(MAKE) -f Tools.gmk ccache $(SUBMAKEVARS) \ TARGET=$@ PREFIX=$(RESULT)/$@-to-$$p || exit 1 ; \ done @echo 'All done"' -today := $(shell date +%Y%m%d) +TODAY := $(shell date +%Y%m%d) define Mktar - $(1)-to-$(2)_tar = $$(RESULT)/sdk-$(1)-to-$(2)-$$(today).tar.gz + $(1)-to-$(2)_tar = $$(RESULT)/sdk-$(1)-to-$(2)-$$(TODAY).tar.gz $$($(1)-to-$(2)_tar) : PLATFORM = $(1)-to-$(2) TARFILES += $$($(1)-to-$(2)_tar) endef -$(foreach p,$(host_platforms),$(foreach t,$(target_platforms),$(eval $(call Mktar,$(p),$(t))))) +$(foreach p,$(HOST_PLATFORMS),$(foreach t,$(TARGET_PLATFORMS),$(eval $(call Mktar,$(p),$(t))))) tars : all $(TARFILES) onlytars : $(TARFILES) @@ -119,9 +119,9 @@ onlytars : $(TARFILES) $(MAKE) -r -f Tars.gmk SRC_DIR=$(RESULT)/$(PLATFORM) TAR_FILE=$@ clean : - rm -rf $(addprefix ../../build/devkit/, result $(host_platforms)) + rm -rf $(addprefix ../../build/devkit/, result $(HOST_PLATFORMS)) dist-clean: clean rm -rf $(addprefix ../../build/devkit/, src download) FORCE : -.PHONY : all compile tars $(configs) $(host_platforms) clean dist-clean +.PHONY : all compile tars $(HOST_PLATFORMS) clean dist-clean diff --git a/make/devkit/Tools.gmk b/make/devkit/Tools.gmk index f27d47b822c..db77f2f3f74 100644 --- a/make/devkit/Tools.gmk +++ b/make/devkit/Tools.gmk @@ -39,7 +39,7 @@ # Fix this... # -uppercase = $(shell echo $1 | tr a-z A-Z) +lowercase = $(shell echo $1 | tr A-Z a-z) $(info TARGET=$(TARGET)) $(info HOST=$(HOST)) @@ -104,26 +104,48 @@ endif ################################################################################ # Define external dependencies -gcc_ver_only := 14.2.0 -binutils_ver_only := 2.43 -ccache_ver_only := 4.10.2 +GNU_BASE_URL := https://ftp.gnu.org/pub/gnu + +BINUTILS_VER_ONLY := 2.43 +BINUTILS_BASE_URL := $(GNU_BASE_URL)/binutils +BINUTILS_SHA512 := 93e063163e54d6a6ee2bd48dc754270bf757a3635b49a702ed6b310e929e94063958512d191e66beaf44275f7ea60865dbde138b624626739679fcc306b133bb + +CCACHE_VER_ONLY := 4.10.2 +CCACHE_BASE_URL := https://github.com/ccache/ccache/releases/download CCACHE_CMAKE_BASED := 1 -mpfr_ver_only := 4.2.1 -gmp_ver_only := 6.3.0 -mpc_ver_only := 1.3.1 -gdb_ver_only := 15.2 +CCACHE_SHA512 := 3815c71d7266c32839acb306763268018acc58b3bbbd9ec79fc101e4217c1720d2ad2f01645bf69168c1c61d27700b6f3bb755cfa82689cca69824f015653f3c -dependencies := gcc binutils ccache mpfr gmp mpc gdb +GCC_VER_ONLY := 14.2.0 +GCC_BASE_URL := $(GNU_BASE_URL)/gcc +GCC_SHA512 := 932bdef0cda94bacedf452ab17f103c0cb511ff2cec55e9112fc0328cbf1d803b42595728ea7b200e0a057c03e85626f937012e49a7515bc5dd256b2bf4bc396 -$(foreach dep,$(dependencies),$(eval $(dep)_ver := $(dep)-$($(dep)_ver_only))) +GDB_VER_ONLY := 15.2 +GDB_BASE_URL := $(GNU_BASE_URL)/gdb +GDB_SHA512 := 624007deceb5b15ba89c0725883d1a699fa46714ef30887f3d0165e17c5d65d634671740a135aa69e437d916218abb08cfa2a38ed309ff19d48f51da56b2a8ba -GCC := http://ftp.gnu.org/pub/gnu/gcc/$(gcc_ver)/$(gcc_ver).tar.xz -BINUTILS := http://ftp.gnu.org/pub/gnu/binutils/$(binutils_ver).tar.gz -CCACHE := https://github.com/ccache/ccache/releases/download/v$(ccache_ver_only)/$(ccache_ver).tar.xz -MPFR := https://www.mpfr.org/$(mpfr_ver)/$(mpfr_ver).tar.bz2 -GMP := http://ftp.gnu.org/pub/gnu/gmp/$(gmp_ver).tar.bz2 -MPC := http://ftp.gnu.org/pub/gnu/mpc/$(mpc_ver).tar.gz -GDB := http://ftp.gnu.org/gnu/gdb/$(gdb_ver).tar.xz +GMP_VER_ONLY := 6.3.0 +GMP_BASE_URL := $(GNU_BASE_URL)/gmp +GMP_SHA512 := e85a0dab5195889948a3462189f0e0598d331d3457612e2d3350799dba2e244316d256f8161df5219538eb003e4b5343f989aaa00f96321559063ed8c8f29fd2 + +MPC_VER_ONLY := 1.3.1 +MPC_BASE_URL := $(GNU_BASE_URL)/mpc +MPC_SHA512 := 4bab4ef6076f8c5dfdc99d810b51108ced61ea2942ba0c1c932d624360a5473df20d32b300fc76f2ba4aa2a97e1f275c9fd494a1ba9f07c4cb2ad7ceaeb1ae97 + +MPFR_VER_ONLY := 4.2.1 +MPFR_BASE_URL := https://www.mpfr.org +MPFR_SHA512 := bc68c0d755d5446403644833ecbb07e37360beca45f474297b5d5c40926df1efc3e2067eecffdf253f946288bcca39ca89b0613f545d46a9e767d1d4cf358475 + +DEPENDENCIES := BINUTILS CCACHE GCC GDB GMP MPC MPFR + +$(foreach dep,$(DEPENDENCIES),$(eval $(dep)_VER := $(call lowercase,$(dep)-$($(dep)_VER_ONLY)))) + +BINUTILS_URL := $(BINUTILS_BASE_URL)/$(BINUTILS_VER).tar.xz +CCACHE_URL := $(CCACHE_BASE_URL)/v$(CCACHE_VER_ONLY)/$(CCACHE_VER).tar.xz +GCC_URL := $(GCC_BASE_URL)/$(GCC_VER)/$(GCC_VER).tar.xz +GDB_URL := $(GDB_BASE_URL)/$(GDB_VER).tar.xz +GMP_URL := $(GMP_BASE_URL)/$(GMP_VER).tar.xz +MPC_URL := $(MPC_BASE_URL)/$(MPC_VER).tar.gz +MPFR_URL := $(MPFR_BASE_URL)/$(MPFR_VER)/$(MPFR_VER).tar.xz REQUIRED_MIN_MAKE_MAJOR_VERSION := 4 ifneq ($(REQUIRED_MIN_MAKE_MAJOR_VERSION),) @@ -180,10 +202,10 @@ DOWNLOAD_RPMS := $(DOWNLOAD)/rpms/$(TARGET)-$(LINUX_VERSION) SRCDIR := $(OUTPUT_ROOT)/src # Marker file for unpacking rpms -rpms := $(SYSROOT)/rpms_unpacked +RPMS := $(SYSROOT)/rpms_unpacked # Need to patch libs that are linker scripts to use non-absolute paths -libs := $(SYSROOT)/libs_patched +LIBS := $(SYSROOT)/libs_patched ################################################################################ # Download RPMs @@ -198,10 +220,10 @@ download-rpms: ################################################################################ # Unpack source packages -# Generate downloading + unpacking of sources. -define Download +# Generate downloading + checksum verification of sources. +define DownloadVerify # Allow override - $(1)_DIRNAME ?= $(basename $(basename $(notdir $($(1))))) + $(1)_DIRNAME ?= $(basename $(basename $(notdir $($(1)_URL)))) $(1)_DIR = $(abspath $(SRCDIR)/$$($(1)_DIRNAME)) ifeq ($$($(1)_CMAKE_BASED),) $(1)_CFG = $$($(1)_DIR)/configure @@ -212,7 +234,7 @@ define Download $(1)_SRC_MARKER = $$($(1)_DIR)/CMakeLists.txt $(1)_CONFIG = $$(CMAKE_CONFIG) $$($(1)_DIR) endif - $(1)_FILE = $(DOWNLOAD)/$(notdir $($(1))) + $(1)_FILE = $(DOWNLOAD)/$(notdir $($(1)_URL)) $$($(1)_SRC_MARKER) : $$($(1)_FILE) mkdir -p $$(SRCDIR) @@ -224,11 +246,20 @@ define Download touch $$@ $$($(1)_FILE) : - wget -P $(DOWNLOAD) $$($(1)) + mkdir -p $$(@D) + wget -O - $$($(1)_URL) > $$@.tmp + sha512_actual="$$$$(sha512sum $$@.tmp | awk '{ print $$$$1; }')"; \ + if [ x"$$$${sha512_actual}" != x"$$($(1)_SHA512)" ]; then \ + echo "Checksum mismatch for $$@.tmp"; \ + echo " Expected: $$($(1)_SHA512)"; \ + echo " Actual: $$$${sha512_actual}"; \ + exit 1; \ + fi + mv $$@.tmp $$@ endef # Download and unpack all source packages -$(foreach dep,$(dependencies),$(eval $(call Download,$(call uppercase,$(dep))))) +$(foreach dep,$(DEPENDENCIES),$(eval $(call DownloadVerify,$(dep)))) ################################################################################ # Unpack RPMS @@ -250,7 +281,7 @@ RPM_FILE_LIST := $(sort $(foreach a, $(RPM_ARCHS), \ # Note. For building linux you should install rpm2cpio. define unrpm $(SYSROOT)/$(notdir $(1)).unpacked : $(1) - $$(rpms) : $(SYSROOT)/$(notdir $(1)).unpacked + $$(RPMS) : $(SYSROOT)/$(notdir $(1)).unpacked endef %.unpacked : @@ -277,7 +308,7 @@ $(foreach p,$(RPM_FILE_LIST),$(eval $(call unrpm,$(p)))) # have it anyway, but just to make sure... # Patch libc.so and libpthread.so to force linking against libraries in sysroot # and not the ones installed on the build machine. -$(libs) : $(rpms) +$(LIBS) : $(RPMS) @echo Patching libc and pthreads @(for f in `find $(SYSROOT) -name libc.so -o -name libpthread.so`; do \ (cat $$f | sed -e 's|/usr/lib64/||g' \ @@ -293,10 +324,10 @@ $(libs) : $(rpms) # Create links for ffi header files so that they become visible by default when using the # devkit. ifeq ($(ARCH), x86_64) - $(SYSROOT)/usr/include/ffi.h: $(rpms) + $(SYSROOT)/usr/include/ffi.h: $(RPMS) cd $(@D) && rm -f $(@F) && ln -s ../lib/libffi-*/include/$(@F) . - $(SYSROOT)/usr/include/ffitarget.h: $(rpms) + $(SYSROOT)/usr/include/ffitarget.h: $(RPMS) cd $(@D) && rm -f $(@F) && ln -s ../lib/libffi-*/include/$(@F) . SYSROOT_LINKS += $(SYSROOT)/usr/include/ffi.h $(SYSROOT)/usr/include/ffitarget.h @@ -305,7 +336,7 @@ endif ################################################################################ # Define marker files for each source package to be compiled -$(foreach dep,$(dependencies),$(eval $(dep) = $(TARGETDIR)/$($(dep)_ver).done)) +$(foreach dep,$(DEPENDENCIES),$(eval $(dep) = $(TARGETDIR)/$($(dep)_VER).done)) ################################################################################ @@ -345,48 +376,48 @@ TOOLS ?= $(call declare_tools,_FOR_TARGET,$(TARGET)-) # CFLAG_ to most likely -m32. define mk_bfd $$(info Libs for $(1)) - $$(BUILDDIR)/$$(binutils_ver)-$(subst /,-,$(1))/Makefile \ + $$(BUILDDIR)/$$(BINUTILS_VER)-$(subst /,-,$(1))/Makefile \ : CFLAGS += $$(CFLAGS_$(1)) - $$(BUILDDIR)/$$(binutils_ver)-$(subst /,-,$(1))/Makefile \ + $$(BUILDDIR)/$$(BINUTILS_VER)-$(subst /,-,$(1))/Makefile \ : LIBDIRS = --libdir=$(TARGETDIR)/$(1) - bfdlib += $$(TARGETDIR)/$$(binutils_ver)-$(subst /,-,$(1)).done - bfdmakes += $$(BUILDDIR)/$$(binutils_ver)-$(subst /,-,$(1))/Makefile + BFDLIB += $$(TARGETDIR)/$$(BINUTILS_VER)-$(subst /,-,$(1)).done + BFDMAKES += $$(BUILDDIR)/$$(BINUTILS_VER)-$(subst /,-,$(1))/Makefile endef # Create one set of bfds etc for each multilib arch $(foreach l,$(LIBDIRS),$(eval $(call mk_bfd,$(l)))) # Only build these two libs. -$(bfdlib) : MAKECMD = all-libiberty all-bfd -$(bfdlib) : INSTALLCMD = install-libiberty install-bfd +$(BFDLIB) : MAKECMD = all-libiberty all-bfd +$(BFDLIB) : INSTALLCMD = install-libiberty install-bfd # Building targets libbfd + libiberty. HOST==TARGET, i.e not # for a cross env. -$(bfdmakes) : CONFIG = --target=$(TARGET) \ +$(BFDMAKES) : CONFIG = --target=$(TARGET) \ --host=$(TARGET) --build=$(BUILD) \ --prefix=$(TARGETDIR) \ --with-sysroot=$(SYSROOT) \ $(LIBDIRS) -$(bfdmakes) : TOOLS = $(call declare_tools,_FOR_TARGET,$(TARGET)-) $(call declare_tools,,$(TARGET)-) +$(BFDMAKES) : TOOLS = $(call declare_tools,_FOR_TARGET,$(TARGET)-) $(call declare_tools,,$(TARGET)-) ################################################################################ -$(gcc) \ - $(binutils) \ - $(gmp) \ - $(mpfr) \ - $(mpc) \ - $(bfdmakes) \ - $(ccache) : ENVS += $(TOOLS) +$(GCC) \ + $(BINUTILS) \ + $(GMP) \ + $(MPFR) \ + $(MPC) \ + $(BFDMAKES) \ + $(CCACHE) : ENVS += $(TOOLS) # libdir to work around hateful bfd stuff installing into wrong dirs... # ensure we have 64 bit bfd support in the HOST library. I.e our # compiler on i686 will know 64 bit symbols, BUT later # we build just the libs again for TARGET, then with whatever the arch # wants. -$(BUILDDIR)/$(binutils_ver)/Makefile : CONFIG += --enable-64-bit-bfd --libdir=$(PREFIX)/$(word 1,$(LIBDIRS)) +$(BUILDDIR)/$(BINUTILS_VER)/Makefile : CONFIG += --enable-64-bit-bfd --libdir=$(PREFIX)/$(word 1,$(LIBDIRS)) ifeq ($(filter $(ARCH), s390x riscv64 ppc64le), ) # gold compiles but cannot link properly on s390x @ gcc 13.2 and Fedore 41 @@ -397,8 +428,8 @@ endif # Makefile creation. Simply run configure in build dir. # Setting CFLAGS to -O2 generates a much faster ld. -$(bfdmakes) \ -$(BUILDDIR)/$(binutils_ver)/Makefile \ +$(BFDMAKES) \ +$(BUILDDIR)/$(BINUTILS_VER)/Makefile \ : $(BINUTILS_CFG) $(info Configuring $@. Log in $(@D)/log.config) @mkdir -p $(@D) @@ -417,7 +448,7 @@ $(BUILDDIR)/$(binutils_ver)/Makefile \ ) > $(@D)/log.config 2>&1 @echo 'done' -$(BUILDDIR)/$(mpfr_ver)/Makefile \ +$(BUILDDIR)/$(MPFR_VER)/Makefile \ : $(MPFR_CFG) $(info Configuring $@. Log in $(@D)/log.config) @mkdir -p $(@D) @@ -432,7 +463,7 @@ $(BUILDDIR)/$(mpfr_ver)/Makefile \ ) > $(@D)/log.config 2>&1 @echo 'done' -$(BUILDDIR)/$(gmp_ver)/Makefile \ +$(BUILDDIR)/$(GMP_VER)/Makefile \ : $(GMP_CFG) $(info Configuring $@. Log in $(@D)/log.config) @mkdir -p $(@D) @@ -449,7 +480,7 @@ $(BUILDDIR)/$(gmp_ver)/Makefile \ ) > $(@D)/log.config 2>&1 @echo 'done' -$(BUILDDIR)/$(mpc_ver)/Makefile \ +$(BUILDDIR)/$(MPC_VER)/Makefile \ : $(MPC_CFG) $(info Configuring $@. Log in $(@D)/log.config) @mkdir -p $(@D) @@ -468,11 +499,11 @@ $(BUILDDIR)/$(mpc_ver)/Makefile \ # Only valid if glibc target -> linux # proper destructor handling for c++ ifneq (,$(findstring linux,$(TARGET))) - $(BUILDDIR)/$(gcc_ver)/Makefile : CONFIG += --enable-__cxa_atexit + $(BUILDDIR)/$(GCC_VER)/Makefile : CONFIG += --enable-__cxa_atexit endif ifeq ($(ARCH), armhfp) - $(BUILDDIR)/$(gcc_ver)/Makefile : CONFIG += --with-float=hard + $(BUILDDIR)/$(GCC_VER)/Makefile : CONFIG += --with-float=hard endif ifneq ($(filter riscv64 ppc64le s390x, $(ARCH)), ) @@ -487,7 +518,7 @@ endif # skip native language. # and link and assemble with the binutils we created # earlier, so --with-gnu* -$(BUILDDIR)/$(gcc_ver)/Makefile \ +$(BUILDDIR)/$(GCC_VER)/Makefile \ : $(GCC_CFG) $(info Configuring $@. Log in $(@D)/log.config) mkdir -p $(@D) @@ -509,17 +540,17 @@ $(BUILDDIR)/$(gcc_ver)/Makefile \ @echo 'done' # need binutils for gcc -$(gcc) : $(binutils) +$(GCC) : $(BINUTILS) # as of 4.3 or so need these for doing config -$(BUILDDIR)/$(gcc_ver)/Makefile : $(gmp) $(mpfr) $(mpc) -$(mpfr) : $(gmp) -$(mpc) : $(gmp) $(mpfr) +$(BUILDDIR)/$(GCC_VER)/Makefile : $(GMP) $(MPFR) $(MPC) +$(MPFR) : $(GMP) +$(MPC) : $(GMP) $(MPFR) ################################################################################ # Build gdb but only where host and target match ifeq ($(HOST), $(TARGET)) - $(BUILDDIR)/$(gdb_ver)/Makefile: $(GDB_CFG) + $(BUILDDIR)/$(GDB_VER)/Makefile: $(GDB_CFG) $(info Configuring $@. Log in $(@D)/log.config) mkdir -p $(@D) ( \ @@ -532,9 +563,9 @@ ifeq ($(HOST), $(TARGET)) ) > $(@D)/log.config 2>&1 @echo 'done' - $(gdb): $(gcc) + $(GDB): $(GCC) else - $(BUILDDIR)/$(gdb_ver)/Makefile: + $(BUILDDIR)/$(GDB_VER)/Makefile: $(info Faking $@, not used when cross-compiling) mkdir -p $(@D) echo "install:" > $@ @@ -543,7 +574,7 @@ endif ################################################################################ # very straightforward. just build a ccache. it is only for host. -$(BUILDDIR)/$(ccache_ver)/Makefile \ +$(BUILDDIR)/$(CCACHE_VER)/Makefile \ : $(CCACHE_SRC_MARKER) $(info Configuring $@. Log in $(@D)/log.config) @mkdir -p $(@D) @@ -554,12 +585,12 @@ $(BUILDDIR)/$(ccache_ver)/Makefile \ ) > $(@D)/log.config 2>&1 @echo 'done' -gccpatch = $(TARGETDIR)/gcc-patched +GCC_PATCHED = $(TARGETDIR)/gcc-patched ################################################################################ # For some reason cpp is not created as a target-compiler ifeq ($(HOST),$(TARGET)) - $(gccpatch) : $(gcc) link_libs + $(GCC_PATCHED) : $(GCC) link_libs @echo -n 'Creating compiler symlinks...' @for f in cpp; do \ if [ ! -e $(PREFIX)/bin/$(TARGET)-$$f ]; \ @@ -587,7 +618,7 @@ ifeq ($(HOST),$(TARGET)) done;) @echo 'done' else - $(gccpatch) : + $(GCC_PATCHED) : @echo 'done' endif @@ -615,7 +646,7 @@ $(PREFIX)/devkit.info: echo '# This file describes to configure how to interpret the contents of this' >> $@ echo '# devkit' >> $@ echo '' >> $@ - echo 'DEVKIT_NAME="$(gcc_ver) - $(LINUX_VERSION)"' >> $@ + echo 'DEVKIT_NAME="$(GCC_VER) - $(LINUX_VERSION)"' >> $@ echo 'DEVKIT_TOOLCHAIN_PATH="$$DEVKIT_ROOT/bin"' >> $@ echo 'DEVKIT_SYSROOT="$$DEVKIT_ROOT/$(TARGET)/sysroot"' >> $@ echo 'DEVKIT_EXTRA_PATH="$$DEVKIT_ROOT/bin"' >> $@ @@ -651,32 +682,32 @@ ifeq ($(TARGET), $(HOST)) @echo 'Creating missing $* soft link' ln -s $(TARGET)-$* $@ - missing-links := $(addprefix $(PREFIX)/bin/, \ - addr2line ar as c++ c++filt dwp elfedit g++ gcc gcc-$(gcc_ver_only) gprof ld ld.bfd \ + MISSING_LINKS := $(addprefix $(PREFIX)/bin/, \ + addr2line ar as c++ c++filt dwp elfedit g++ gcc gcc-$(GCC_VER_ONLY) gprof ld ld.bfd \ ld.gold nm objcopy objdump ranlib readelf size strings strip) endif # Add link to work around "plugin needed to handle lto object" (JDK-8344272) -$(PREFIX)/lib/bfd-plugins/liblto_plugin.so: $(PREFIX)/libexec/gcc/$(TARGET)/$(gcc_ver_only)/liblto_plugin.so +$(PREFIX)/lib/bfd-plugins/liblto_plugin.so: $(PREFIX)/libexec/gcc/$(TARGET)/$(GCC_VER_ONLY)/liblto_plugin.so @echo 'Creating missing $(@F) soft link' @mkdir -p $(@D) ln -s $$(realpath -s --relative-to=$(@D) $<) $@ -missing-links += $(PREFIX)/lib/bfd-plugins/liblto_plugin.so +MISSING_LINKS += $(PREFIX)/lib/bfd-plugins/liblto_plugin.so ################################################################################ -bfdlib : $(bfdlib) -binutils : $(binutils) -rpms : $(rpms) -libs : $(libs) +bfdlib : $(BFDLIB) +binutils : $(BINUTILS) +rpms : $(RPMS) +libs : $(LIBS) sysroot : rpms libs -gcc : sysroot $(gcc) $(gccpatch) -gdb : $(gdb) -all : binutils gcc bfdlib $(PREFIX)/devkit.info $(missing-links) $(SYSROOT_LINKS) \ +gcc : sysroot $(GCC) $(GCC_PATCHED) +gdb : $(GDB) +all : binutils gcc bfdlib $(PREFIX)/devkit.info $(MISSING_LINKS) $(SYSROOT_LINKS) \ $(THESE_MAKEFILES) gdb # this is only built for host. so separate. -ccache : $(ccache) +ccache : $(CCACHE) .PHONY : gcc all binutils bfdlib link_libs rpms libs sysroot diff --git a/make/devkit/createAutoconfBundle.sh b/make/devkit/createAutoconfBundle.sh index ebe9c427f76..4697e4eb1e3 100644 --- a/make/devkit/createAutoconfBundle.sh +++ b/make/devkit/createAutoconfBundle.sh @@ -93,7 +93,7 @@ elif test "x$TARGET_PLATFORM" = xlinux_x64; then rpm2cpio $OUTPUT_ROOT/m4-$M4_VERSION.el6.x86_64.rpm | cpio -d -i elif test "x$TARGET_PLATFORM" = xlinux_x86; then M4_VERSION=1.4.13-5 - wget http://yum.oracle.com/repo/OracleLinux/OL6/latest/i386/getPackage/m4-$M4_VERSION.el6.i686.rpm + wget https://yum.oracle.com/repo/OracleLinux/OL6/latest/i386/getPackage/m4-$M4_VERSION.el6.i686.rpm cd $IMAGE_DIR rpm2cpio $OUTPUT_ROOT/m4-$M4_VERSION.el6.i686.rpm | cpio -d -i else diff --git a/make/modules/java.scripting/Java.gmk b/make/modules/java.scripting/Java.gmk index 1909ec1a2df..6e5ea0e2c73 100644 --- a/make/modules/java.scripting/Java.gmk +++ b/make/modules/java.scripting/Java.gmk @@ -27,7 +27,5 @@ DOCLINT += -Xdoclint:all/protected \ '-Xdoclint/package:java.*,javax.*' -COPY += .js -CLEAN += .properties ################################################################################ diff --git a/make/modules/java.scripting/Launcher.gmk b/make/modules/java.scripting/Launcher.gmk deleted file mode 100644 index ee6b93fbf2c..00000000000 --- a/make/modules/java.scripting/Launcher.gmk +++ /dev/null @@ -1,39 +0,0 @@ -# -# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -################################################################################ - -include LauncherCommon.gmk - -################################################################################ -## Build jrunscript -################################################################################ - -$(eval $(call SetupBuildLauncher, jrunscript, \ - MAIN_CLASS := com.sun.tools.script.shell.Main, \ - JAVA_ARGS := --add-modules ALL-DEFAULT, \ -)) - -################################################################################ diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index e0459716122..51cdf8c71df 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -2568,10 +2568,6 @@ RegMask Matcher::modL_proj_mask() { return RegMask(); } -const RegMask Matcher::method_handle_invoke_SP_save_mask() { - return FP_REG_mask(); -} - bool size_fits_all_mem_uses(AddPNode* addp, int shift) { for (DUIterator_Fast imax, i = addp->fast_outs(imax); i < imax; i++) { Node* u = addp->fast_out(i); diff --git a/src/hotspot/cpu/aarch64/c1_FrameMap_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_FrameMap_aarch64.cpp index 9d30092b45a..83d0952dcb4 100644 --- a/src/hotspot/cpu/aarch64/c1_FrameMap_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_FrameMap_aarch64.cpp @@ -383,13 +383,6 @@ LIR_Opr FrameMap::stack_pointer() { return FrameMap::sp_opr; } - -// JSR 292 -LIR_Opr FrameMap::method_handle_invoke_SP_save_opr() { - return LIR_OprFact::illegalOpr; // Not needed on aarch64 -} - - bool FrameMap::validate_frame() { return true; } diff --git a/src/hotspot/cpu/aarch64/frame_aarch64.cpp b/src/hotspot/cpu/aarch64/frame_aarch64.cpp index aff50b9cf2f..bdbef53bfdb 100644 --- a/src/hotspot/cpu/aarch64/frame_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/frame_aarch64.cpp @@ -228,8 +228,7 @@ bool frame::safe_for_sender(JavaThread *thread) { nmethod* nm = sender_blob->as_nmethod_or_null(); if (nm != nullptr) { - if (nm->is_deopt_mh_entry(sender_pc) || nm->is_deopt_entry(sender_pc) || - nm->method()->is_method_handle_intrinsic()) { + if (nm->is_deopt_entry(sender_pc) || nm->method()->is_method_handle_intrinsic()) { return false; } } @@ -454,48 +453,6 @@ JavaThread** frame::saved_thread_address(const frame& f) { return thread_addr; } -//------------------------------------------------------------------------------ -// frame::verify_deopt_original_pc -// -// Verifies the calculated original PC of a deoptimization PC for the -// given unextended SP. -#ifdef ASSERT -void frame::verify_deopt_original_pc(nmethod* nm, intptr_t* unextended_sp) { - frame fr; - - // This is ugly but it's better than to change {get,set}_original_pc - // to take an SP value as argument. And it's only a debugging - // method anyway. - fr._unextended_sp = unextended_sp; - - address original_pc = nm->get_original_pc(&fr); - assert(nm->insts_contains_inclusive(original_pc), - "original PC must be in the main code section of the compiled method (or must be immediately following it)"); -} -#endif - -//------------------------------------------------------------------------------ -// frame::adjust_unextended_sp -#ifdef ASSERT -void frame::adjust_unextended_sp() { - // On aarch64, sites calling method handle intrinsics and lambda forms are treated - // as any other call site. Therefore, no special action is needed when we are - // returning to any of these call sites. - - if (_cb != nullptr) { - nmethod* sender_nm = _cb->as_nmethod_or_null(); - if (sender_nm != nullptr) { - // If the sender PC is a deoptimization point, get the original PC. - if (sender_nm->is_deopt_entry(_pc) || - sender_nm->is_deopt_mh_entry(_pc)) { - verify_deopt_original_pc(sender_nm, _unextended_sp); - } - } - } -} -#endif - - //------------------------------------------------------------------------------ // frame::sender_for_interpreter_frame frame frame::sender_for_interpreter_frame(RegisterMap* map) const { diff --git a/src/hotspot/cpu/aarch64/frame_aarch64.hpp b/src/hotspot/cpu/aarch64/frame_aarch64.hpp index da020b4234d..231710df7d7 100644 --- a/src/hotspot/cpu/aarch64/frame_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/frame_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -141,8 +141,6 @@ int _offset_unextended_sp; // for use in stack-chunk frames }; - void adjust_unextended_sp() NOT_DEBUG_RETURN; - // true means _sp value is correct and we can use it to get the sender's sp // of the compiled frame, otherwise, _sp value may be invalid and we can use // _fp to get the sender's sp if PreserveFramePointer is enabled. @@ -152,11 +150,6 @@ return (intptr_t*) addr_at(offset); } -#ifdef ASSERT - // Used in frame::sender_for_{interpreter,compiled}_frame - static void verify_deopt_original_pc(nmethod* nm, intptr_t* unextended_sp); -#endif - public: // Constructors diff --git a/src/hotspot/cpu/aarch64/frame_aarch64.inline.hpp b/src/hotspot/cpu/aarch64/frame_aarch64.inline.hpp index 47ae93a4932..cb53d8663ad 100644 --- a/src/hotspot/cpu/aarch64/frame_aarch64.inline.hpp +++ b/src/hotspot/cpu/aarch64/frame_aarch64.inline.hpp @@ -116,8 +116,6 @@ inline void frame::init(intptr_t* sp, intptr_t* fp, address pc) { } inline void frame::setup(address pc) { - adjust_unextended_sp(); - address original_pc = get_deopt_original_pc(); if (original_pc != nullptr) { _pc = original_pc; @@ -223,7 +221,6 @@ inline frame::frame(intptr_t* sp, intptr_t* fp) { // assert(_pc != nullptr, "no pc?"); _cb = CodeCache::find_blob(_pc); - adjust_unextended_sp(); address original_pc = get_deopt_original_pc(); if (original_pc != nullptr) { diff --git a/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp b/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp index 948ba97aa22..1e788590b64 100644 --- a/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp @@ -35,8 +35,6 @@ const bool CCallingConventionRequiresIntsAsLongs = false; #define SUPPORTS_NATIVE_CX8 -#define SUPPORT_MONITOR_COUNT - // Aarch64 was not originally defined to be multi-copy-atomic, but now // is. See: "Simplifying ARM Concurrency: Multicopy-atomic Axiomatic // and Operational Models for ARMv8" diff --git a/src/hotspot/cpu/aarch64/javaFrameAnchor_aarch64.hpp b/src/hotspot/cpu/aarch64/javaFrameAnchor_aarch64.hpp index 8d125d3c027..a6e7d74f528 100644 --- a/src/hotspot/cpu/aarch64/javaFrameAnchor_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/javaFrameAnchor_aarch64.hpp @@ -39,24 +39,22 @@ public: // 3 - restoring an old state (javaCalls) void clear(void) { + // No hardware barriers are necessary. All members are volatile and the profiler + // is run from a signal handler and only observers the thread its running on. + // clearing _last_Java_sp must be first _last_Java_sp = nullptr; - OrderAccess::release(); _last_Java_fp = nullptr; _last_Java_pc = nullptr; } void copy(JavaFrameAnchor* src) { - // In order to make sure the transition state is valid for "this" + // No hardware barriers are necessary. All members are volatile and the profiler + // is run from a signal handler and only observers the thread its running on. + // We must clear _last_Java_sp before copying the rest of the new data - // - // Hack Alert: Temporary bugfix for 4717480/4721647 - // To act like previous version (pd_cache_state) don't null _last_Java_sp - // unless the value is changing - // if (_last_Java_sp != src->_last_Java_sp) { _last_Java_sp = nullptr; - OrderAccess::release(); } _last_Java_fp = src->_last_Java_fp; _last_Java_pc = src->_last_Java_pc; diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 3999beeec2b..a37edab8578 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -634,12 +634,13 @@ void MacroAssembler::set_last_Java_frame(Register last_java_sp, last_java_sp = esp; } - str(last_java_sp, Address(rthread, JavaThread::last_Java_sp_offset())); - // last_java_fp is optional if (last_java_fp->is_valid()) { str(last_java_fp, Address(rthread, JavaThread::last_Java_fp_offset())); } + + // We must set sp last. + str(last_java_sp, Address(rthread, JavaThread::last_Java_sp_offset())); } void MacroAssembler::set_last_Java_frame(Register last_java_sp, @@ -5630,38 +5631,6 @@ void MacroAssembler::tlab_allocate(Register obj, bs->tlab_allocate(this, obj, var_size_in_bytes, con_size_in_bytes, t1, t2, slow_case); } -void MacroAssembler::inc_held_monitor_count(Register tmp) { - Address dst(rthread, JavaThread::held_monitor_count_offset()); -#ifdef ASSERT - ldr(tmp, dst); - increment(tmp); - str(tmp, dst); - Label ok; - tbz(tmp, 63, ok); - STOP("assert(held monitor count underflow)"); - should_not_reach_here(); - bind(ok); -#else - increment(dst); -#endif -} - -void MacroAssembler::dec_held_monitor_count(Register tmp) { - Address dst(rthread, JavaThread::held_monitor_count_offset()); -#ifdef ASSERT - ldr(tmp, dst); - decrement(tmp); - str(tmp, dst); - Label ok; - tbz(tmp, 63, ok); - STOP("assert(held monitor count underflow)"); - should_not_reach_here(); - bind(ok); -#else - decrement(dst); -#endif -} - void MacroAssembler::verify_tlab() { #ifdef ASSERT if (UseTLAB && VerifyOops) { diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 0570fad5b8d..705bd19093c 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -983,9 +983,6 @@ public: void push_cont_fastpath(Register java_thread = rthread); void pop_cont_fastpath(Register java_thread = rthread); - void inc_held_monitor_count(Register tmp); - void dec_held_monitor_count(Register tmp); - // Round up to a power of two void round_to(Register reg, int modulus); diff --git a/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp b/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp index f5d7d9e4387..94694b58d2f 100644 --- a/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp @@ -90,7 +90,6 @@ void Relocation::pd_set_call_destination(address x) { void trampoline_stub_Relocation::pd_fix_owner_after_move() { NativeCall* call = nativeCall_at(owner()); - assert(call->raw_destination() == owner(), "destination should be empty"); address trampoline = addr(); address dest = nativeCallTrampolineStub_at(trampoline)->destination(); if (!Assembler::reachable_from_branch_at(owner(), dest)) { diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp index 70af8dd91d8..39609cbe0ac 100644 --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp @@ -985,11 +985,8 @@ static void fill_continuation_entry(MacroAssembler* masm) { __ ldr(rscratch1, Address(rthread, JavaThread::cont_fastpath_offset())); __ str(rscratch1, Address(sp, ContinuationEntry::parent_cont_fastpath_offset())); - __ ldr(rscratch1, Address(rthread, JavaThread::held_monitor_count_offset())); - __ str(rscratch1, Address(sp, ContinuationEntry::parent_held_monitor_count_offset())); __ str(zr, Address(rthread, JavaThread::cont_fastpath_offset())); - __ str(zr, Address(rthread, JavaThread::held_monitor_count_offset())); } // on entry, sp points to the ContinuationEntry @@ -1005,50 +1002,6 @@ static void continuation_enter_cleanup(MacroAssembler* masm) { #endif __ ldr(rscratch1, Address(sp, ContinuationEntry::parent_cont_fastpath_offset())); __ str(rscratch1, Address(rthread, JavaThread::cont_fastpath_offset())); - - if (CheckJNICalls) { - // Check if this is a virtual thread continuation - Label L_skip_vthread_code; - __ ldrw(rscratch1, Address(sp, ContinuationEntry::flags_offset())); - __ cbzw(rscratch1, L_skip_vthread_code); - - // If the held monitor count is > 0 and this vthread is terminating then - // it failed to release a JNI monitor. So we issue the same log message - // that JavaThread::exit does. - __ ldr(rscratch1, Address(rthread, JavaThread::jni_monitor_count_offset())); - __ cbz(rscratch1, L_skip_vthread_code); - - // Save return value potentially containing the exception oop in callee-saved R19. - __ mov(r19, r0); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::log_jni_monitor_still_held)); - // Restore potential return value. - __ mov(r0, r19); - - // For vthreads we have to explicitly zero the JNI monitor count of the carrier - // on termination. The held count is implicitly zeroed below when we restore from - // the parent held count (which has to be zero). - __ str(zr, Address(rthread, JavaThread::jni_monitor_count_offset())); - - __ bind(L_skip_vthread_code); - } -#ifdef ASSERT - else { - // Check if this is a virtual thread continuation - Label L_skip_vthread_code; - __ ldrw(rscratch1, Address(sp, ContinuationEntry::flags_offset())); - __ cbzw(rscratch1, L_skip_vthread_code); - - // See comment just above. If not checking JNI calls the JNI count is only - // needed for assertion checking. - __ str(zr, Address(rthread, JavaThread::jni_monitor_count_offset())); - - __ bind(L_skip_vthread_code); - } -#endif - - __ ldr(rscratch1, Address(sp, ContinuationEntry::parent_held_monitor_count_offset())); - __ str(rscratch1, Address(rthread, JavaThread::held_monitor_count_offset())); - __ ldr(rscratch2, Address(sp, ContinuationEntry::parent_offset())); __ str(rscratch2, Address(rthread, JavaThread::cont_entry_offset())); __ add(rfp, sp, (int)ContinuationEntry::size()); diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index 308deeaf5e2..a04e9defa4b 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2020, Red Hat Inc. All rights reserved. + * Copyright 2025 Arm Limited and/or its affiliates. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -222,10 +223,13 @@ void VM_Version::initialize() { // Neoverse // N1: 0xd0c // N2: 0xd49 + // N3: 0xd8e // V1: 0xd40 // V2: 0xd4f + // V3: 0xd84 if (_cpu == CPU_ARM && (model_is(0xd0c) || model_is(0xd49) || - model_is(0xd40) || model_is(0xd4f))) { + model_is(0xd40) || model_is(0xd4f) || + model_is(0xd8e) || model_is(0xd84))) { if (FLAG_IS_DEFAULT(UseSIMDForMemoryOps)) { FLAG_SET_DEFAULT(UseSIMDForMemoryOps, true); } @@ -260,7 +264,9 @@ void VM_Version::initialize() { // Neoverse // V1: 0xd40 // V2: 0xd4f - if (_cpu == CPU_ARM && (model_is(0xd40) || model_is(0xd4f))) { + // V3: 0xd84 + if (_cpu == CPU_ARM && + (model_is(0xd40) || model_is(0xd4f) || model_is(0xd84))) { if (FLAG_IS_DEFAULT(UseCryptoPmullForCRC32)) { FLAG_SET_DEFAULT(UseCryptoPmullForCRC32, true); } diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index 2835a256153..68fece5263d 100644 --- a/src/hotspot/cpu/arm/arm.ad +++ b/src/hotspot/cpu/arm/arm.ad @@ -1154,10 +1154,6 @@ RegMask Matcher::modL_proj_mask() { return RegMask(); } -const RegMask Matcher::method_handle_invoke_SP_save_mask() { - return FP_REGP_mask(); -} - bool maybe_far_call(const CallNode *n) { return !MacroAssembler::_reachable_from_cache(n->as_Call()->entry_point()); } @@ -1248,23 +1244,6 @@ encode %{ __ set_inst_mark(mark); %} - enc_class preserve_SP %{ - // preserve mark - address mark = __ inst_mark(); - DEBUG_ONLY(int off0 = __ offset()); - // FP is preserved across all calls, even compiled calls. - // Use it to preserve SP in places where the callee might change the SP. - __ mov(Rmh_SP_save, SP); - DEBUG_ONLY(int off1 = __ offset()); - assert(off1 - off0 == 4, "correct size prediction"); - // restore mark - __ set_inst_mark(mark); - %} - - enc_class restore_SP %{ - __ mov(SP, Rmh_SP_save); - %} - enc_class Java_Dynamic_Call (method meth) %{ Register R8_ic_reg = reg_to_register_object(Matcher::inline_cache_reg_encode()); assert(R8_ic_reg == Ricklass, "should be"); @@ -8799,7 +8778,6 @@ instruct safePoint_poll(iRegP poll, R12RegI tmp, flagsReg icc) %{ // Call Java Static Instruction instruct CallStaticJavaDirect( method meth ) %{ match(CallStaticJava); - predicate(! ((CallStaticJavaNode*)n)->is_method_handle_invoke()); effect(USE meth); ins_cost(CALL_COST); @@ -8808,20 +8786,6 @@ instruct CallStaticJavaDirect( method meth ) %{ ins_pipe(simple_call); %} -// Call Java Static Instruction (method handle version) -instruct CallStaticJavaHandle( method meth ) %{ - match(CallStaticJava); - predicate(((CallStaticJavaNode*)n)->is_method_handle_invoke()); - effect(USE meth); - // FP is saved by all callees (for interpreter stack correction). - // We use it here for a similar purpose, in {preserve,restore}_FP. - - ins_cost(CALL_COST); - format %{ "CALL,static/MethodHandle ==> " %} - ins_encode( SetInstMark, preserve_SP, Java_Static_Call( meth ), restore_SP, call_epilog, ClearInstMark ); - ins_pipe(simple_call); -%} - // Call Java Dynamic Instruction instruct CallDynamicJavaDirect( method meth ) %{ match(CallDynamicJava); diff --git a/src/hotspot/cpu/arm/arm_32.ad b/src/hotspot/cpu/arm/arm_32.ad index 1c15d55fbc3..00bf3bd61e4 100644 --- a/src/hotspot/cpu/arm/arm_32.ad +++ b/src/hotspot/cpu/arm/arm_32.ad @@ -1,5 +1,5 @@ // -// Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. // // This code is free software; you can redistribute it and/or modify it @@ -432,8 +432,7 @@ OptoRegPair c2::return_value(int ideal_reg) { int MachCallStaticJavaNode::ret_addr_offset() { bool far = (_method == nullptr) ? maybe_far_call(this) : !cache_reachable(); - return ((far ? 3 : 1) + (_method_handle_invoke ? 1 : 0)) * - NativeInstruction::instruction_size; + return (far ? 3 : 1) * NativeInstruction::instruction_size; } int MachCallDynamicJavaNode::ret_addr_offset() { diff --git a/src/hotspot/cpu/arm/c1_FrameMap_arm.cpp b/src/hotspot/cpu/arm/c1_FrameMap_arm.cpp index 0fd113c8ceb..a820da4d283 100644 --- a/src/hotspot/cpu/arm/c1_FrameMap_arm.cpp +++ b/src/hotspot/cpu/arm/c1_FrameMap_arm.cpp @@ -174,11 +174,6 @@ LIR_Opr FrameMap::stack_pointer() { return FrameMap::SP_opr; } -LIR_Opr FrameMap::method_handle_invoke_SP_save_opr() { - assert(Rmh_SP_save == FP, "Fix register used for saving SP for MethodHandle calls"); - return FP_opr; -} - bool FrameMap::validate_frame() { int max_offset = in_bytes(framesize_in_bytes()); int java_index = 0; diff --git a/src/hotspot/cpu/arm/c1_Runtime1_arm.cpp b/src/hotspot/cpu/arm/c1_Runtime1_arm.cpp index 32da2c24d26..9983f2ce7a2 100644 --- a/src/hotspot/cpu/arm/c1_Runtime1_arm.cpp +++ b/src/hotspot/cpu/arm/c1_Runtime1_arm.cpp @@ -275,14 +275,6 @@ OopMapSet* Runtime1::generate_exception_throw(StubAssembler* sasm, address targe } -static void restore_sp_for_method_handle(StubAssembler* sasm) { - // Restore SP from its saved reg (FP) if the exception PC is a MethodHandle call site. - __ ldr_s32(Rtemp, Address(Rthread, JavaThread::is_method_handle_return_offset())); - __ cmp(Rtemp, 0); - __ mov(SP, Rmh_SP_save, ne); -} - - OopMapSet* Runtime1::generate_handle_exception(StubId id, StubAssembler* sasm) { __ block_comment("generate_handle_exception"); @@ -339,7 +331,6 @@ OopMapSet* Runtime1::generate_handle_exception(StubId id, StubAssembler* sasm) { break; case StubId::c1_handle_exception_from_callee_id: restore_live_registers_without_return(sasm); // must not jump immediately to handler - restore_sp_for_method_handle(sasm); __ ret(); break; default: ShouldNotReachHere(); @@ -372,9 +363,6 @@ void Runtime1::generate_unwind_exception(StubAssembler* sasm) { // Jump to handler __ verify_not_null_oop(Rexception_obj); - // JSR292 extension - restore_sp_for_method_handle(sasm); - __ jump(R0); } diff --git a/src/hotspot/cpu/arm/frame_arm.cpp b/src/hotspot/cpu/arm/frame_arm.cpp index 2722f93edec..7a23296a3d4 100644 --- a/src/hotspot/cpu/arm/frame_arm.cpp +++ b/src/hotspot/cpu/arm/frame_arm.cpp @@ -329,56 +329,6 @@ JavaThread** frame::saved_thread_address(const frame& f) { return nullptr; } -//------------------------------------------------------------------------------ -// frame::verify_deopt_original_pc -// -// Verifies the calculated original PC of a deoptimization PC for the -// given unextended SP. The unextended SP might also be the saved SP -// for MethodHandle call sites. -#ifdef ASSERT -void frame::verify_deopt_original_pc(nmethod* nm, intptr_t* unextended_sp, bool is_method_handle_return) { - frame fr; - - // This is ugly but it's better than to change {get,set}_original_pc - // to take an SP value as argument. And it's only a debugging - // method anyway. - fr._unextended_sp = unextended_sp; - - address original_pc = nm->get_original_pc(&fr); - assert(nm->insts_contains_inclusive(original_pc), - "original PC must be in the main code section of the compiled method (or must be immediately following it)"); - assert(nm->is_method_handle_return(original_pc) == is_method_handle_return, "must be"); -} -#endif - -//------------------------------------------------------------------------------ -// frame::adjust_unextended_sp -void frame::adjust_unextended_sp() { - // same as on x86 - - // If we are returning to a compiled MethodHandle call site, the - // saved_fp will in fact be a saved value of the unextended SP. The - // simplest way to tell whether we are returning to such a call site - // is as follows: - - nmethod* sender_nm = (_cb == nullptr) ? nullptr : _cb->as_nmethod_or_null(); - if (sender_nm != nullptr) { - // If the sender PC is a deoptimization point, get the original - // PC. For MethodHandle call site the unextended_sp is stored in - // saved_fp. - if (sender_nm->is_deopt_mh_entry(_pc)) { - DEBUG_ONLY(verify_deopt_mh_original_pc(sender_nm, _fp)); - _unextended_sp = _fp; - } - else if (sender_nm->is_deopt_entry(_pc)) { - DEBUG_ONLY(verify_deopt_original_pc(sender_nm, _unextended_sp)); - } - else if (sender_nm->is_method_handle_return(_pc)) { - _unextended_sp = _fp; - } - } -} - //------------------------------------------------------------------------------ // frame::update_map_with_saved_link void frame::update_map_with_saved_link(RegisterMap* map, intptr_t** link_addr) { diff --git a/src/hotspot/cpu/arm/frame_arm.hpp b/src/hotspot/cpu/arm/frame_arm.hpp index dec27554a47..6d4ac042831 100644 --- a/src/hotspot/cpu/arm/frame_arm.hpp +++ b/src/hotspot/cpu/arm/frame_arm.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -85,20 +85,11 @@ // original sp. intptr_t* _unextended_sp; - void adjust_unextended_sp(); intptr_t* ptr_at_addr(int offset) const { return (intptr_t*) addr_at(offset); } -#ifdef ASSERT - // Used in frame::sender_for_{interpreter,compiled}_frame - static void verify_deopt_original_pc(nmethod* nm, intptr_t* unextended_sp, bool is_method_handle_return = false); - static void verify_deopt_mh_original_pc(nmethod* nm, intptr_t* unextended_sp) { - verify_deopt_original_pc(nm, unextended_sp, true); - } -#endif - public: // Constructors diff --git a/src/hotspot/cpu/arm/frame_arm.inline.hpp b/src/hotspot/cpu/arm/frame_arm.inline.hpp index 4be190f0504..42e034cfdc7 100644 --- a/src/hotspot/cpu/arm/frame_arm.inline.hpp +++ b/src/hotspot/cpu/arm/frame_arm.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -112,8 +112,6 @@ inline void frame::init(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, add } inline void frame::setup(address pc) { - adjust_unextended_sp(); - address original_pc = get_deopt_original_pc(); if (original_pc != nullptr) { _pc = original_pc; diff --git a/src/hotspot/cpu/arm/register_arm.hpp b/src/hotspot/cpu/arm/register_arm.hpp index fca23d07fee..e0688af0d36 100644 --- a/src/hotspot/cpu/arm/register_arm.hpp +++ b/src/hotspot/cpu/arm/register_arm.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -364,7 +364,6 @@ class VFPSystemRegisterImpl : public AbstractRegisterImpl { // This does not seem to conflict with Rexception_pc // In case of issues, R3 might be OK but adapters calling the runtime would have to save it #define R5_mh R5 // MethodHandle register, used during the call setup -#define Rmh_SP_save FP // for C1 /* * C++ Interpreter Register Defines diff --git a/src/hotspot/cpu/arm/runtime_arm.cpp b/src/hotspot/cpu/arm/runtime_arm.cpp index ea4c9a76381..8d48de5795a 100644 --- a/src/hotspot/cpu/arm/runtime_arm.cpp +++ b/src/hotspot/cpu/arm/runtime_arm.cpp @@ -264,11 +264,6 @@ ExceptionBlob* OptoRuntime::generate_exception_blob() { __ raw_pop(FP, LR); - // Restore SP from its saved reg (FP) if the exception PC is a MethodHandle call site. - __ ldr(Rtemp, Address(Rthread, JavaThread::is_method_handle_return_offset())); - __ cmp(Rtemp, 0); - __ mov(SP, Rmh_SP_save, ne); - // R0 contains handler address // Since this may be the deopt blob we must set R5 to look like we returned // from the original pc that threw the exception diff --git a/src/hotspot/cpu/ppc/c1_FrameMap_ppc.cpp b/src/hotspot/cpu/ppc/c1_FrameMap_ppc.cpp index 8ce324a570b..cb9368f2ce9 100644 --- a/src/hotspot/cpu/ppc/c1_FrameMap_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_FrameMap_ppc.cpp @@ -374,15 +374,6 @@ LIR_Opr FrameMap::stack_pointer() { return SP_opr; } - -// JSR 292 -// On PPC64, there is no need to save the SP, because neither -// method handle intrinsics, nor compiled lambda forms modify it. -LIR_Opr FrameMap::method_handle_invoke_SP_save_opr() { - return LIR_OprFact::illegalOpr; -} - - bool FrameMap::validate_frame() { int max_offset = in_bytes(framesize_in_bytes()); int java_index = 0; diff --git a/src/hotspot/cpu/ppc/globalDefinitions_ppc.hpp b/src/hotspot/cpu/ppc/globalDefinitions_ppc.hpp index f8f15741301..6c41e56b20b 100644 --- a/src/hotspot/cpu/ppc/globalDefinitions_ppc.hpp +++ b/src/hotspot/cpu/ppc/globalDefinitions_ppc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2016 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -43,8 +43,6 @@ const bool CCallingConventionRequiresIntsAsLongs = true; #define SUPPORTS_NATIVE_CX8 -#define SUPPORT_MONITOR_COUNT - // PPC64 is not specified as multi-copy-atomic // So we must not #define CPU_MULTI_COPY_ATOMIC diff --git a/src/hotspot/cpu/ppc/nativeInst_ppc.cpp b/src/hotspot/cpu/ppc/nativeInst_ppc.cpp index 4e93992f413..ebdfd9a57d6 100644 --- a/src/hotspot/cpu/ppc/nativeInst_ppc.cpp +++ b/src/hotspot/cpu/ppc/nativeInst_ppc.cpp @@ -402,7 +402,7 @@ void NativePostCallNop::make_deopt() { bool NativePostCallNop::patch(int32_t oopmap_slot, int32_t cb_offset) { int32_t i2, i1; assert(is_aligned(cb_offset, 4), "cb offset alignment does not match instruction alignment"); - assert(!decode(i1, i2), "already patched"); + assert(!decode(i1, i2) || NMethodRelocation, "already patched"); cb_offset = cb_offset >> 2; if (((oopmap_slot & ppc_oopmap_slot_mask) != oopmap_slot) || ((cb_offset & ppc_cb_offset_mask) != cb_offset)) { diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 290369360fc..2c83b2d5765 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -2473,10 +2473,6 @@ RegMask Matcher::modL_proj_mask() { return RegMask(); } -const RegMask Matcher::method_handle_invoke_SP_save_mask() { - return RegMask(); -} - %} //----------ENCODING BLOCK----------------------------------------------------- @@ -3434,7 +3430,6 @@ encode %{ // Create the call node. CallDynamicJavaDirectSchedNode *call = new CallDynamicJavaDirectSchedNode(); - call->_method_handle_invoke = _method_handle_invoke; call->_vtable_index = _vtable_index; call->_method = _method; call->_optimized_virtual = _optimized_virtual; diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index aec36b3f3f9..9fe7e1f22ff 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -1639,7 +1639,6 @@ static void fill_continuation_entry(MacroAssembler* masm, Register reg_cont_obj, assert_different_registers(reg_cont_obj, reg_flags); Register zero = R8_ARG6; Register tmp2 = R9_ARG7; - Register tmp3 = R10_ARG8; DEBUG_ONLY(__ block_comment("fill {")); #ifdef ASSERT @@ -1655,12 +1654,9 @@ static void fill_continuation_entry(MacroAssembler* masm, Register reg_cont_obj, __ stw(zero, in_bytes(ContinuationEntry::pin_count_offset()), R1_SP); __ ld_ptr(tmp2, JavaThread::cont_fastpath_offset(), R16_thread); - __ ld(tmp3, in_bytes(JavaThread::held_monitor_count_offset()), R16_thread); __ st_ptr(tmp2, ContinuationEntry::parent_cont_fastpath_offset(), R1_SP); - __ std(tmp3, in_bytes(ContinuationEntry::parent_held_monitor_count_offset()), R1_SP); __ st_ptr(zero, JavaThread::cont_fastpath_offset(), R16_thread); - __ std(zero, in_bytes(JavaThread::held_monitor_count_offset()), R16_thread); DEBUG_ONLY(__ block_comment("} fill")); } @@ -1681,7 +1677,6 @@ static void fill_continuation_entry(MacroAssembler* masm, Register reg_cont_obj, static void continuation_enter_cleanup(MacroAssembler* masm) { Register tmp1 = R8_ARG6; Register tmp2 = R9_ARG7; - Register tmp3 = R10_ARG8; #ifdef ASSERT __ block_comment("clean {"); @@ -1692,57 +1687,8 @@ static void continuation_enter_cleanup(MacroAssembler* masm) { __ ld_ptr(tmp1, ContinuationEntry::parent_cont_fastpath_offset(), R1_SP); __ st_ptr(tmp1, JavaThread::cont_fastpath_offset(), R16_thread); - - if (CheckJNICalls) { - // Check if this is a virtual thread continuation - Label L_skip_vthread_code; - __ lwz(R0, in_bytes(ContinuationEntry::flags_offset()), R1_SP); - __ cmpwi(CR0, R0, 0); - __ beq(CR0, L_skip_vthread_code); - - // If the held monitor count is > 0 and this vthread is terminating then - // it failed to release a JNI monitor. So we issue the same log message - // that JavaThread::exit does. - __ ld(R0, in_bytes(JavaThread::jni_monitor_count_offset()), R16_thread); - __ cmpdi(CR0, R0, 0); - __ beq(CR0, L_skip_vthread_code); - - // Save return value potentially containing the exception oop - Register ex_oop = R15_esp; // nonvolatile register - __ mr(ex_oop, R3_RET); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::log_jni_monitor_still_held)); - // Restore potental return value - __ mr(R3_RET, ex_oop); - - // For vthreads we have to explicitly zero the JNI monitor count of the carrier - // on termination. The held count is implicitly zeroed below when we restore from - // the parent held count (which has to be zero). - __ li(tmp1, 0); - __ std(tmp1, in_bytes(JavaThread::jni_monitor_count_offset()), R16_thread); - - __ bind(L_skip_vthread_code); - } -#ifdef ASSERT - else { - // Check if this is a virtual thread continuation - Label L_skip_vthread_code; - __ lwz(R0, in_bytes(ContinuationEntry::flags_offset()), R1_SP); - __ cmpwi(CR0, R0, 0); - __ beq(CR0, L_skip_vthread_code); - - // See comment just above. If not checking JNI calls the JNI count is only - // needed for assertion checking. - __ li(tmp1, 0); - __ std(tmp1, in_bytes(JavaThread::jni_monitor_count_offset()), R16_thread); - - __ bind(L_skip_vthread_code); - } -#endif - - __ ld(tmp2, in_bytes(ContinuationEntry::parent_held_monitor_count_offset()), R1_SP); - __ ld_ptr(tmp3, ContinuationEntry::parent_offset(), R1_SP); - __ std(tmp2, in_bytes(JavaThread::held_monitor_count_offset()), R16_thread); - __ st_ptr(tmp3, JavaThread::cont_entry_offset(), R16_thread); + __ ld_ptr(tmp2, ContinuationEntry::parent_offset(), R1_SP); + __ st_ptr(tmp2, JavaThread::cont_entry_offset(), R16_thread); DEBUG_ONLY(__ block_comment("} clean")); } diff --git a/src/hotspot/cpu/riscv/c1_FrameMap_riscv.cpp b/src/hotspot/cpu/riscv/c1_FrameMap_riscv.cpp index d3ccd46048b..b3a0b261f54 100644 --- a/src/hotspot/cpu/riscv/c1_FrameMap_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_FrameMap_riscv.cpp @@ -377,11 +377,6 @@ LIR_Opr FrameMap::stack_pointer() { return FrameMap::sp_opr; } -// JSR 292 -LIR_Opr FrameMap::method_handle_invoke_SP_save_opr() { - return LIR_OprFact::illegalOpr; // Not needed on riscv -} - bool FrameMap::validate_frame() { return true; } diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index 1bdb7bc2f7c..154b62db47f 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -1687,6 +1687,7 @@ void C2_MacroAssembler::arrays_hashcode(Register ary, Register cnt, Register res Register tmp4, Register tmp5, Register tmp6, BasicType eltype) { + assert(!UseRVV, "sanity"); assert_different_registers(ary, cnt, result, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, t0, t1); const int elsize = arrays_hashcode_elsize(eltype); @@ -1759,29 +1760,143 @@ void C2_MacroAssembler::arrays_hashcode(Register ary, Register cnt, Register res BLOCK_COMMENT("} // arrays_hashcode"); } +void C2_MacroAssembler::arrays_hashcode_v(Register ary, Register cnt, Register result, + Register tmp1, Register tmp2, Register tmp3, + BasicType eltype) +{ + assert(UseRVV, "sanity"); + assert(StubRoutines::riscv::arrays_hashcode_powers_of_31() != nullptr, "sanity"); + assert_different_registers(ary, cnt, result, tmp1, tmp2, tmp3, t0, t1); + + // The MaxVectorSize should have been set by detecting RVV max vector register + // size when check UseRVV (i.e. MaxVectorSize == VM_Version::_initial_vector_length). + // Let's use T_INT as all hashCode calculations eventually deal with ints. + const int lmul = 2; + const int stride = MaxVectorSize / sizeof(jint) * lmul; + + const int elsize_bytes = arrays_hashcode_elsize(eltype); + const int elsize_shift = exact_log2(elsize_bytes); + + switch (eltype) { + case T_BOOLEAN: BLOCK_COMMENT("arrays_hashcode_v(unsigned byte) {"); break; + case T_CHAR: BLOCK_COMMENT("arrays_hashcode_v(char) {"); break; + case T_BYTE: BLOCK_COMMENT("arrays_hashcode_v(byte) {"); break; + case T_SHORT: BLOCK_COMMENT("arrays_hashcode_v(short) {"); break; + case T_INT: BLOCK_COMMENT("arrays_hashcode_v(int) {"); break; + default: + ShouldNotReachHere(); + } + + const Register pow31_highest = tmp1; + const Register ary_end = tmp2; + const Register consumed = tmp3; + + const VectorRegister v_sum = v2; + const VectorRegister v_src = v4; + const VectorRegister v_coeffs = v6; + const VectorRegister v_tmp = v8; + + const address adr_pows31 = StubRoutines::riscv::arrays_hashcode_powers_of_31() + + sizeof(jint); + Label VEC_LOOP, DONE, SCALAR_TAIL, SCALAR_TAIL_LOOP; + + // NB: at this point (a) 'result' already has some value, + // (b) 'cnt' is not 0 or 1, see java code for details. + + andi(t0, cnt, ~(stride - 1)); + beqz(t0, SCALAR_TAIL); + + la(t1, ExternalAddress(adr_pows31)); + lw(pow31_highest, Address(t1, -1 * sizeof(jint))); + + vsetvli(consumed, cnt, Assembler::e32, Assembler::m2); + vle32_v(v_coeffs, t1); // 31^^(stride - 1) ... 31^^0 + vmv_v_x(v_sum, x0); + + bind(VEC_LOOP); + arrays_hashcode_elload_v(v_src, v_tmp, ary, eltype); + vmul_vv(v_src, v_src, v_coeffs); + vmadd_vx(v_sum, pow31_highest, v_src); + mulw(result, result, pow31_highest); + shadd(ary, consumed, ary, t0, elsize_shift); + subw(cnt, cnt, consumed); + andi(t1, cnt, ~(stride - 1)); + bnez(t1, VEC_LOOP); + + vmv_s_x(v_tmp, x0); + vredsum_vs(v_sum, v_sum, v_tmp); + vmv_x_s(t0, v_sum); + addw(result, result, t0); + beqz(cnt, DONE); + + bind(SCALAR_TAIL); + shadd(ary_end, cnt, ary, t0, elsize_shift); + + bind(SCALAR_TAIL_LOOP); + arrays_hashcode_elload(t0, Address(ary), eltype); + slli(t1, result, 5); // optimize 31 * result + subw(result, t1, result); // with result<<5 - result + addw(result, result, t0); + addi(ary, ary, elsize_bytes); + bne(ary, ary_end, SCALAR_TAIL_LOOP); + + bind(DONE); + BLOCK_COMMENT("} // arrays_hashcode_v"); +} + int C2_MacroAssembler::arrays_hashcode_elsize(BasicType eltype) { switch (eltype) { - case T_BOOLEAN: return sizeof(jboolean); - case T_BYTE: return sizeof(jbyte); - case T_SHORT: return sizeof(jshort); - case T_CHAR: return sizeof(jchar); - case T_INT: return sizeof(jint); - default: - ShouldNotReachHere(); - return -1; + case T_BOOLEAN: return sizeof(jboolean); + case T_BYTE: return sizeof(jbyte); + case T_SHORT: return sizeof(jshort); + case T_CHAR: return sizeof(jchar); + case T_INT: return sizeof(jint); + default: + ShouldNotReachHere(); + return -1; } } void C2_MacroAssembler::arrays_hashcode_elload(Register dst, Address src, BasicType eltype) { switch (eltype) { - // T_BOOLEAN used as surrogate for unsigned byte - case T_BOOLEAN: lbu(dst, src); break; - case T_BYTE: lb(dst, src); break; - case T_SHORT: lh(dst, src); break; - case T_CHAR: lhu(dst, src); break; - case T_INT: lw(dst, src); break; - default: - ShouldNotReachHere(); + // T_BOOLEAN used as surrogate for unsigned byte + case T_BOOLEAN: lbu(dst, src); break; + case T_BYTE: lb(dst, src); break; + case T_SHORT: lh(dst, src); break; + case T_CHAR: lhu(dst, src); break; + case T_INT: lw(dst, src); break; + default: + ShouldNotReachHere(); + } +} + +void C2_MacroAssembler::arrays_hashcode_elload_v(VectorRegister vdst, + VectorRegister vtmp, + Register src, + BasicType eltype) { + assert_different_registers(vdst, vtmp); + switch (eltype) { + case T_BOOLEAN: + vle8_v(vtmp, src); + vzext_vf4(vdst, vtmp); + break; + case T_BYTE: + vle8_v(vtmp, src); + vsext_vf4(vdst, vtmp); + break; + case T_CHAR: + vle16_v(vtmp, src); + vzext_vf2(vdst, vtmp); + break; + case T_SHORT: + vle16_v(vtmp, src); + vsext_vf2(vdst, vtmp); + break; + case T_INT: + vle32_v(vdst, src); + break; + default: + ShouldNotReachHere(); } } diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp index 309ef8d9d5e..2d5339dc153 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp @@ -92,11 +92,15 @@ Register tmp3, Register tmp4, Register tmp5, Register tmp6, BasicType eltype); - - // helper function for arrays_hashcode int arrays_hashcode_elsize(BasicType eltype); void arrays_hashcode_elload(Register dst, Address src, BasicType eltype); + void arrays_hashcode_v(Register ary, Register cnt, Register result, + Register tmp1, Register tmp2, Register tmp3, + BasicType eltype); + void arrays_hashcode_elload_v(VectorRegister vdst, VectorRegister vtmp, + Register src, BasicType eltype); + void string_equals(Register r1, Register r2, Register result, Register cnt1); diff --git a/src/hotspot/cpu/riscv/frame_riscv.cpp b/src/hotspot/cpu/riscv/frame_riscv.cpp index b677c980c78..19dbdd6aeae 100644 --- a/src/hotspot/cpu/riscv/frame_riscv.cpp +++ b/src/hotspot/cpu/riscv/frame_riscv.cpp @@ -217,8 +217,7 @@ bool frame::safe_for_sender(JavaThread *thread) { nmethod* nm = sender_blob->as_nmethod_or_null(); if (nm != nullptr) { - if (nm->is_deopt_mh_entry(sender_pc) || nm->is_deopt_entry(sender_pc) || - nm->method()->is_method_handle_intrinsic()) { + if (nm->is_deopt_entry(sender_pc) || nm->method()->is_method_handle_intrinsic()) { return false; } } @@ -427,49 +426,6 @@ JavaThread** frame::saved_thread_address(const frame& f) { return thread_addr; } -//------------------------------------------------------------------------------ -// frame::verify_deopt_original_pc -// -// Verifies the calculated original PC of a deoptimization PC for the -// given unextended SP. -#ifdef ASSERT -void frame::verify_deopt_original_pc(nmethod* nm, intptr_t* unextended_sp) { - frame fr; - - // This is ugly but it's better than to change {get,set}_original_pc - // to take an SP value as argument. And it's only a debugging - // method anyway. - fr._unextended_sp = unextended_sp; - - assert_cond(nm != nullptr); - address original_pc = nm->get_original_pc(&fr); - assert(nm->insts_contains_inclusive(original_pc), - "original PC must be in the main code section of the compiled method (or must be immediately following it)"); -} -#endif - -//------------------------------------------------------------------------------ -// frame::adjust_unextended_sp -#ifdef ASSERT -void frame::adjust_unextended_sp() { - // On riscv, sites calling method handle intrinsics and lambda forms are treated - // as any other call site. Therefore, no special action is needed when we are - // returning to any of these call sites. - - if (_cb != nullptr) { - nmethod* sender_nm = _cb->as_nmethod_or_null(); - if (sender_nm != nullptr) { - // If the sender PC is a deoptimization point, get the original PC. - if (sender_nm->is_deopt_entry(_pc) || - sender_nm->is_deopt_mh_entry(_pc)) { - verify_deopt_original_pc(sender_nm, _unextended_sp); - } - } - } -} -#endif - - //------------------------------------------------------------------------------ // frame::sender_for_interpreter_frame frame frame::sender_for_interpreter_frame(RegisterMap* map) const { diff --git a/src/hotspot/cpu/riscv/frame_riscv.hpp b/src/hotspot/cpu/riscv/frame_riscv.hpp index b4540c45ab8..ce5a8dde230 100644 --- a/src/hotspot/cpu/riscv/frame_riscv.hpp +++ b/src/hotspot/cpu/riscv/frame_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -179,17 +179,10 @@ int _offset_unextended_sp; // for use in stack-chunk frames }; - void adjust_unextended_sp() NOT_DEBUG_RETURN; - intptr_t* ptr_at_addr(int offset) const { return (intptr_t*) addr_at(offset); } -#ifdef ASSERT - // Used in frame::sender_for_{interpreter,compiled}_frame - static void verify_deopt_original_pc(nmethod* nm, intptr_t* unextended_sp); -#endif - public: // Constructors diff --git a/src/hotspot/cpu/riscv/frame_riscv.inline.hpp b/src/hotspot/cpu/riscv/frame_riscv.inline.hpp index fb31760e20b..51a203c548c 100644 --- a/src/hotspot/cpu/riscv/frame_riscv.inline.hpp +++ b/src/hotspot/cpu/riscv/frame_riscv.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -114,8 +114,6 @@ inline void frame::init(intptr_t* ptr_sp, intptr_t* ptr_fp, address pc) { } inline void frame::setup(address pc) { - adjust_unextended_sp(); - address original_pc = get_deopt_original_pc(); if (original_pc != nullptr) { _pc = original_pc; @@ -215,7 +213,6 @@ inline frame::frame(intptr_t* ptr_sp, intptr_t* ptr_fp) { // value. _cb = CodeCache::find_blob(_pc); - adjust_unextended_sp(); address original_pc = get_deopt_original_pc(); if (original_pc != nullptr) { diff --git a/src/hotspot/cpu/riscv/globalDefinitions_riscv.hpp b/src/hotspot/cpu/riscv/globalDefinitions_riscv.hpp index 407017ee1c0..57223cf4390 100644 --- a/src/hotspot/cpu/riscv/globalDefinitions_riscv.hpp +++ b/src/hotspot/cpu/riscv/globalDefinitions_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2015, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -44,8 +44,6 @@ const bool CCallingConventionRequiresIntsAsLongs = false; #define SUPPORTS_NATIVE_CX8 -#define SUPPORT_MONITOR_COUNT - #define SUPPORT_RESERVED_STACK_AREA #define USE_POINTERS_TO_REGISTER_IMPL_ARRAY diff --git a/src/hotspot/cpu/riscv/javaFrameAnchor_riscv.hpp b/src/hotspot/cpu/riscv/javaFrameAnchor_riscv.hpp index 1293fae0c0b..6bf1230914a 100644 --- a/src/hotspot/cpu/riscv/javaFrameAnchor_riscv.hpp +++ b/src/hotspot/cpu/riscv/javaFrameAnchor_riscv.hpp @@ -39,25 +39,23 @@ public: // 3 - restoring an old state (javaCalls) void clear(void) { + // No hardware barriers are necessary. All members are volatile and the profiler + // is run from a signal handler and the only observer is the thread its running on. + // clearing _last_Java_sp must be first _last_Java_sp = nullptr; - OrderAccess::release(); _last_Java_fp = nullptr; _last_Java_pc = nullptr; } void copy(JavaFrameAnchor* src) { - // In order to make sure the transition state is valid for "this" + // No hardware barriers are necessary. All members are volatile and the profiler + // is run from a signal handler and the only observer is the thread its running on. + // We must clear _last_Java_sp before copying the rest of the new data - // - // Hack Alert: Temporary bugfix for 4717480/4721647 - // To act like previous version (pd_cache_state) don't null _last_Java_sp - // unless the value is changing - // assert(src != nullptr, "Src should not be null."); if (_last_Java_sp != src->_last_Java_sp) { _last_Java_sp = nullptr; - OrderAccess::release(); } _last_Java_fp = src->_last_Java_fp; _last_Java_pc = src->_last_Java_pc; diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index 5c85cc13bed..700e42e6194 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -225,36 +225,6 @@ void MacroAssembler::pop_cont_fastpath(Register java_thread) { bind(done); } -void MacroAssembler::inc_held_monitor_count(Register tmp) { - Address dst(xthread, JavaThread::held_monitor_count_offset()); - ld(tmp, dst); - addi(tmp, tmp, 1); - sd(tmp, dst); -#ifdef ASSERT - Label ok; - test_bit(tmp, tmp, 63); - beqz(tmp, ok); - STOP("assert(held monitor count overflow)"); - should_not_reach_here(); - bind(ok); -#endif -} - -void MacroAssembler::dec_held_monitor_count(Register tmp) { - Address dst(xthread, JavaThread::held_monitor_count_offset()); - ld(tmp, dst); - subi(tmp, tmp, 1); - sd(tmp, dst); -#ifdef ASSERT - Label ok; - test_bit(tmp, tmp, 63); - beqz(tmp, ok); - STOP("assert(held monitor count underflow)"); - should_not_reach_here(); - bind(ok); -#endif -} - int MacroAssembler::align(int modulus, int extra_offset) { CompressibleScope scope(this); intptr_t before = offset(); @@ -390,12 +360,14 @@ void MacroAssembler::set_last_Java_frame(Register last_java_sp, last_java_sp = esp; } - sd(last_java_sp, Address(xthread, JavaThread::last_Java_sp_offset())); - // last_java_fp is optional if (last_java_fp->is_valid()) { sd(last_java_fp, Address(xthread, JavaThread::last_Java_fp_offset())); } + + // We must set sp last. + sd(last_java_sp, Address(xthread, JavaThread::last_Java_sp_offset())); + } void MacroAssembler::set_last_Java_frame(Register last_java_sp, diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index 13b70d5dbd7..9e713e90270 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2024, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -849,9 +849,6 @@ public: void push_cont_fastpath(Register java_thread = xthread); void pop_cont_fastpath(Register java_thread = xthread); - void inc_held_monitor_count(Register tmp); - void dec_held_monitor_count(Register tmp); - // if heap base register is used - reinit it with the correct value void reinit_heapbase(); diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index d816f2405c4..009acd628a0 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -2152,10 +2152,6 @@ RegMask Matcher::modL_proj_mask() { return RegMask(); } -const RegMask Matcher::method_handle_invoke_SP_save_mask() { - return FP_REG_mask(); -} - bool size_fits_all_mem_uses(AddPNode* addp, int shift) { assert_cond(addp != nullptr); for (DUIterator_Fast imax, i = addp->fast_outs(imax); i < imax; i++) { @@ -10995,6 +10991,7 @@ instruct arrays_hashcode(iRegP_R11 ary, iRegI_R12 cnt, iRegI_R10 result, immI ba iRegLNoSp tmp3, iRegLNoSp tmp4, iRegLNoSp tmp5, iRegLNoSp tmp6, rFlagsReg cr) %{ + predicate(!UseRVV); match(Set result (VectorizedHashCode (Binary ary cnt) (Binary result basic_type))); effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, TEMP tmp6, USE_KILL ary, USE_KILL cnt, USE basic_type, KILL cr); diff --git a/src/hotspot/cpu/riscv/riscv_v.ad b/src/hotspot/cpu/riscv/riscv_v.ad index f2845ee2a6c..fe323474d60 100644 --- a/src/hotspot/cpu/riscv/riscv_v.ad +++ b/src/hotspot/cpu/riscv/riscv_v.ad @@ -4080,6 +4080,28 @@ instruct varray_equalsC(iRegP_R11 ary1, iRegP_R12 ary2, iRegI_R10 result, ins_pipe(pipe_class_memory); %} +// fast ArraysSupport.vectorizedHashCode +instruct varrays_hashcode(iRegP_R11 ary, iRegI_R12 cnt, iRegI_R10 result, immI basic_type, + vReg_V2 v2, vReg_V3 v3, vReg_V4 v4, vReg_V5 v5, + vReg_V6 v6, vReg_V7 v7, vReg_V8 v8, vReg_V9 v9, + iRegLNoSp tmp1, iRegLNoSp tmp2, iRegLNoSp tmp3, + rFlagsReg cr) +%{ + predicate(UseRVV); + match(Set result (VectorizedHashCode (Binary ary cnt) (Binary result basic_type))); + effect(USE_KILL ary, USE_KILL cnt, USE basic_type, + TEMP v2, TEMP v3, TEMP v4, TEMP v5, TEMP v6, TEMP v7, TEMP v8, TEMP v9, + TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + + format %{ "Array HashCode array[] $ary,$cnt,$result,$basic_type -> $result // KILL all" %} + ins_encode %{ + __ arrays_hashcode_v($ary$$Register, $cnt$$Register, $result$$Register, + $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, + (BasicType)$basic_type$$constant); + %} + ins_pipe(pipe_class_memory); +%} + instruct vstring_compareU_128b(iRegP_R11 str1, iRegI_R12 cnt1, iRegP_R13 str2, iRegI_R14 cnt2, iRegI_R10 result, vReg_V4 v4, vReg_V5 v5, vReg_V6 v6, vReg_V7 v7, vReg_V8 v8, vReg_V9 v9, vReg_V10 v10, vReg_V11 v11, diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp index 94506e9f19d..b303178a666 100644 --- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp +++ b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp @@ -885,11 +885,8 @@ static void fill_continuation_entry(MacroAssembler* masm) { __ ld(t0, Address(xthread, JavaThread::cont_fastpath_offset())); __ sd(t0, Address(sp, ContinuationEntry::parent_cont_fastpath_offset())); - __ ld(t0, Address(xthread, JavaThread::held_monitor_count_offset())); - __ sd(t0, Address(sp, ContinuationEntry::parent_held_monitor_count_offset())); __ sd(zr, Address(xthread, JavaThread::cont_fastpath_offset())); - __ sd(zr, Address(xthread, JavaThread::held_monitor_count_offset())); } // on entry, sp points to the ContinuationEntry @@ -905,50 +902,6 @@ static void continuation_enter_cleanup(MacroAssembler* masm) { __ ld(t0, Address(sp, ContinuationEntry::parent_cont_fastpath_offset())); __ sd(t0, Address(xthread, JavaThread::cont_fastpath_offset())); - - if (CheckJNICalls) { - // Check if this is a virtual thread continuation - Label L_skip_vthread_code; - __ lwu(t0, Address(sp, ContinuationEntry::flags_offset())); - __ beqz(t0, L_skip_vthread_code); - - // If the held monitor count is > 0 and this vthread is terminating then - // it failed to release a JNI monitor. So we issue the same log message - // that JavaThread::exit does. - __ ld(t0, Address(xthread, JavaThread::jni_monitor_count_offset())); - __ beqz(t0, L_skip_vthread_code); - - // Save return value potentially containing the exception oop in callee-saved x9 - __ mv(x9, x10); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::log_jni_monitor_still_held)); - // Restore potential return value - __ mv(x10, x9); - - // For vthreads we have to explicitly zero the JNI monitor count of the carrier - // on termination. The held count is implicitly zeroed below when we restore from - // the parent held count (which has to be zero). - __ sd(zr, Address(xthread, JavaThread::jni_monitor_count_offset())); - - __ bind(L_skip_vthread_code); - } -#ifdef ASSERT - else { - // Check if this is a virtual thread continuation - Label L_skip_vthread_code; - __ lwu(t0, Address(sp, ContinuationEntry::flags_offset())); - __ beqz(t0, L_skip_vthread_code); - - // See comment just above. If not checking JNI calls the JNI count is only - // needed for assertion checking. - __ sd(zr, Address(xthread, JavaThread::jni_monitor_count_offset())); - - __ bind(L_skip_vthread_code); - } -#endif - - __ ld(t0, Address(sp, ContinuationEntry::parent_held_monitor_count_offset())); - __ sd(t0, Address(xthread, JavaThread::held_monitor_count_offset())); - __ ld(t0, Address(sp, ContinuationEntry::parent_offset())); __ sd(t0, Address(xthread, JavaThread::cont_entry_offset())); __ add(fp, sp, (int)ContinuationEntry::size() + 2 * wordSize /* 2 extra words to match up with leave() */); diff --git a/src/hotspot/cpu/riscv/stubDeclarations_riscv.hpp b/src/hotspot/cpu/riscv/stubDeclarations_riscv.hpp index fe7f52884fa..f977d759d20 100644 --- a/src/hotspot/cpu/riscv/stubDeclarations_riscv.hpp +++ b/src/hotspot/cpu/riscv/stubDeclarations_riscv.hpp @@ -73,6 +73,9 @@ do_stub(compiler, string_indexof_linear_ul) \ do_arch_entry(riscv, compiler, string_indexof_linear_ul, \ string_indexof_linear_ul, string_indexof_linear_ul) \ + do_stub(compiler, arrays_hashcode_powers_of_31) \ + do_arch_entry(riscv, compiler, arrays_hashcode_powers_of_31, \ + arrays_hashcode_powers_of_31, arrays_hashcode_powers_of_31) \ #define STUBGEN_FINAL_BLOBS_ARCH_DO(do_stub, \ diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index 88961ccd5a4..ec268d9bb65 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -6624,6 +6624,24 @@ static const int64_t right_3_bits = right_n_bits(3); return start; } + address generate_arrays_hashcode_powers_of_31() { + assert(UseRVV, "sanity"); + const int lmul = 2; + const int stride = MaxVectorSize / sizeof(jint) * lmul; + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "arrays_hashcode_powers_of_31"); + address start = __ pc(); + for (int i = stride; i >= 0; i--) { + jint power_of_31 = 1; + for (int j = i; j > 0; j--) { + power_of_31 = java_multiply(power_of_31, 31); + } + __ emit_int32(power_of_31); + } + + return start; + } + #endif // COMPILER2 /** @@ -6818,6 +6836,10 @@ static const int64_t right_3_bits = right_n_bits(3); StubRoutines::_bigIntegerRightShiftWorker = generate_bigIntegerRightShift(); } + if (UseVectorizedHashCodeIntrinsic && UseRVV) { + StubRoutines::riscv::_arrays_hashcode_powers_of_31 = generate_arrays_hashcode_powers_of_31(); + } + if (UseSHA256Intrinsics) { Sha2Generator sha2(_masm, this); StubRoutines::_sha256_implCompress = sha2.generate_sha256_implCompress(StubId::stubgen_sha256_implCompress_id); diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp index 53f7ff9bc66..9d6146a8389 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp @@ -35,20 +35,20 @@ uint32_t VM_Version::_initial_vector_length = 0; -#define DEF_RV_EXT_FEATURE(NAME, PRETTY, LINUX_BIT, FSTRING, FLAGF) \ -VM_Version::NAME##RVExtFeatureValue VM_Version::NAME; +#define DEF_RV_EXT_FEATURE(PRETTY, LINUX_BIT, FSTRING, FLAGF) \ +VM_Version::ext_##PRETTY##RVExtFeatureValue VM_Version::ext_##PRETTY; RV_EXT_FEATURE_FLAGS(DEF_RV_EXT_FEATURE) #undef DEF_RV_EXT_FEATURE -#define DEF_RV_NON_EXT_FEATURE(NAME, PRETTY, LINUX_BIT, FSTRING, FLAGF) \ -VM_Version::NAME##RVNonExtFeatureValue VM_Version::NAME; +#define DEF_RV_NON_EXT_FEATURE(PRETTY, LINUX_BIT, FSTRING, FLAGF) \ +VM_Version::PRETTY##RVNonExtFeatureValue VM_Version::PRETTY; RV_NON_EXT_FEATURE_FLAGS(DEF_RV_NON_EXT_FEATURE) #undef DEF_RV_NON_EXT_FEATURE -#define ADD_RV_EXT_FEATURE_IN_LIST(NAME, PRETTY, LINUX_BIT, FSTRING, FLAGF) \ - &VM_Version::NAME, -#define ADD_RV_NON_EXT_FEATURE_IN_LIST(NAME, PRETTY, LINUX_BIT, FSTRING, FLAGF) \ - &VM_Version::NAME, +#define ADD_RV_EXT_FEATURE_IN_LIST(PRETTY, LINUX_BIT, FSTRING, FLAGF) \ + &VM_Version::ext_##PRETTY, +#define ADD_RV_NON_EXT_FEATURE_IN_LIST(PRETTY, LINUX_BIT, FSTRING, FLAGF) \ + &VM_Version::PRETTY, VM_Version::RVFeatureValue* VM_Version::_feature_list[] = { RV_EXT_FEATURE_FLAGS(ADD_RV_EXT_FEATURE_IN_LIST) RV_NON_EXT_FEATURE_FLAGS(ADD_RV_NON_EXT_FEATURE_IN_LIST) @@ -148,7 +148,7 @@ void VM_Version::common_initialize() { FLAG_SET_DEFAULT(UseSignumIntrinsic, true); } - if (UseRVC && !ext_C.enabled()) { + if (UseRVC && !ext_c.enabled()) { warning("RVC is not supported on this CPU"); FLAG_SET_DEFAULT(UseRVC, false); diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp index b3d905dff9d..346ca35dc1e 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp @@ -185,52 +185,6 @@ class VM_Version : public Abstract_VM_Version { } }; - // Frozen standard extensions - // I RV64I - // M Integer Multiplication and Division - // A Atomic Instructions - // F Single-Precision Floating-Point - // D Single-Precision Floating-Point - // (G = M + A + F + D) - // Q Quad-Precision Floating-Point - // C Compressed Instructions - // H Hypervisor - // - // Others, open and non-standard - // V Vector - // - // Cache Management Operations - // Zicbom Cache Block Management Operations - // Zicboz Cache Block Zero Operations - // Zicbop Cache Block Prefetch Operations - // - // Bit-manipulation - // Zba Address generation instructions - // Zbb Basic bit-manipulation - // Zbc Carry-less multiplication - // Zbs Single-bit instructions - // - // Zfh Half-Precision Floating-Point instructions - // Zfhmin Minimal Half-Precision Floating-Point instructions - // - // Zicond Conditional operations - // - // Zicsr Control and Status Register (CSR) Instructions - // Zifencei Instruction-Fetch Fence - // Zic64b Cache blocks must be 64 bytes in size, naturally aligned in the address space. - // Zihintpause Pause instruction HINT - // - // Zc Code Size Reduction - Additional compressed instructions. - // Zcb Simple code-size saving instructions - // - // Other features and settings - // mvendorid Manufactory JEDEC id encoded, ISA vol 2 3.1.2.. - // marchid Id for microarch. Mvendorid plus marchid uniquely identify the microarch. - // mimpid A unique encoding of the version of the processor implementation. - // unaligned_scalar Performance of misaligned scalar accesses (unknown, emulated, slow, fast, unsupported) - // unaligned_vector Performance of misaligned vector accesses (unknown, unspported, slow, fast) - // satp mode SATP bits (number of virtual addr bits) mbare, sv39, sv48, sv57, sv64 - public: #define RV_NO_FLAG_BIT (BitsPerWord+1) // nth_bit will return 0 on values larger than BitsPerWord @@ -239,48 +193,84 @@ class VM_Version : public Abstract_VM_Version { // // Fields description in `decl`: // declaration name, extension name, bit value from linux, feature string?, mapped flag) - #define RV_EXT_FEATURE_FLAGS(decl) \ - decl(ext_I , i , ('I' - 'A'), true , NO_UPDATE_DEFAULT) \ - decl(ext_M , m , ('M' - 'A'), true , NO_UPDATE_DEFAULT) \ - decl(ext_A , a , ('A' - 'A'), true , NO_UPDATE_DEFAULT) \ - decl(ext_F , f , ('F' - 'A'), true , NO_UPDATE_DEFAULT) \ - decl(ext_D , d , ('D' - 'A'), true , NO_UPDATE_DEFAULT) \ - decl(ext_C , c , ('C' - 'A'), true , UPDATE_DEFAULT(UseRVC)) \ - decl(ext_Q , q , ('Q' - 'A'), true , NO_UPDATE_DEFAULT) \ - decl(ext_H , h , ('H' - 'A'), true , NO_UPDATE_DEFAULT) \ - decl(ext_V , v , ('V' - 'A'), true , UPDATE_DEFAULT(UseRVV)) \ - decl(ext_Zicbom , Zicbom , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicbom)) \ - decl(ext_Zicboz , Zicboz , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicboz)) \ - decl(ext_Zicbop , Zicbop , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicbop)) \ - decl(ext_Zba , Zba , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZba)) \ - decl(ext_Zbb , Zbb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbb)) \ - decl(ext_Zbc , Zbc , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ - decl(ext_Zbs , Zbs , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbs)) \ - decl(ext_Zbkb , Zbkb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbkb)) \ - decl(ext_Zcb , Zcb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZcb)) \ - decl(ext_Zfa , Zfa , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfa)) \ - decl(ext_Zfh , Zfh , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfh)) \ - decl(ext_Zfhmin , Zfhmin , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfhmin)) \ - decl(ext_Zicsr , Zicsr , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ - decl(ext_Zicntr , Zicntr , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ - decl(ext_Zifencei , Zifencei , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ - decl(ext_Zic64b , Zic64b , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZic64b)) \ - decl(ext_Ztso , Ztso , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZtso)) \ - decl(ext_Zihintpause , Zihintpause , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZihintpause)) \ - decl(ext_Zacas , Zacas , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZacas)) \ - decl(ext_Zvbb , Zvbb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvbb, &ext_V, nullptr)) \ - decl(ext_Zvbc , Zvbc , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvbc, &ext_V, nullptr)) \ - decl(ext_Zvfh , Zvfh , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvfh, &ext_V, &ext_Zfh, nullptr)) \ - decl(ext_Zvkn , Zvkn , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvkn, &ext_V, nullptr)) \ - decl(ext_Zicond , Zicond , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicond)) \ + #define RV_EXT_FEATURE_FLAGS(decl) \ + /* A Atomic Instructions */ \ + decl(a , ('A' - 'A'), true , NO_UPDATE_DEFAULT) \ + /* C Compressed Instructions */ \ + decl(c , ('C' - 'A'), true , UPDATE_DEFAULT(UseRVC)) \ + /* D Single-Precision Floating-Point */ \ + decl(d , ('D' - 'A'), true , NO_UPDATE_DEFAULT) \ + /* F Single-Precision Floating-Point */ \ + decl(f , ('F' - 'A'), true , NO_UPDATE_DEFAULT) \ + /* H Hypervisor */ \ + decl(h , ('H' - 'A'), true , NO_UPDATE_DEFAULT) \ + /* I RV64I */ \ + decl(i , ('I' - 'A'), true , NO_UPDATE_DEFAULT) \ + /* M Integer Multiplication and Division */ \ + decl(m , ('M' - 'A'), true , NO_UPDATE_DEFAULT) \ + /* Q Quad-Precision Floating-Point */ \ + decl(q , ('Q' - 'A'), true , NO_UPDATE_DEFAULT) \ + /* V Vector */ \ + decl(v , ('V' - 'A'), true , UPDATE_DEFAULT(UseRVV)) \ + \ + /* ----------------------- Other extensions ----------------------- */ \ + \ + /* Atomic compare-and-swap (CAS) instructions */ \ + decl(Zacas , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZacas)) \ + /* Zba Address generation instructions */ \ + decl(Zba , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZba)) \ + /* Zbb Basic bit-manipulation */ \ + decl(Zbb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbb)) \ + /* Zbc Carry-less multiplication */ \ + decl(Zbc , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ + /* Bitmanip instructions for Cryptography */ \ + decl(Zbkb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbkb)) \ + /* Zbs Single-bit instructions */ \ + decl(Zbs , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbs)) \ + /* Zcb Simple code-size saving instructions */ \ + decl(Zcb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZcb)) \ + /* Additional Floating-Point instructions */ \ + decl(Zfa , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfa)) \ + /* Zfh Half-Precision Floating-Point instructions */ \ + decl(Zfh , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfh)) \ + /* Zfhmin Minimal Half-Precision Floating-Point instructions */ \ + decl(Zfhmin , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfhmin)) \ + /* Zicbom Cache Block Management Operations */ \ + decl(Zicbom , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicbom)) \ + /* Zicbop Cache Block Prefetch Operations */ \ + decl(Zicbop , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicbop)) \ + /* Zicboz Cache Block Zero Operations */ \ + decl(Zicboz , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicboz)) \ + /* Base Counters and Timers */ \ + decl(Zicntr , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ + /* Zicond Conditional operations */ \ + decl(Zicond , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicond)) \ + /* Zicsr Control and Status Register (CSR) Instructions */ \ + decl(Zicsr , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ + /* Zic64b Cache blocks must be 64 bytes in size, naturally aligned in the address space. */ \ + decl(Zic64b , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZic64b)) \ + /* Zifencei Instruction-Fetch Fence */ \ + decl(Zifencei , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \ + /* Zihintpause Pause instruction HINT */ \ + decl(Zihintpause , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZihintpause)) \ + /* Total Store Ordering */ \ + decl(Ztso , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZtso)) \ + /* Vector Basic Bit-manipulation */ \ + decl(Zvbb , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvbb, &ext_v, nullptr)) \ + /* Vector Carryless Multiplication */ \ + decl(Zvbc , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvbc, &ext_v, nullptr)) \ + /* Vector Extension for Half-Precision Floating-Point */ \ + decl(Zvfh , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvfh, &ext_v, &ext_Zfh, nullptr)) \ + /* Shorthand for Zvkned + Zvknhb + Zvkb + Zvkt */ \ + decl(Zvkn , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvkn, &ext_v, nullptr)) \ - #define DECLARE_RV_EXT_FEATURE(NAME, PRETTY, LINUX_BIT, FSTRING, FLAGF) \ - struct NAME##RVExtFeatureValue : public RVExtFeatureValue { \ - NAME##RVExtFeatureValue() : \ - RVExtFeatureValue(#PRETTY, LINUX_BIT, RVExtFeatures::CPU_##NAME, FSTRING) {} \ - FLAGF; \ - }; \ - static NAME##RVExtFeatureValue NAME; \ + #define DECLARE_RV_EXT_FEATURE(PRETTY, LINUX_BIT, FSTRING, FLAGF) \ + struct ext_##PRETTY##RVExtFeatureValue : public RVExtFeatureValue { \ + ext_##PRETTY##RVExtFeatureValue() : \ + RVExtFeatureValue(#PRETTY, LINUX_BIT, RVExtFeatures::CPU_##ext_##PRETTY, FSTRING) {} \ + FLAGF; \ + }; \ + static ext_##PRETTY##RVExtFeatureValue ext_##PRETTY; \ RV_EXT_FEATURE_FLAGS(DECLARE_RV_EXT_FEATURE) #undef DECLARE_RV_EXT_FEATURE @@ -288,21 +278,27 @@ class VM_Version : public Abstract_VM_Version { // Non-extension features // #define RV_NON_EXT_FEATURE_FLAGS(decl) \ - decl(mvendorid , VendorId , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ - decl(marchid , ArchId , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ - decl(mimpid , ImpId , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ - decl(satp_mode , SATP , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ - decl(unaligned_scalar , UnalignedScalar , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ - decl(unaligned_vector , UnalignedVector , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ - decl(zicboz_block_size, ZicbozBlockSize , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ + /* Id for microarch. Mvendorid plus marchid uniquely identify the microarch. */ \ + decl(marchid , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ + /* A unique encoding of the version of the processor implementation. */ \ + decl(mimpid , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ + /* SATP bits (number of virtual addr bits) mbare, sv39, sv48, sv57, sv64 */ \ + decl(satp_mode , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ + /* Performance of misaligned scalar accesses (unknown, emulated, slow, fast, unsupported) */ \ + decl(unaligned_scalar , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ + /* Performance of misaligned vector accesses (unknown, unspported, slow, fast) */ \ + decl(unaligned_vector , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ + /* Manufactory JEDEC id encoded, ISA vol 2 3.1.2.. */ \ + decl(mvendorid , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ + decl(zicboz_block_size , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ - #define DECLARE_RV_NON_EXT_FEATURE(NAME, PRETTY, LINUX_BIT, FSTRING, FLAGF) \ - struct NAME##RVNonExtFeatureValue : public RVNonExtFeatureValue { \ - NAME##RVNonExtFeatureValue() : \ + #define DECLARE_RV_NON_EXT_FEATURE(PRETTY, LINUX_BIT, FSTRING, FLAGF) \ + struct PRETTY##RVNonExtFeatureValue : public RVNonExtFeatureValue { \ + PRETTY##RVNonExtFeatureValue() : \ RVNonExtFeatureValue(#PRETTY, LINUX_BIT, FSTRING) {} \ FLAGF; \ }; \ - static NAME##RVNonExtFeatureValue NAME; \ + static PRETTY##RVNonExtFeatureValue PRETTY; \ RV_NON_EXT_FEATURE_FLAGS(DECLARE_RV_NON_EXT_FEATURE) #undef DECLARE_RV_NON_EXT_FEATURE @@ -312,7 +308,7 @@ private: class RVExtFeatures : public CHeapObj { public: enum RVFeatureIndex { - #define DECLARE_RV_FEATURE_ENUM(NAME, PRETTY, LINUX_BIT, FSTRING, FLAGF) CPU_##NAME, + #define DECLARE_RV_FEATURE_ENUM(PRETTY, LINUX_BIT, FSTRING, FLAGF) CPU_##ext_##PRETTY, RV_EXT_FEATURE_FLAGS(DECLARE_RV_FEATURE_ENUM) MAX_CPU_FEATURE_INDEX diff --git a/src/hotspot/cpu/s390/c1_FrameMap_s390.cpp b/src/hotspot/cpu/s390/c1_FrameMap_s390.cpp index ddba445154a..e219e9bbb40 100644 --- a/src/hotspot/cpu/s390/c1_FrameMap_s390.cpp +++ b/src/hotspot/cpu/s390/c1_FrameMap_s390.cpp @@ -282,13 +282,6 @@ LIR_Opr FrameMap::stack_pointer() { return Z_SP_opr; } -// JSR 292 -// On ZARCH_64, there is no need to save the SP, because neither -// method handle intrinsics nor compiled lambda forms modify it. -LIR_Opr FrameMap::method_handle_invoke_SP_save_opr() { - return LIR_OprFact::illegalOpr; -} - bool FrameMap::validate_frame() { return true; } diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index e9377733d2d..cfc8b19534b 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -1980,11 +1980,6 @@ RegMask Matcher::modL_proj_mask() { return _Z_RARG3_LONG_REG_mask; } -// Copied from sparc. -const RegMask Matcher::method_handle_invoke_SP_save_mask() { - return RegMask(); -} - // Should the matcher clone input 'm' of node 'n'? bool Matcher::pd_clone_node(Node* n, Node* m, Matcher::MStack& mstack) { if (is_encode_and_store_pattern(n, m)) { diff --git a/src/hotspot/cpu/x86/c1_FrameMap_x86.cpp b/src/hotspot/cpu/x86/c1_FrameMap_x86.cpp index bdbab432180..68c9814fd20 100644 --- a/src/hotspot/cpu/x86/c1_FrameMap_x86.cpp +++ b/src/hotspot/cpu/x86/c1_FrameMap_x86.cpp @@ -326,13 +326,6 @@ LIR_Opr FrameMap::stack_pointer() { return FrameMap::rsp_opr; } -// JSR 292 -// On x86, there is no need to save the SP, because neither -// method handle intrinsics, nor compiled lambda forms modify it. -LIR_Opr FrameMap::method_handle_invoke_SP_save_opr() { - return LIR_OprFact::illegalOpr; -} - bool FrameMap::validate_frame() { return true; } diff --git a/src/hotspot/cpu/x86/frame_x86.cpp b/src/hotspot/cpu/x86/frame_x86.cpp index 46ffda93699..5f52f2fabf2 100644 --- a/src/hotspot/cpu/x86/frame_x86.cpp +++ b/src/hotspot/cpu/x86/frame_x86.cpp @@ -219,8 +219,7 @@ bool frame::safe_for_sender(JavaThread *thread) { nmethod* nm = sender_blob->as_nmethod_or_null(); if (nm != nullptr) { - if (nm->is_deopt_mh_entry(sender_pc) || nm->is_deopt_entry(sender_pc) || - nm->method()->is_method_handle_intrinsic()) { + if (nm->is_deopt_entry(sender_pc) || nm->method()->is_method_handle_intrinsic()) { return false; } } @@ -443,47 +442,6 @@ JavaThread** frame::saved_thread_address(const frame& f) { return thread_addr; } -//------------------------------------------------------------------------------ -// frame::verify_deopt_original_pc -// -// Verifies the calculated original PC of a deoptimization PC for the -// given unextended SP. -#ifdef ASSERT -void frame::verify_deopt_original_pc(nmethod* nm, intptr_t* unextended_sp) { - frame fr; - - // This is ugly but it's better than to change {get,set}_original_pc - // to take an SP value as argument. And it's only a debugging - // method anyway. - fr._unextended_sp = unextended_sp; - - address original_pc = nm->get_original_pc(&fr); - assert(nm->insts_contains_inclusive(original_pc), - "original PC must be in the main code section of the compiled method (or must be immediately following it) original_pc: " INTPTR_FORMAT " unextended_sp: " INTPTR_FORMAT " name: %s", p2i(original_pc), p2i(unextended_sp), nm->name()); -} -#endif - -//------------------------------------------------------------------------------ -// frame::adjust_unextended_sp -#ifdef ASSERT -void frame::adjust_unextended_sp() { - // On x86, sites calling method handle intrinsics and lambda forms are treated - // as any other call site. Therefore, no special action is needed when we are - // returning to any of these call sites. - - if (_cb != nullptr) { - nmethod* sender_nm = _cb->as_nmethod_or_null(); - if (sender_nm != nullptr) { - // If the sender PC is a deoptimization point, get the original PC. - if (sender_nm->is_deopt_entry(_pc) || - sender_nm->is_deopt_mh_entry(_pc)) { - verify_deopt_original_pc(sender_nm, _unextended_sp); - } - } - } -} -#endif - //------------------------------------------------------------------------------ // frame::sender_for_interpreter_frame frame frame::sender_for_interpreter_frame(RegisterMap* map) const { diff --git a/src/hotspot/cpu/x86/frame_x86.hpp b/src/hotspot/cpu/x86/frame_x86.hpp index f3034ee9263..19f37c42cf4 100644 --- a/src/hotspot/cpu/x86/frame_x86.hpp +++ b/src/hotspot/cpu/x86/frame_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -138,17 +138,10 @@ int _offset_unextended_sp; // for use in stack-chunk frames }; - void adjust_unextended_sp() NOT_DEBUG_RETURN; - intptr_t* ptr_at_addr(int offset) const { return (intptr_t*) addr_at(offset); } -#ifdef ASSERT - // Used in frame::sender_for_{interpreter,compiled}_frame - static void verify_deopt_original_pc(nmethod* nm, intptr_t* unextended_sp); -#endif - public: // Constructors diff --git a/src/hotspot/cpu/x86/frame_x86.inline.hpp b/src/hotspot/cpu/x86/frame_x86.inline.hpp index afc4ab8767b..ca51fe66786 100644 --- a/src/hotspot/cpu/x86/frame_x86.inline.hpp +++ b/src/hotspot/cpu/x86/frame_x86.inline.hpp @@ -111,8 +111,6 @@ inline void frame::init(intptr_t* sp, intptr_t* fp, address pc) { } inline void frame::setup(address pc) { - adjust_unextended_sp(); - address original_pc = get_deopt_original_pc(); if (original_pc != nullptr) { _pc = original_pc; @@ -209,7 +207,6 @@ inline frame::frame(intptr_t* sp, intptr_t* fp) { // assert(_pc != nullptr, "no pc?"); _cb = CodeCache::find_blob(_pc); - adjust_unextended_sp(); address original_pc = get_deopt_original_pc(); if (original_pc != nullptr) { diff --git a/src/hotspot/cpu/x86/globalDefinitions_x86.hpp b/src/hotspot/cpu/x86/globalDefinitions_x86.hpp index 3c1474ae861..abbeb66a1ca 100644 --- a/src/hotspot/cpu/x86/globalDefinitions_x86.hpp +++ b/src/hotspot/cpu/x86/globalDefinitions_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,8 +34,6 @@ const bool CCallingConventionRequiresIntsAsLongs = false; #define SUPPORTS_NATIVE_CX8 -#define SUPPORT_MONITOR_COUNT - #define CPU_MULTI_COPY_ATOMIC // The expected size in bytes of a cache line. diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index c1319b2ef7f..4f19b30b832 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -2431,14 +2431,6 @@ void MacroAssembler::pop_cont_fastpath() { bind(L_done); } -void MacroAssembler::inc_held_monitor_count() { - incrementq(Address(r15_thread, JavaThread::held_monitor_count_offset())); -} - -void MacroAssembler::dec_held_monitor_count() { - decrementq(Address(r15_thread, JavaThread::held_monitor_count_offset())); -} - #ifdef ASSERT void MacroAssembler::stop_if_in_cont(Register cont, const char* name) { Label no_cont; @@ -5847,7 +5839,7 @@ void MacroAssembler::generate_fill(BasicType t, bool aligned, orl(value, rtmp); } - cmpptr(count, 2<sleep(FT_SLEEP_MILLISECS); - - end = os::elapsed_counter(); - OrderAccess::fence(); - fend = os::rdtsc(); - - time_base += end - start; - time_fast += fend - fstart; - - // basis for calculating the os tick start - // to fast time tick start offset - time_base_elapsed += end; - time_fast_elapsed += (fend - _epoch); - } - - time_base /= loopcount; - time_fast /= loopcount; - time_base_elapsed /= loopcount; - time_fast_elapsed /= loopcount; -} - -static jlong initialize_frequency() { - assert(0 == tsc_frequency, "invariant"); +jlong Rdtsc::initialize_frequency() { + assert(0 == _tsc_frequency, "invariant"); assert(0 == _epoch, "invariant"); const jlong initial_counter = set_epoch(); if (initial_counter == 0) { @@ -102,29 +59,6 @@ static jlong initialize_frequency() { // for invariant tsc platforms, take the maximum qualified cpu frequency tsc_freq = (double)VM_Version::maximum_qualified_cpu_frequency(); os_to_tsc_conv_factor = tsc_freq / os_freq; - } else { - // use measurements to estimate - // a conversion factor and the tsc frequency - - volatile jlong time_base = 0; - volatile jlong time_fast = 0; - volatile jlong time_base_elapsed = 0; - volatile jlong time_fast_elapsed = 0; - - // do measurements to get base data - // on os timer and fast ticks tsc time relation. - do_time_measurements(time_base, time_fast, time_base_elapsed, time_fast_elapsed); - - // if invalid measurements, cannot proceed - if (time_fast == 0 || time_base == 0) { - return 0; - } - - os_to_tsc_conv_factor = (double)time_fast / (double)time_base; - if (os_to_tsc_conv_factor > 1) { - // estimate on tsc counter frequency - tsc_freq = os_to_tsc_conv_factor * os_freq; - } } if ((tsc_freq < 0) || (tsc_freq > 0 && tsc_freq <= os_freq) || (os_to_tsc_conv_factor <= 1)) { @@ -136,47 +70,52 @@ static jlong initialize_frequency() { return (jlong)tsc_freq; } -static bool initialize_elapsed_counter() { - tsc_frequency = initialize_frequency(); - return tsc_frequency != 0 && _epoch != 0; +bool Rdtsc::initialize_elapsed_counter() { + _tsc_frequency = initialize_frequency(); + return _tsc_frequency != 0 && _epoch != 0; } static bool ergonomics() { - const bool invtsc_support = Rdtsc::is_supported(); - if (FLAG_IS_DEFAULT(UseFastUnorderedTimeStamps) && invtsc_support) { - FLAG_SET_ERGO(UseFastUnorderedTimeStamps, true); - } + if (Rdtsc::is_supported()) { + // Use rdtsc when it is supported by default + FLAG_SET_ERGO_IF_DEFAULT(UseFastUnorderedTimeStamps, true); + } else if (UseFastUnorderedTimeStamps) { + assert(!FLAG_IS_DEFAULT(UseFastUnorderedTimeStamps), "Unexpected default value"); - bool ft_enabled = UseFastUnorderedTimeStamps && invtsc_support; - - if (!ft_enabled) { - if (UseFastUnorderedTimeStamps && VM_Version::supports_tsc()) { - warning("\nThe hardware does not support invariant tsc (INVTSC) register and/or cannot guarantee tsc synchronization between sockets at startup.\n"\ - "Values returned via rdtsc() are not guaranteed to be accurate, esp. when comparing values from cross sockets reads. Enabling UseFastUnorderedTimeStamps on non-invariant tsc hardware should be considered experimental.\n"); - ft_enabled = true; - } - } - - if (!ft_enabled) { - // Warn if unable to support command-line flag - if (UseFastUnorderedTimeStamps && !VM_Version::supports_tsc()) { + if (VM_Version::supports_tsc()) { + warning("Ignoring UseFastUnorderedTimeStamps, the hardware does not support invariant tsc (INVTSC) register and/or cannot guarantee tsc synchronization between sockets at startup.\n" + "Values returned via rdtsc() are not guaranteed to be accurate, esp. when comparing values from cross sockets reads."); + } else { warning("Ignoring UseFastUnorderedTimeStamps, hardware does not support normal tsc"); } + + // We do not support non invariant rdtsc + FLAG_SET_ERGO(UseFastUnorderedTimeStamps, false); } - return ft_enabled; + return UseFastUnorderedTimeStamps; +} + +bool Rdtsc::initialize() { + precond(AtomicAccess::xchg(&_initialized, 1) == 0); + assert(0 == _tsc_frequency, "invariant"); + assert(0 == _epoch, "invariant"); + + if (!ergonomics()) { + // We decided to ergonomically not support rdtsc. + return false; + } + + // Try to initialize the elapsed counter + return initialize_elapsed_counter(); } bool Rdtsc::is_supported() { return VM_Version::supports_tscinv_ext(); } -bool Rdtsc::is_elapsed_counter_enabled() { - return rdtsc_elapsed_counter_enabled; -} - jlong Rdtsc::frequency() { - return tsc_frequency; + return _tsc_frequency; } jlong Rdtsc::elapsed_counter() { @@ -191,19 +130,7 @@ jlong Rdtsc::raw() { return os::rdtsc(); } -bool Rdtsc::initialize() { - static bool initialized = false; - if (!initialized) { - assert(!rdtsc_elapsed_counter_enabled, "invariant"); - VM_Version::initialize_tsc(); - assert(0 == tsc_frequency, "invariant"); - assert(0 == _epoch, "invariant"); - bool result = initialize_elapsed_counter(); // init hw - if (result) { - result = ergonomics(); // check logical state - } - rdtsc_elapsed_counter_enabled = result; - initialized = true; - } - return rdtsc_elapsed_counter_enabled; +bool Rdtsc::enabled() { + static bool enabled = initialize(); + return enabled; } diff --git a/src/hotspot/cpu/x86/rdtsc_x86.hpp b/src/hotspot/cpu/x86/rdtsc_x86.hpp index d9e77a0ae6b..a6519c439d3 100644 --- a/src/hotspot/cpu/x86/rdtsc_x86.hpp +++ b/src/hotspot/cpu/x86/rdtsc_x86.hpp @@ -38,14 +38,24 @@ // INVTSC is a minimal requirement for auto-enablement. class Rdtsc : AllStatic { + private: + DEBUG_ONLY(static volatile int _initialized;) + static jlong _epoch; + static jlong _tsc_frequency; + + static jlong set_epoch(); + + static jlong initialize_frequency(); + static bool initialize_elapsed_counter(); + static bool initialize(); + public: static jlong elapsed_counter(); // provides quick time stamps static jlong frequency(); // tsc register static bool is_supported(); // InvariantTSC static jlong raw(); // direct rdtsc() access - static bool is_elapsed_counter_enabled(); // turn off with -XX:-UseFastUnorderedTimeStamps static jlong epoch(); - static bool initialize(); + static bool enabled(); }; #endif // CPU_X86_RDTSC_X86_HPP diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp index e2a8f36b050..e702b587edd 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp @@ -1352,11 +1352,8 @@ static void fill_continuation_entry(MacroAssembler* masm, Register reg_cont_obj, __ movptr(rax, Address(r15_thread, JavaThread::cont_fastpath_offset())); __ movptr(Address(rsp, ContinuationEntry::parent_cont_fastpath_offset()), rax); - __ movq(rax, Address(r15_thread, JavaThread::held_monitor_count_offset())); - __ movq(Address(rsp, ContinuationEntry::parent_held_monitor_count_offset()), rax); __ movptr(Address(r15_thread, JavaThread::cont_fastpath_offset()), 0); - __ movq(Address(r15_thread, JavaThread::held_monitor_count_offset()), 0); } //---------------------------- continuation_enter_cleanup --------------------------- @@ -1380,49 +1377,6 @@ static void continuation_enter_cleanup(MacroAssembler* masm) { #endif __ movptr(rbx, Address(rsp, ContinuationEntry::parent_cont_fastpath_offset())); __ movptr(Address(r15_thread, JavaThread::cont_fastpath_offset()), rbx); - - if (CheckJNICalls) { - // Check if this is a virtual thread continuation - Label L_skip_vthread_code; - __ cmpl(Address(rsp, ContinuationEntry::flags_offset()), 0); - __ jcc(Assembler::equal, L_skip_vthread_code); - - // If the held monitor count is > 0 and this vthread is terminating then - // it failed to release a JNI monitor. So we issue the same log message - // that JavaThread::exit does. - __ cmpptr(Address(r15_thread, JavaThread::jni_monitor_count_offset()), 0); - __ jcc(Assembler::equal, L_skip_vthread_code); - - // rax may hold an exception oop, save it before the call - __ push(rax); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::log_jni_monitor_still_held)); - __ pop(rax); - - // For vthreads we have to explicitly zero the JNI monitor count of the carrier - // on termination. The held count is implicitly zeroed below when we restore from - // the parent held count (which has to be zero). - __ movq(Address(r15_thread, JavaThread::jni_monitor_count_offset()), 0); - - __ bind(L_skip_vthread_code); - } -#ifdef ASSERT - else { - // Check if this is a virtual thread continuation - Label L_skip_vthread_code; - __ cmpl(Address(rsp, ContinuationEntry::flags_offset()), 0); - __ jcc(Assembler::equal, L_skip_vthread_code); - - // See comment just above. If not checking JNI calls the JNI count is only - // needed for assertion checking. - __ movq(Address(r15_thread, JavaThread::jni_monitor_count_offset()), 0); - - __ bind(L_skip_vthread_code); - } -#endif - - __ movq(rbx, Address(rsp, ContinuationEntry::parent_held_monitor_count_offset())); - __ movq(Address(r15_thread, JavaThread::held_monitor_count_offset()), rbx); - __ movptr(rbx, Address(rsp, ContinuationEntry::parent_offset())); __ movptr(Address(r15_thread, JavaThread::cont_entry_offset()), rbx); __ addptr(rsp, checked_cast(ContinuationEntry::size())); diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 83daf932a27..213e988a581 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -62,7 +62,7 @@ address VM_Version::_cpuinfo_segv_addr_apx = nullptr; address VM_Version::_cpuinfo_cont_addr_apx = nullptr; static BufferBlob* stub_blob; -static const int stub_size = 2000; +static const int stub_size = 2550; int VM_Version::VM_Features::_features_bitmap_size = sizeof(VM_Version::VM_Features::_features_bitmap) / BytesPerLong; @@ -73,10 +73,12 @@ extern "C" { typedef void (*get_cpu_info_stub_t)(void*); typedef void (*detect_virt_stub_t)(uint32_t, uint32_t*); typedef void (*clear_apx_test_state_t)(void); + typedef void (*getCPUIDBrandString_stub_t)(void*); } static get_cpu_info_stub_t get_cpu_info_stub = nullptr; static detect_virt_stub_t detect_virt_stub = nullptr; static clear_apx_test_state_t clear_apx_test_state_stub = nullptr; +static getCPUIDBrandString_stub_t getCPUIDBrandString_stub = nullptr; bool VM_Version::supports_clflush() { // clflush should always be available on x86_64 @@ -2131,6 +2133,8 @@ void VM_Version::initialize() { g.generate_detect_virt()); clear_apx_test_state_stub = CAST_TO_FN_PTR(clear_apx_test_state_t, g.clear_apx_test_state()); + getCPUIDBrandString_stub = CAST_TO_FN_PTR(getCPUIDBrandString_stub_t, + g.generate_getCPUIDBrandString()); get_processor_features(); Assembler::precompute_instructions(); @@ -2187,15 +2191,6 @@ typedef enum { TM_FLAG = 0x20000000 } FeatureEdxFlag; -static BufferBlob* cpuid_brand_string_stub_blob; -static const int cpuid_brand_string_stub_size = 550; - -extern "C" { - typedef void (*getCPUIDBrandString_stub_t)(void*); -} - -static getCPUIDBrandString_stub_t getCPUIDBrandString_stub = nullptr; - // VM_Version statics enum { ExtendedFamilyIdLength_INTEL = 16, @@ -2488,19 +2483,6 @@ const char* const _feature_extended_ecx_id[] = { "" }; -void VM_Version::initialize_tsc(void) { - ResourceMark rm; - - cpuid_brand_string_stub_blob = BufferBlob::create("getCPUIDBrandString_stub", cpuid_brand_string_stub_size); - if (cpuid_brand_string_stub_blob == nullptr) { - vm_exit_during_initialization("Unable to allocate getCPUIDBrandString_stub"); - } - CodeBuffer c(cpuid_brand_string_stub_blob); - VM_Version_StubGenerator g(&c); - getCPUIDBrandString_stub = CAST_TO_FN_PTR(getCPUIDBrandString_stub_t, - g.generate_getCPUIDBrandString()); -} - const char* VM_Version::cpu_model_description(void) { uint32_t cpu_family = extended_cpu_family(); uint32_t cpu_model = extended_cpu_model(); @@ -2589,7 +2571,12 @@ void VM_Version::resolve_cpu_information_details(void) { _no_of_threads = os::processor_count(); // find out number of threads per cpu package - int threads_per_package = threads_per_core() * cores_per_cpu(); + int threads_per_package = _cpuid_info.tpl_cpuidB1_ebx.bits.logical_cpus; + if (threads_per_package == 0) { + // Fallback code to avoid div by zero in subsequent code. + // CPUID 0Bh (ECX = 1) might return 0 on older AMD processor (EPYC 7763 at least) + threads_per_package = threads_per_core() * cores_per_cpu(); + } // use amount of threads visible to the process in order to guess number of sockets _no_of_sockets = _no_of_threads / threads_per_package; @@ -2743,6 +2730,10 @@ size_t VM_Version::cpu_write_support_string(char* const buf, size_t buf_len) { WRITE_TO_BUF("Invariant TSC"); } + if (supports_hybrid()) { + WRITE_TO_BUF("Hybrid Architecture"); + } + return written; } diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp index eaa9412fe85..aa9a527e0b7 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.hpp +++ b/src/hotspot/cpu/x86/vm_version_x86.hpp @@ -1093,7 +1093,6 @@ public: static bool supports_tscinv_ext(void); - static void initialize_tsc(); static void initialize_cpu_information(void); }; diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index 0b254966db6..b40f9e2924a 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -1697,11 +1697,6 @@ RegMask Matcher::modL_proj_mask() { return LONG_RDX_REG_mask(); } -// Register for saving SP into on method handle invokes. Not used on x86_64. -const RegMask Matcher::method_handle_invoke_SP_save_mask() { - return NO_REG_mask(); -} - %} //----------ENCODING BLOCK----------------------------------------------------- diff --git a/src/hotspot/os/posix/safefetch_sigjmp.cpp b/src/hotspot/os/posix/safefetch_sigjmp.cpp index 9f6ef34070b..572805acb7b 100644 --- a/src/hotspot/os/posix/safefetch_sigjmp.cpp +++ b/src/hotspot/os/posix/safefetch_sigjmp.cpp @@ -67,6 +67,14 @@ ATTRIBUTE_NO_ASAN static bool _SafeFetchXX_internal(const T *adr, T* result) { T n = 0; +#ifdef AIX + // AIX allows reading from nullptr without signalling + if (adr == nullptr) { + *result = 0; + return false; + } +#endif + // Set up a jump buffer. Anchor its pointer in TLS. Then read from the unsafe address. // If that address was invalid, we fault, and in the signal handler we will jump back // to the jump point. diff --git a/src/hotspot/os/posix/signals_posix.cpp b/src/hotspot/os/posix/signals_posix.cpp index 886bd453415..714eac12d22 100644 --- a/src/hotspot/os/posix/signals_posix.cpp +++ b/src/hotspot/os/posix/signals_posix.cpp @@ -621,9 +621,7 @@ int JVM_HANDLE_XXX_SIGNAL(int sig, siginfo_t* info, if (cb != nullptr && cb->is_nmethod()) { nmethod* nm = cb->as_nmethod(); assert(nm->insts_contains_inclusive(pc), ""); - address deopt = nm->is_method_handle_return(pc) ? - nm->deopt_mh_handler_begin() : - nm->deopt_handler_begin(); + address deopt = nm->deopt_handler_begin(); assert(deopt != nullptr, ""); frame fr = os::fetch_frame_from_context(uc); diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 875e97ce038..ba05d390c9f 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -2630,14 +2630,13 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) { DWORD exception_code = exception_record->ExceptionCode; #if defined(_M_ARM64) address pc = (address) exceptionInfo->ContextRecord->Pc; + + if (handle_safefetch(exception_code, pc, (void*)exceptionInfo->ContextRecord)) { + return EXCEPTION_CONTINUE_EXECUTION; + } #elif defined(_M_AMD64) address pc = (address) exceptionInfo->ContextRecord->Rip; -#else - #error unknown architecture -#endif - Thread* t = Thread::current_or_null_safe(); -#if defined(_M_AMD64) if ((exception_code == EXCEPTION_ACCESS_VIOLATION) && VM_Version::is_cpuinfo_segv_addr(pc)) { // Verify that OS save/restore AVX registers. @@ -2650,6 +2649,8 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) { VM_Version::clear_apx_test_state(); return Handle_Exception(exceptionInfo, VM_Version::cpuinfo_cont_addr_apx()); } +#else + #error unknown architecture #endif #ifdef CAN_SHOW_REGISTERS_ON_ASSERT @@ -2660,6 +2661,7 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) { } #endif + Thread* t = Thread::current_or_null_safe(); if (t != nullptr && t->is_Java_thread()) { JavaThread* thread = JavaThread::cast(t); bool in_java = thread->thread_state() == _thread_in_Java; @@ -2690,10 +2692,8 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) { // Fatal red zone violation. overflow_state->disable_stack_red_zone(); tty->print_raw_cr("An unrecoverable stack overflow has occurred."); -#if !defined(USE_VECTORED_EXCEPTION_HANDLING) report_error(t, exception_code, pc, exception_record, exceptionInfo->ContextRecord); -#endif return EXCEPTION_CONTINUE_SEARCH; } } else if (exception_code == EXCEPTION_ACCESS_VIOLATION) { @@ -2745,10 +2745,8 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) { } // Stack overflow or null pointer exception in native code. -#if !defined(USE_VECTORED_EXCEPTION_HANDLING) report_error(t, exception_code, pc, exception_record, exceptionInfo->ContextRecord); -#endif return EXCEPTION_CONTINUE_SEARCH; } // /EXCEPTION_ACCESS_VIOLATION // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2797,9 +2795,7 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) { if (cb != nullptr && cb->is_nmethod()) { nmethod* nm = cb->as_nmethod(); frame fr = os::fetch_frame_from_context((void*)exceptionInfo->ContextRecord); - address deopt = nm->is_method_handle_return(pc) ? - nm->deopt_mh_handler_begin() : - nm->deopt_handler_begin(); + address deopt = nm->deopt_handler_begin(); assert(nm->insts_contains_inclusive(pc), ""); nm->set_original_pc(&fr, pc); // Set pc to handler @@ -2810,41 +2806,21 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) { } } -#if !defined(USE_VECTORED_EXCEPTION_HANDLING) - if (exception_code != EXCEPTION_BREAKPOINT) { + bool should_report_error = (exception_code != EXCEPTION_BREAKPOINT); + +#if defined(_M_ARM64) + should_report_error = should_report_error && + FAILED(exception_code) && + (exception_code != EXCEPTION_UNCAUGHT_CXX_EXCEPTION); +#endif + + if (should_report_error) { report_error(t, exception_code, pc, exception_record, exceptionInfo->ContextRecord); } -#endif - return EXCEPTION_CONTINUE_SEARCH; -} - -#if defined(USE_VECTORED_EXCEPTION_HANDLING) -LONG WINAPI topLevelVectoredExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) { - PEXCEPTION_RECORD exceptionRecord = exceptionInfo->ExceptionRecord; -#if defined(_M_ARM64) - address pc = (address) exceptionInfo->ContextRecord->Pc; -#elif defined(_M_AMD64) - address pc = (address) exceptionInfo->ContextRecord->Rip; -#else - #error unknown architecture -#endif - - // Fast path for code part of the code cache - if (CodeCache::low_bound() <= pc && pc < CodeCache::high_bound()) { - return topLevelExceptionFilter(exceptionInfo); - } - - // If the exception occurred in the codeCache, pass control - // to our normal exception handler. - CodeBlob* cb = CodeCache::find_blob(pc); - if (cb != nullptr) { - return topLevelExceptionFilter(exceptionInfo); - } return EXCEPTION_CONTINUE_SEARCH; } -#endif #if defined(USE_VECTORED_EXCEPTION_HANDLING) LONG WINAPI topLevelUnhandledExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) { @@ -4521,7 +4497,7 @@ jint os::init_2(void) { // Setup Windows Exceptions #if defined(USE_VECTORED_EXCEPTION_HANDLING) - topLevelVectoredExceptionHandler = AddVectoredExceptionHandler(1, topLevelVectoredExceptionFilter); + topLevelVectoredExceptionHandler = AddVectoredExceptionHandler(1, topLevelExceptionFilter); previousUnhandledExceptionFilter = SetUnhandledExceptionFilter(topLevelUnhandledExceptionFilter); #endif diff --git a/src/hotspot/os/windows/os_windows.hpp b/src/hotspot/os/windows/os_windows.hpp index efb7b414989..f1153bbbfd3 100644 --- a/src/hotspot/os/windows/os_windows.hpp +++ b/src/hotspot/os/windows/os_windows.hpp @@ -150,6 +150,8 @@ public: // signal support static void* install_signal_handler(int sig, signal_handler_t handler); static void* user_handler(); + + static void context_set_pc(CONTEXT* uc, address pc); }; #endif // OS_WINDOWS_OS_WINDOWS_HPP diff --git a/src/hotspot/os/windows/safefetch_static_windows.cpp b/src/hotspot/os/windows/safefetch_static_windows.cpp new file mode 100644 index 00000000000..3ea8b96b32d --- /dev/null +++ b/src/hotspot/os/windows/safefetch_static_windows.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022 SAP SE. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + + +#include "os_windows.hpp" +#include "runtime/os.hpp" +#include "runtime/safefetch.hpp" +#include "utilities/globalDefinitions.hpp" + +#ifdef SAFEFETCH_METHOD_STATIC_ASSEMBLY + +// SafeFetch handling, static assembly style: +// +// SafeFetch32 and SafeFetchN are implemented via static assembly +// and live in os_cpu/xx_xx/safefetch_xx_xx.S + +extern "C" char _SafeFetch32_continuation[]; +extern "C" char _SafeFetch32_fault[]; + +#ifdef _LP64 +extern "C" char _SafeFetchN_continuation[]; +extern "C" char _SafeFetchN_fault[]; +#endif // _LP64 + +bool handle_safefetch(int exception_code, address pc, void* context) { + CONTEXT* ctx = (CONTEXT*)context; + if (exception_code == EXCEPTION_ACCESS_VIOLATION && ctx != nullptr) { + if (pc == (address)_SafeFetch32_fault) { + os::win32::context_set_pc(ctx, (address)_SafeFetch32_continuation); + return true; + } +#ifdef _LP64 + if (pc == (address)_SafeFetchN_fault) { + os::win32::context_set_pc(ctx, (address)_SafeFetchN_continuation); + return true; + } +#endif + } + return false; +} + +#endif // SAFEFETCH_METHOD_STATIC_ASSEMBLY diff --git a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp index a95bfb4ff96..017d8a43666 100644 --- a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp +++ b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp @@ -177,16 +177,16 @@ void RiscvHwprobe::add_features_from_query_result() { VM_Version::mimpid.enable_feature(query[RISCV_HWPROBE_KEY_MIMPID].value); } if (is_set(RISCV_HWPROBE_KEY_BASE_BEHAVIOR, RISCV_HWPROBE_BASE_BEHAVIOR_IMA)) { - VM_Version::ext_I.enable_feature(); - VM_Version::ext_M.enable_feature(); - VM_Version::ext_A.enable_feature(); + VM_Version::ext_i.enable_feature(); + VM_Version::ext_m.enable_feature(); + VM_Version::ext_a.enable_feature(); } if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_IMA_FD)) { - VM_Version::ext_F.enable_feature(); - VM_Version::ext_D.enable_feature(); + VM_Version::ext_f.enable_feature(); + VM_Version::ext_d.enable_feature(); } if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_IMA_C)) { - VM_Version::ext_C.enable_feature(); + VM_Version::ext_c.enable_feature(); } if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_IMA_V)) { // Linux signal return bug when using vector with vlen > 128b in pre 6.8.5. @@ -199,7 +199,7 @@ void RiscvHwprobe::add_features_from_query_result() { log.info("Vector not enabled automatically via hwprobe, but can be turned on with -XX:+UseRVV."); } } else { - VM_Version::ext_V.enable_feature(); + VM_Version::ext_v.enable_feature(); } } if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZBA)) { diff --git a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp index 67d405f0656..e414a3889c2 100644 --- a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp +++ b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp @@ -105,15 +105,15 @@ uint32_t VM_Version::cpu_vector_length() { void VM_Version::setup_cpu_available_features() { - assert(ext_I.feature_bit() == HWCAP_ISA_I, "Bit for I must follow Linux HWCAP"); - assert(ext_M.feature_bit() == HWCAP_ISA_M, "Bit for M must follow Linux HWCAP"); - assert(ext_A.feature_bit() == HWCAP_ISA_A, "Bit for A must follow Linux HWCAP"); - assert(ext_F.feature_bit() == HWCAP_ISA_F, "Bit for F must follow Linux HWCAP"); - assert(ext_D.feature_bit() == HWCAP_ISA_D, "Bit for D must follow Linux HWCAP"); - assert(ext_C.feature_bit() == HWCAP_ISA_C, "Bit for C must follow Linux HWCAP"); - assert(ext_Q.feature_bit() == HWCAP_ISA_Q, "Bit for Q must follow Linux HWCAP"); - assert(ext_H.feature_bit() == HWCAP_ISA_H, "Bit for H must follow Linux HWCAP"); - assert(ext_V.feature_bit() == HWCAP_ISA_V, "Bit for V must follow Linux HWCAP"); + assert(ext_i.feature_bit() == HWCAP_ISA_I, "Bit for I must follow Linux HWCAP"); + assert(ext_m.feature_bit() == HWCAP_ISA_M, "Bit for M must follow Linux HWCAP"); + assert(ext_a.feature_bit() == HWCAP_ISA_A, "Bit for A must follow Linux HWCAP"); + assert(ext_f.feature_bit() == HWCAP_ISA_F, "Bit for F must follow Linux HWCAP"); + assert(ext_d.feature_bit() == HWCAP_ISA_D, "Bit for D must follow Linux HWCAP"); + assert(ext_c.feature_bit() == HWCAP_ISA_C, "Bit for C must follow Linux HWCAP"); + assert(ext_q.feature_bit() == HWCAP_ISA_Q, "Bit for Q must follow Linux HWCAP"); + assert(ext_h.feature_bit() == HWCAP_ISA_H, "Bit for H must follow Linux HWCAP"); + assert(ext_v.feature_bit() == HWCAP_ISA_V, "Bit for V must follow Linux HWCAP"); if (!RiscvHwprobe::probe_features()) { os_aux_features(); diff --git a/src/hotspot/os_cpu/windows_aarch64/os_windows_aarch64.cpp b/src/hotspot/os_cpu/windows_aarch64/os_windows_aarch64.cpp index 01105e6d51e..d99e0167cbd 100644 --- a/src/hotspot/os_cpu/windows_aarch64/os_windows_aarch64.cpp +++ b/src/hotspot/os_cpu/windows_aarch64/os_windows_aarch64.cpp @@ -115,6 +115,10 @@ intptr_t* os::fetch_bcp_from_context(const void* ucVoid) { return reinterpret_cast(uc->REG_BCP); } +void os::win32::context_set_pc(CONTEXT* uc, address pc) { + uc->Pc = (intptr_t)pc; +} + bool os::win32::get_frame_at_stack_banging_point(JavaThread* thread, struct _EXCEPTION_POINTERS* exceptionInfo, address pc, frame* fr) { PEXCEPTION_RECORD exceptionRecord = exceptionInfo->ExceptionRecord; diff --git a/src/hotspot/os_cpu/windows_aarch64/safefetch_windows_aarch64.S b/src/hotspot/os_cpu/windows_aarch64/safefetch_windows_aarch64.S new file mode 100644 index 00000000000..494b68fe4cd --- /dev/null +++ b/src/hotspot/os_cpu/windows_aarch64/safefetch_windows_aarch64.S @@ -0,0 +1,65 @@ +; +; Copyright (c) 2022 SAP SE. All rights reserved. +; Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. +; DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +; +; This code is free software; you can redistribute it and/or modify it +; under the terms of the GNU General Public License version 2 only, as +; published by the Free Software Foundation. +; +; This code is distributed in the hope that it will be useful, but WITHOUT +; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +; version 2 for more details (a copy is included in the LICENSE file that +; accompanied this code). +; +; You should have received a copy of the GNU General Public License version +; 2 along with this work; if not, write to the Free Software Foundation, +; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +; +; Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +; or visit www.oracle.com if you need additional information or have any +; questions. +; + + ; Support for int SafeFetch32(int* address, int defaultval); + ; + ; x0 : address + ; w1 : defaultval + + ; needed to align function start to 4 byte + ALIGN 4 + EXPORT _SafeFetch32_fault + EXPORT _SafeFetch32_continuation + EXPORT SafeFetch32_impl + AREA safefetch_text, CODE + +SafeFetch32_impl +_SafeFetch32_fault + ldr w0, [x0] + ret + +_SafeFetch32_continuation + mov x0, x1 + ret + + ; Support for intptr_t SafeFetchN(intptr_t* address, intptr_t defaultval); + ; + ; x0 : address + ; x1 : defaultval + + ALIGN 4 + EXPORT _SafeFetchN_fault + EXPORT _SafeFetchN_continuation + EXPORT SafeFetchN_impl + +SafeFetchN_impl +_SafeFetchN_fault + ldr x0, [x0] + ret + +_SafeFetchN_continuation + mov x0, x1 + ret + + END diff --git a/src/hotspot/share/asm/codeBuffer.hpp b/src/hotspot/share/asm/codeBuffer.hpp index 35bbd2f657f..430d4949467 100644 --- a/src/hotspot/share/asm/codeBuffer.hpp +++ b/src/hotspot/share/asm/codeBuffer.hpp @@ -57,7 +57,6 @@ public: OSR_Entry, Exceptions, // Offset where exception handler lives Deopt, // Offset where deopt handler lives - DeoptMH, // Offset where MethodHandle deopt handler lives UnwindHandler, // Offset to default unwind handler max_Entries }; @@ -77,7 +76,6 @@ public: _values[OSR_Entry ] = 0; _values[Exceptions ] = -1; _values[Deopt ] = -1; - _values[DeoptMH ] = -1; _values[UnwindHandler ] = -1; } diff --git a/src/hotspot/share/c1/c1_Compilation.cpp b/src/hotspot/share/c1/c1_Compilation.cpp index bb5ceb0106b..368cf604eeb 100644 --- a/src/hotspot/share/c1/c1_Compilation.cpp +++ b/src/hotspot/share/c1/c1_Compilation.cpp @@ -310,14 +310,6 @@ void Compilation::emit_code_epilog(LIR_Assembler* assembler) { code_offsets->set_value(CodeOffsets::Deopt, assembler->emit_deopt_handler()); CHECK_BAILOUT(); - // Emit the MethodHandle deopt handler code (if required). - if (has_method_handle_invokes()) { - // We can use the same code as for the normal deopt handler, we - // just need a different entry point address. - code_offsets->set_value(CodeOffsets::DeoptMH, assembler->emit_deopt_handler()); - CHECK_BAILOUT(); - } - // Emit the handler to remove the activation from the stack and // dispatch to the caller. offsets()->set_value(CodeOffsets::UnwindHandler, assembler->emit_unwind_handler()); @@ -574,7 +566,6 @@ Compilation::Compilation(AbstractCompiler* compiler, ciEnv* env, ciMethod* metho , _has_unsafe_access(false) , _has_irreducible_loops(false) , _would_profile(false) -, _has_method_handle_invokes(false) , _has_reserved_stack_access(method->has_reserved_stack_access()) , _has_monitors(method->is_synchronized() || method->has_monitor_bytecodes()) , _has_scoped_access(method->is_scoped()) diff --git a/src/hotspot/share/c1/c1_Compilation.hpp b/src/hotspot/share/c1/c1_Compilation.hpp index 0c6b95e66c5..5125e0bbe0a 100644 --- a/src/hotspot/share/c1/c1_Compilation.hpp +++ b/src/hotspot/share/c1/c1_Compilation.hpp @@ -79,7 +79,6 @@ class Compilation: public StackObj { bool _has_unsafe_access; bool _has_irreducible_loops; bool _would_profile; - bool _has_method_handle_invokes; // True if this method has MethodHandle invokes. bool _has_reserved_stack_access; bool _has_monitors; // Fastpath monitors detection for Continuations bool _has_scoped_access; // For shared scope closure @@ -180,10 +179,6 @@ class Compilation: public StackObj { // Statistics gathering void notice_inlined_method(ciMethod* method); - // JSR 292 - bool has_method_handle_invokes() const { return _has_method_handle_invokes; } - void set_has_method_handle_invokes(bool z) { _has_method_handle_invokes = z; } - bool has_reserved_stack_access() const { return _has_reserved_stack_access; } void set_has_reserved_stack_access(bool z) { _has_reserved_stack_access = z; } diff --git a/src/hotspot/share/c1/c1_FrameMap.hpp b/src/hotspot/share/c1/c1_FrameMap.hpp index f10c4d3f226..67ae92e9875 100644 --- a/src/hotspot/share/c1/c1_FrameMap.hpp +++ b/src/hotspot/share/c1/c1_FrameMap.hpp @@ -155,9 +155,6 @@ class FrameMap : public CompilationResourceObj { // Opr representing the stack_pointer on this platform static LIR_Opr stack_pointer(); - // JSR 292 - static LIR_Opr method_handle_invoke_SP_save_opr(); - static BasicTypeArray* signature_type_array_for(const ciMethod* method); // for outgoing calls, these also update the reserved area to diff --git a/src/hotspot/share/c1/c1_IR.cpp b/src/hotspot/share/c1/c1_IR.cpp index ae8332116b3..238a9bdda0d 100644 --- a/src/hotspot/share/c1/c1_IR.cpp +++ b/src/hotspot/share/c1/c1_IR.cpp @@ -190,7 +190,6 @@ CodeEmitInfo::CodeEmitInfo(ValueStack* stack, XHandlers* exception_handlers, boo , _exception_handlers(exception_handlers) , _oop_map(nullptr) , _stack(stack) - , _is_method_handle_invoke(false) , _deoptimize_on_exception(deoptimize_on_exception) , _force_reexecute(false) { assert(_stack != nullptr, "must be non null"); @@ -203,7 +202,6 @@ CodeEmitInfo::CodeEmitInfo(CodeEmitInfo* info, ValueStack* stack) , _exception_handlers(nullptr) , _oop_map(nullptr) , _stack(stack == nullptr ? info->_stack : stack) - , _is_method_handle_invoke(info->_is_method_handle_invoke) , _deoptimize_on_exception(info->_deoptimize_on_exception) , _force_reexecute(info->_force_reexecute) { @@ -218,7 +216,7 @@ void CodeEmitInfo::record_debug_info(DebugInformationRecorder* recorder, int pc_ // record the safepoint before recording the debug info for enclosing scopes recorder->add_safepoint(pc_offset, _oop_map->deep_copy()); bool reexecute = _force_reexecute || _scope_debug_info->should_reexecute(); - _scope_debug_info->record_debug_info(recorder, pc_offset, reexecute, _is_method_handle_invoke); + _scope_debug_info->record_debug_info(recorder, pc_offset, reexecute); recorder->end_safepoint(pc_offset); } diff --git a/src/hotspot/share/c1/c1_IR.hpp b/src/hotspot/share/c1/c1_IR.hpp index a9a7a026390..d6a4cddb9d7 100644 --- a/src/hotspot/share/c1/c1_IR.hpp +++ b/src/hotspot/share/c1/c1_IR.hpp @@ -234,7 +234,7 @@ class IRScopeDebugInfo: public CompilationResourceObj { //Whether we should reexecute this bytecode for deopt bool should_reexecute(); - void record_debug_info(DebugInformationRecorder* recorder, int pc_offset, bool reexecute, bool is_method_handle_invoke = false) { + void record_debug_info(DebugInformationRecorder* recorder, int pc_offset, bool reexecute) { if (caller() != nullptr) { // Order is significant: Must record caller first. caller()->record_debug_info(recorder, pc_offset, false/*reexecute*/); @@ -248,7 +248,7 @@ class IRScopeDebugInfo: public CompilationResourceObj { bool has_ea_local_in_scope = false; bool arg_escape = false; recorder->describe_scope(pc_offset, methodHandle(), scope()->method(), bci(), - reexecute, rethrow_exception, is_method_handle_invoke, return_oop, + reexecute, rethrow_exception, return_oop, has_ea_local_in_scope, arg_escape, locvals, expvals, monvals); } }; @@ -262,7 +262,6 @@ class CodeEmitInfo: public CompilationResourceObj { XHandlers* _exception_handlers; OopMap* _oop_map; ValueStack* _stack; // used by deoptimization (contains also monitors - bool _is_method_handle_invoke; // true if the associated call site is a MethodHandle call site. bool _deoptimize_on_exception; bool _force_reexecute; // force the reexecute flag on, used for patching stub @@ -288,9 +287,6 @@ class CodeEmitInfo: public CompilationResourceObj { void add_register_oop(LIR_Opr opr); void record_debug_info(DebugInformationRecorder* recorder, int pc_offset); - bool is_method_handle_invoke() const { return _is_method_handle_invoke; } - void set_is_method_handle_invoke(bool x) { _is_method_handle_invoke = x; } - bool force_reexecute() const { return _force_reexecute; } void set_force_reexecute() { _force_reexecute = true; } diff --git a/src/hotspot/share/c1/c1_LIR.cpp b/src/hotspot/share/c1/c1_LIR.cpp index f11e178bd55..012c0f92f25 100644 --- a/src/hotspot/share/c1/c1_LIR.cpp +++ b/src/hotspot/share/c1/c1_LIR.cpp @@ -709,11 +709,6 @@ void LIR_OpVisitState::visit(LIR_Op* op) { } if (opJavaCall->_info) do_info(opJavaCall->_info); - if (FrameMap::method_handle_invoke_SP_save_opr() != LIR_OprFact::illegalOpr && - opJavaCall->is_method_handle_invoke()) { - opJavaCall->_method_handle_invoke_SP_save_opr = FrameMap::method_handle_invoke_SP_save_opr(); - do_temp(opJavaCall->_method_handle_invoke_SP_save_opr); - } do_call(); if (opJavaCall->_result->is_valid()) do_output(opJavaCall->_result); diff --git a/src/hotspot/share/c1/c1_LIR.hpp b/src/hotspot/share/c1/c1_LIR.hpp index 0427c868e6f..847184731ce 100644 --- a/src/hotspot/share/c1/c1_LIR.hpp +++ b/src/hotspot/share/c1/c1_LIR.hpp @@ -1176,7 +1176,6 @@ class LIR_OpJavaCall: public LIR_OpCall { private: ciMethod* _method; LIR_Opr _receiver; - LIR_Opr _method_handle_invoke_SP_save_opr; // Used in LIR_OpVisitState::visit to store the reference to FrameMap::method_handle_invoke_SP_save_opr. public: LIR_OpJavaCall(LIR_Code code, ciMethod* method, @@ -1186,7 +1185,6 @@ class LIR_OpJavaCall: public LIR_OpCall { : LIR_OpCall(code, addr, result, arguments, info) , _method(method) , _receiver(receiver) - , _method_handle_invoke_SP_save_opr(LIR_OprFact::illegalOpr) { assert(is_in_range(code, begin_opJavaCall, end_opJavaCall), "code check"); } LIR_OpJavaCall(LIR_Code code, ciMethod* method, @@ -1195,7 +1193,6 @@ class LIR_OpJavaCall: public LIR_OpCall { : LIR_OpCall(code, (address)vtable_offset, result, arguments, info) , _method(method) , _receiver(receiver) - , _method_handle_invoke_SP_save_opr(LIR_OprFact::illegalOpr) { assert(is_in_range(code, begin_opJavaCall, end_opJavaCall), "code check"); } LIR_Opr receiver() const { return _receiver; } diff --git a/src/hotspot/share/c1/c1_LIRAssembler.cpp b/src/hotspot/share/c1/c1_LIRAssembler.cpp index 6dbd35f054f..e6963c00c6a 100644 --- a/src/hotspot/share/c1/c1_LIRAssembler.cpp +++ b/src/hotspot/share/c1/c1_LIRAssembler.cpp @@ -478,12 +478,6 @@ void LIR_Assembler::emit_call(LIR_OpJavaCall* op) { fatal("unexpected op code: %s", op->name()); break; } - - // JSR 292 - // Record if this method has MethodHandle invokes. - if (op->is_method_handle_invoke()) { - compilation()->set_has_method_handle_invokes(true); - } } diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp index 66adfa5ed66..f6807abcd7a 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.cpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp @@ -2712,19 +2712,7 @@ void LIRGenerator::do_Invoke(Invoke* x) { // emit invoke code assert(receiver->is_illegal() || receiver->is_equal(LIR_Assembler::receiverOpr()), "must match"); - // JSR 292 - // Preserve the SP over MethodHandle call sites, if needed. ciMethod* target = x->target(); - bool is_method_handle_invoke = (// %%% FIXME: Are both of these relevant? - target->is_method_handle_intrinsic() || - target->is_compiled_lambda_form()); - if (is_method_handle_invoke) { - info->set_is_method_handle_invoke(true); - if(FrameMap::method_handle_invoke_SP_save_opr() != LIR_OprFact::illegalOpr) { - __ move(FrameMap::stack_pointer(), FrameMap::method_handle_invoke_SP_save_opr()); - } - } - switch (x->code()) { case Bytecodes::_invokestatic: __ call_static(target, result_register, @@ -2757,13 +2745,6 @@ void LIRGenerator::do_Invoke(Invoke* x) { break; } - // JSR 292 - // Restore the SP after MethodHandle call sites, if needed. - if (is_method_handle_invoke - && FrameMap::method_handle_invoke_SP_save_opr() != LIR_OprFact::illegalOpr) { - __ move(FrameMap::method_handle_invoke_SP_save_opr(), FrameMap::stack_pointer()); - } - if (result_register->is_valid()) { LIR_Opr result = rlock_result(x); __ move(result_register, result); diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp index 637c7c46ef4..a4c956ff5be 100644 --- a/src/hotspot/share/c1/c1_Runtime1.cpp +++ b/src/hotspot/share/c1/c1_Runtime1.cpp @@ -541,9 +541,6 @@ extern void vm_exit(int code); // unpack_with_exception entry instead. This makes life for the exception blob easier // because making that same check and diverting is painful from assembly language. JRT_ENTRY_NO_ASYNC(static address, exception_handler_for_pc_helper(JavaThread* current, oopDesc* ex, address pc, nmethod*& nm)) - // Reset method handle flag. - current->set_is_method_handle_return(false); - Handle exception(current, ex); // This function is called when we are about to throw an exception. Therefore, @@ -622,8 +619,6 @@ JRT_ENTRY_NO_ASYNC(static address, exception_handler_for_pc_helper(JavaThread* c if (guard_pages_enabled) { address fast_continuation = nm->handler_for_exception_and_pc(exception, pc); if (fast_continuation != nullptr) { - // Set flag if return address is a method handle call site. - current->set_is_method_handle_return(nm->is_method_handle_return(pc)); return fast_continuation; } } @@ -660,8 +655,6 @@ JRT_ENTRY_NO_ASYNC(static address, exception_handler_for_pc_helper(JavaThread* c } current->set_vm_result_oop(exception()); - // Set flag if return address is a method handle call site. - current->set_is_method_handle_return(nm->is_method_handle_return(pc)); if (log_is_enabled(Info, exceptions)) { ResourceMark rm; diff --git a/src/hotspot/share/cds/aotMapLogger.cpp b/src/hotspot/share/cds/aotMapLogger.cpp index a15d7f670d4..b21e2fa141c 100644 --- a/src/hotspot/share/cds/aotMapLogger.cpp +++ b/src/hotspot/share/cds/aotMapLogger.cpp @@ -33,7 +33,10 @@ #include "memory/metaspaceClosure.hpp" #include "memory/resourceArea.hpp" #include "oops/method.hpp" +#include "oops/methodCounters.hpp" +#include "oops/methodData.hpp" #include "oops/oop.inline.hpp" +#include "oops/trainingData.hpp" #include "runtime/fieldDescriptor.inline.hpp" #include "runtime/globals_extension.hpp" #include "utilities/growableArray.hpp" @@ -348,9 +351,18 @@ void AOTMapLogger::log_metaspace_objects_impl(address region_base, address regio case MetaspaceObj::MethodType: log_method((Method*)src, requested_addr, type_name, bytes, current); break; + case MetaspaceObj::MethodCountersType: + log_method_counters((MethodCounters*)src, requested_addr, type_name, bytes, current); + break; + case MetaspaceObj::MethodDataType: + log_method_data((MethodData*)src, requested_addr, type_name, bytes, current); + break; case MetaspaceObj::SymbolType: log_symbol((Symbol*)src, requested_addr, type_name, bytes, current); break; + case MetaspaceObj::KlassTrainingDataType: + log_klass_training_data((KlassTrainingData*)src, requested_addr, type_name, bytes, current); + break; default: log_debug(aot, map)(_LOG_PREFIX, p2i(requested_addr), type_name, bytes); break; @@ -389,6 +401,18 @@ void AOTMapLogger::log_const_method(ConstMethod* cm, address requested_addr, con log_debug(aot, map)(_LOG_PREFIX " %s", p2i(requested_addr), type_name, bytes, cm->method()->external_name()); } +void AOTMapLogger::log_method_counters(MethodCounters* mc, address requested_addr, const char* type_name, + int bytes, Thread* current) { + ResourceMark rm(current); + log_debug(aot, map)(_LOG_PREFIX " %s", p2i(requested_addr), type_name, bytes, mc->method()->external_name()); +} + +void AOTMapLogger::log_method_data(MethodData* md, address requested_addr, const char* type_name, + int bytes, Thread* current) { + ResourceMark rm(current); + log_debug(aot, map)(_LOG_PREFIX " %s", p2i(requested_addr), type_name, bytes, md->method()->external_name()); +} + void AOTMapLogger::log_klass(Klass* k, address requested_addr, const char* type_name, int bytes, Thread* current) { ResourceMark rm(current); @@ -407,6 +431,16 @@ void AOTMapLogger::log_symbol(Symbol* s, address requested_addr, const char* typ log_debug(aot, map)(_LOG_PREFIX " %s", p2i(requested_addr), type_name, bytes, s->as_quoted_ascii()); } +void AOTMapLogger::log_klass_training_data(KlassTrainingData* ktd, address requested_addr, const char* type_name, + int bytes, Thread* current) { + ResourceMark rm(current); + if (ktd->has_holder()) { + log_debug(aot, map)(_LOG_PREFIX " %s", p2i(requested_addr), type_name, bytes, + ktd->name()->as_klass_external_name()); + } else { + log_debug(aot, map)(_LOG_PREFIX, p2i(requested_addr), type_name, bytes); + } +} #undef _LOG_PREFIX diff --git a/src/hotspot/share/cds/aotMapLogger.hpp b/src/hotspot/share/cds/aotMapLogger.hpp index 9cd67fb7ff6..3f87215e62a 100644 --- a/src/hotspot/share/cds/aotMapLogger.hpp +++ b/src/hotspot/share/cds/aotMapLogger.hpp @@ -35,6 +35,7 @@ class ArchiveHeapInfo; class DumpRegion; class FileMapInfo; +class KlassTrainingData; class outputStream; // Write detailed info to a mapfile to analyze contents of the AOT cache/CDS archive. @@ -98,9 +99,14 @@ class AOTMapLogger : AllStatic { static void log_constant_pool_cache(ConstantPoolCache* cpc, address requested_addr, const char* type_name, int bytes, Thread* current); static void log_const_method(ConstMethod* cm, address requested_addr, const char* type_name, int bytes, Thread* current); + static void log_method_counters(MethodCounters* mc, address requested_addr, const char* type_name, int bytes, + Thread* current); + static void log_method_data(MethodData* md, address requested_addr, const char* type_name, int bytes, + Thread* current); static void log_klass(Klass* k, address requested_addr, const char* type_name, int bytes, Thread* current); static void log_method(Method* m, address requested_addr, const char* type_name, int bytes, Thread* current); static void log_symbol(Symbol* s, address requested_addr, const char* type_name, int bytes, Thread* current); + static void log_klass_training_data(KlassTrainingData* ktd, address requested_addr, const char* type_name, int bytes, Thread* current); #if INCLUDE_CDS_JAVA_HEAP diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index fddd9df726b..87f2da91288 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -4678,11 +4678,15 @@ const char* ClassFileParser::skip_over_field_signature(const char* signature, return signature + 1; case JVM_SIGNATURE_CLASS: { if (_major_version < JAVA_1_5_VERSION) { + signature++; + length--; // Skip over the class name if one is there - const char* const p = skip_over_field_name(signature + 1, true, --length); - + const char* const p = skip_over_field_name(signature, true, length); + assert(p == nullptr || p > signature, "must parse one character at least"); // The next character better be a semicolon - if (p && (p - signature) > 1 && p[0] == JVM_SIGNATURE_ENDCLASS) { + if (p != nullptr && // Parse of field name succeeded. + p - signature < static_cast(length) && // There is at least one character left to parse. + p[0] == JVM_SIGNATURE_ENDCLASS) { return p + 1; } } diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index da022436292..b092e71f4e7 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -174,7 +174,6 @@ InstanceKlass* SystemDictionaryShared::acquire_class_for_current_thread( // No longer holding SharedDictionary_lock // No need to lock, as can be held only by a single thread. - loader_data->add_class(ik); // Get the package entry. PackageEntry* pkg_entry = CDSProtectionDomain::get_package_entry_from_class(ik, class_loader); diff --git a/src/hotspot/share/classfile/vmClasses.cpp b/src/hotspot/share/classfile/vmClasses.cpp index 478f40ff05b..fbb234d95c0 100644 --- a/src/hotspot/share/classfile/vmClasses.cpp +++ b/src/hotspot/share/classfile/vmClasses.cpp @@ -119,9 +119,11 @@ void vmClasses::resolve_all(TRAPS) { // after vmSymbols::initialize() is called but before any classes are pre-loaded. ClassLoader::classLoader_init2(THREAD); +#if INCLUDE_CDS if (CDSConfig::is_using_aot_linked_classes()) { AOTLinkedClassBulkLoader::preload_classes(THREAD); } +#endif // Preload commonly used klasses vmClassID scan = vmClassID::FIRST; diff --git a/src/hotspot/share/code/codeBehaviours.cpp b/src/hotspot/share/code/codeBehaviours.cpp index 1f9eb0e2914..bc5a81c95b3 100644 --- a/src/hotspot/share/code/codeBehaviours.cpp +++ b/src/hotspot/share/code/codeBehaviours.cpp @@ -23,23 +23,24 @@ */ #include "code/codeBehaviours.hpp" +#include "code/nmethod.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/safepoint.hpp" CompiledICProtectionBehaviour* CompiledICProtectionBehaviour::_current = nullptr; -bool DefaultICProtectionBehaviour::lock(nmethod* method) { - if (is_safe(method)) { +bool DefaultICProtectionBehaviour::lock(nmethod* nm) { + if (is_safe(nm)) { return false; } CompiledIC_lock->lock_without_safepoint_check(); return true; } -void DefaultICProtectionBehaviour::unlock(nmethod* method) { +void DefaultICProtectionBehaviour::unlock(nmethod* nm) { CompiledIC_lock->unlock(); } -bool DefaultICProtectionBehaviour::is_safe(nmethod* method) { - return SafepointSynchronize::is_at_safepoint() || CompiledIC_lock->owned_by_self(); +bool DefaultICProtectionBehaviour::is_safe(nmethod* nm) { + return SafepointSynchronize::is_at_safepoint() || CompiledIC_lock->owned_by_self() || (NMethodState_lock->owned_by_self() && nm->is_not_installed()); } diff --git a/src/hotspot/share/code/codeBehaviours.hpp b/src/hotspot/share/code/codeBehaviours.hpp index 0350f5752f6..96a38fffc30 100644 --- a/src/hotspot/share/code/codeBehaviours.hpp +++ b/src/hotspot/share/code/codeBehaviours.hpp @@ -33,18 +33,18 @@ class CompiledICProtectionBehaviour { static CompiledICProtectionBehaviour* _current; public: - virtual bool lock(nmethod* method) = 0; - virtual void unlock(nmethod* method) = 0; - virtual bool is_safe(nmethod* method) = 0; + virtual bool lock(nmethod* nm) = 0; + virtual void unlock(nmethod* nm) = 0; + virtual bool is_safe(nmethod* nm) = 0; static CompiledICProtectionBehaviour* current() { return _current; } static void set_current(CompiledICProtectionBehaviour* current) { _current = current; } }; class DefaultICProtectionBehaviour: public CompiledICProtectionBehaviour, public CHeapObj { - virtual bool lock(nmethod* method); - virtual void unlock(nmethod* method); - virtual bool is_safe(nmethod* method); + virtual bool lock(nmethod* nm); + virtual void unlock(nmethod* nm); + virtual bool is_safe(nmethod* nm); }; #endif // SHARE_CODE_CODEBEHAVIOURS_HPP diff --git a/src/hotspot/share/code/codeCache.hpp b/src/hotspot/share/code/codeCache.hpp index 3e446ab8430..dc9e5d7dc20 100644 --- a/src/hotspot/share/code/codeCache.hpp +++ b/src/hotspot/share/code/codeCache.hpp @@ -259,7 +259,7 @@ class CodeCache : AllStatic { static bool heap_available(CodeBlobType code_blob_type); // Returns the CodeBlobType for the given nmethod - static CodeBlobType get_code_blob_type(nmethod* nm) { + static CodeBlobType get_code_blob_type(const nmethod* nm) { return get_code_heap(nm)->code_blob_type(); } diff --git a/src/hotspot/share/code/compiledIC.cpp b/src/hotspot/share/code/compiledIC.cpp index 03d9adc8e24..5f5c9711441 100644 --- a/src/hotspot/share/code/compiledIC.cpp +++ b/src/hotspot/share/code/compiledIC.cpp @@ -55,8 +55,8 @@ CompiledICLocker::~CompiledICLocker() { } } -bool CompiledICLocker::is_safe(nmethod* method) { - return CompiledICProtectionBehaviour::current()->is_safe(method); +bool CompiledICLocker::is_safe(nmethod* nm) { + return CompiledICProtectionBehaviour::current()->is_safe(nm); } bool CompiledICLocker::is_safe(address code) { diff --git a/src/hotspot/share/code/compiledIC.hpp b/src/hotspot/share/code/compiledIC.hpp index 624c1b428de..db8a0860b39 100644 --- a/src/hotspot/share/code/compiledIC.hpp +++ b/src/hotspot/share/code/compiledIC.hpp @@ -50,7 +50,7 @@ class CompiledICLocker: public StackObj { public: CompiledICLocker(nmethod* method); ~CompiledICLocker(); - static bool is_safe(nmethod* method); + static bool is_safe(nmethod* nm); static bool is_safe(address code); }; diff --git a/src/hotspot/share/code/debugInfoRec.cpp b/src/hotspot/share/code/debugInfoRec.cpp index 8449f5d6929..41d944ec76f 100644 --- a/src/hotspot/share/code/debugInfoRec.cpp +++ b/src/hotspot/share/code/debugInfoRec.cpp @@ -283,7 +283,6 @@ void DebugInformationRecorder::describe_scope(int pc_offset, int bci, bool reexecute, bool rethrow_exception, - bool is_method_handle_invoke, bool return_oop, bool has_ea_local_in_scope, bool arg_escape, @@ -301,7 +300,6 @@ void DebugInformationRecorder::describe_scope(int pc_offset, // Record flags into pcDesc. last_pd->set_should_reexecute(reexecute); last_pd->set_rethrow_exception(rethrow_exception); - last_pd->set_is_method_handle_invoke(is_method_handle_invoke); last_pd->set_return_oop(return_oop); last_pd->set_has_ea_local_in_scope(has_ea_local_in_scope); last_pd->set_arg_escape(arg_escape); diff --git a/src/hotspot/share/code/debugInfoRec.hpp b/src/hotspot/share/code/debugInfoRec.hpp index f6d7cf6d278..d18208e17c4 100644 --- a/src/hotspot/share/code/debugInfoRec.hpp +++ b/src/hotspot/share/code/debugInfoRec.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -105,7 +105,6 @@ class DebugInformationRecorder: public ResourceObj { int bci, bool reexecute, bool rethrow_exception = false, - bool is_method_handle_invoke = false, bool return_oop = false, bool has_ea_local_in_scope = false, bool arg_escape = false, diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index c0091cd2f65..7274b627f3e 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -465,14 +465,6 @@ static int adjust_pcs_size(int pcs_size) { return nsize; } -bool nmethod::is_method_handle_return(address return_pc) { - if (!has_method_handle_invokes()) return false; - PcDesc* pd = pc_desc_at(return_pc); - if (pd == nullptr) - return false; - return pd->is_method_handle_invoke(); -} - // Returns a string version of the method state. const char* nmethod::state() const { int state = get_state(); @@ -762,7 +754,7 @@ Method* nmethod::attached_method_before_pc(address pc) { } void nmethod::clear_inline_caches() { - assert(SafepointSynchronize::is_at_safepoint(), "clearing of IC's only allowed at safepoint"); + assert(SafepointSynchronize::is_at_safepoint() || (NMethodState_lock->owned_by_self() && is_not_installed()), "clearing of IC's only allowed at safepoint or when not installed"); RelocIterator iter(this); while (iter.next()) { iter.reloc()->clear_inline_cache(); @@ -1154,7 +1146,8 @@ nmethod* nmethod::new_nmethod(const methodHandle& method, #if INCLUDE_JVMCI + align_up(speculations_len , oopSize) #endif - + align_up(debug_info->data_size() , oopSize); + + align_up(debug_info->data_size() , oopSize) + + align_up(ImmutableDataReferencesCounterSize, oopSize); // First, allocate space for immutable data in C heap. address immutable_data = nullptr; @@ -1231,7 +1224,6 @@ void nmethod::init_defaults(CodeBuffer *code_buffer, CodeOffsets* offsets) { _state = not_installed; _has_unsafe_access = 0; - _has_method_handle_invokes = 0; _has_wide_vectors = 0; _has_monitors = 0; _has_scoped_access = 0; @@ -1310,7 +1302,6 @@ nmethod::nmethod( // Native wrappers do not have deopt handlers. Make the values // something that will never match a pc like the nmethod vtable entry _deopt_handler_offset = 0; - _deopt_mh_handler_offset = 0; _unwind_handler_offset = 0; CHECKED_CAST(_oops_size, uint16_t, align_up(code_buffer->total_oop_size(), oopSize)); @@ -1381,10 +1372,266 @@ nmethod::nmethod( } } + +nmethod::nmethod(const nmethod &nm) : CodeBlob(nm._name, nm._kind, nm._size, nm._header_size) +{ + + if (nm._oop_maps != nullptr) { + _oop_maps = nm._oop_maps->clone(); + } else { + _oop_maps = nullptr; + } + + _size = nm._size; + _relocation_size = nm._relocation_size; + _content_offset = nm._content_offset; + _code_offset = nm._code_offset; + _data_offset = nm._data_offset; + _frame_size = nm._frame_size; + + S390_ONLY( _ctable_offset = nm._ctable_offset; ) + + _header_size = nm._header_size; + _frame_complete_offset = nm._frame_complete_offset; + + _kind = nm._kind; + + _caller_must_gc_arguments = nm._caller_must_gc_arguments; + +#ifndef PRODUCT + _asm_remarks.share(nm._asm_remarks); + _dbg_strings.share(nm._dbg_strings); +#endif + + // Allocate memory and copy mutable data to C heap + _mutable_data_size = nm._mutable_data_size; + if (_mutable_data_size > 0) { + _mutable_data = (address)os::malloc(_mutable_data_size, mtCode); + if (_mutable_data == nullptr) { + vm_exit_out_of_memory(_mutable_data_size, OOM_MALLOC_ERROR, "nmethod: no space for mutable data"); + } + memcpy(mutable_data_begin(), nm.mutable_data_begin(), nm.mutable_data_size()); + } else { + _mutable_data = nullptr; + } + + _deoptimization_generation = 0; + _gc_epoch = CodeCache::gc_epoch(); + _method = nm._method; + _osr_link = nullptr; + + // Increment number of references to immutable data to share it between nmethods + _immutable_data_size = nm._immutable_data_size; + if (_immutable_data_size > 0) { + _immutable_data = nm._immutable_data; + set_immutable_data_references_counter(get_immutable_data_references_counter() + 1); + } else { + _immutable_data = blob_end(); + } + + _exception_cache = nullptr; + _gc_data = nullptr; + _oops_do_mark_nmethods = nullptr; + _oops_do_mark_link = nullptr; + _compiled_ic_data = nullptr; + + if (nm._osr_entry_point != nullptr) { + _osr_entry_point = (nm._osr_entry_point - (address) &nm) + (address) this; + } else { + _osr_entry_point = nullptr; + } + + _entry_offset = nm._entry_offset; + _verified_entry_offset = nm._verified_entry_offset; + _entry_bci = nm._entry_bci; + + _skipped_instructions_size = nm._skipped_instructions_size; + _stub_offset = nm._stub_offset; + _exception_offset = nm._exception_offset; + _deopt_handler_offset = nm._deopt_handler_offset; + _unwind_handler_offset = nm._unwind_handler_offset; + _num_stack_arg_slots = nm._num_stack_arg_slots; + _oops_size = nm._oops_size; +#if INCLUDE_JVMCI + _metadata_size = nm._metadata_size; +#endif + _nul_chk_table_offset = nm._nul_chk_table_offset; + _handler_table_offset = nm._handler_table_offset; + _scopes_pcs_offset = nm._scopes_pcs_offset; + _scopes_data_offset = nm._scopes_data_offset; +#if INCLUDE_JVMCI + _speculations_offset = nm._speculations_offset; +#endif + + _orig_pc_offset = nm._orig_pc_offset; + _compile_id = nm._compile_id; + _comp_level = nm._comp_level; + _compiler_type = nm._compiler_type; + _is_unloading_state = nm._is_unloading_state; + _state = not_installed; + + _has_unsafe_access = nm._has_unsafe_access; + _has_wide_vectors = nm._has_wide_vectors; + _has_monitors = nm._has_monitors; + _has_scoped_access = nm._has_scoped_access; + _has_flushed_dependencies = nm._has_flushed_dependencies; + _is_unlinked = nm._is_unlinked; + _load_reported = nm._load_reported; + + _deoptimization_status = nm._deoptimization_status; + + if (nm._pc_desc_container != nullptr) { + _pc_desc_container = new PcDescContainer(scopes_pcs_begin()); + } else { + _pc_desc_container = nullptr; + } + + // Copy nmethod contents excluding header + // - Constant part (doubles, longs and floats used in nmethod) + // - Code part: + // - Code body + // - Exception handler + // - Stub code + // - OOP table + memcpy(consts_begin(), nm.consts_begin(), nm.data_end() - nm.consts_begin()); + + post_init(); +} + +nmethod* nmethod::relocate(CodeBlobType code_blob_type) { + assert(NMethodRelocation, "must enable use of function"); + + // Locks required to be held by caller to ensure the nmethod + // is not modified or purged from code cache during relocation + assert_lock_strong(CodeCache_lock); + assert_lock_strong(Compile_lock); + assert(CompiledICLocker::is_safe(this), "mt unsafe call"); + + if (!is_relocatable()) { + return nullptr; + } + + run_nmethod_entry_barrier(); + nmethod* nm_copy = new (size(), code_blob_type) nmethod(*this); + + if (nm_copy == nullptr) { + return nullptr; + } + + // Fix relocation + RelocIterator iter(nm_copy); + CodeBuffer src(this); + CodeBuffer dst(nm_copy); + while (iter.next()) { +#ifdef USE_TRAMPOLINE_STUB_FIX_OWNER + // Direct calls may no longer be in range and the use of a trampoline may now be required. + // Instead, allow trampoline relocations to update their owners and perform the necessary checks. + if (iter.reloc()->is_call()) { + address trampoline = trampoline_stub_Relocation::get_trampoline_for(iter.reloc()->addr(), nm_copy); + if (trampoline != nullptr) { + continue; + } + } +#endif + + iter.reloc()->fix_relocation_after_move(&src, &dst); + } + + // To make dependency checking during class loading fast, record + // the nmethod dependencies in the classes it is dependent on. + // This allows the dependency checking code to simply walk the + // class hierarchy above the loaded class, checking only nmethods + // which are dependent on those classes. The slow way is to + // check every nmethod for dependencies which makes it linear in + // the number of methods compiled. For applications with a lot + // classes the slow way is too slow. + for (Dependencies::DepStream deps(nm_copy); deps.next(); ) { + if (deps.type() == Dependencies::call_site_target_value) { + // CallSite dependencies are managed on per-CallSite instance basis. + oop call_site = deps.argument_oop(0); + MethodHandles::add_dependent_nmethod(call_site, nm_copy); + } else { + InstanceKlass* ik = deps.context_type(); + if (ik == nullptr) { + continue; // ignore things like evol_method + } + // record this nmethod as dependent on this klass + ik->add_dependent_nmethod(nm_copy); + } + } + + MutexLocker ml_NMethodState_lock(NMethodState_lock, Mutex::_no_safepoint_check_flag); + + // Verify the nm we copied from is still valid + if (!is_marked_for_deoptimization() && is_in_use()) { + assert(method() != nullptr && method()->code() == this, "should be if is in use"); + + nm_copy->clear_inline_caches(); + + // Attempt to start using the copy + if (nm_copy->make_in_use()) { + ICache::invalidate_range(nm_copy->code_begin(), nm_copy->code_size()); + + methodHandle mh(Thread::current(), nm_copy->method()); + nm_copy->method()->set_code(mh, nm_copy); + + make_not_used(); + + nm_copy->post_compiled_method_load_event(); + + nm_copy->log_relocated_nmethod(this); + + return nm_copy; + } + } + + nm_copy->make_not_used(); + + return nullptr; +} + +bool nmethod::is_relocatable() { + if (!is_java_method()) { + return false; + } + + if (!is_in_use()) { + return false; + } + + if (is_osr_method()) { + return false; + } + + if (is_marked_for_deoptimization()) { + return false; + } + +#if INCLUDE_JVMCI + if (jvmci_nmethod_data() != nullptr && jvmci_nmethod_data()->has_mirror()) { + return false; + } +#endif + + if (is_unloading()) { + return false; + } + + if (has_evol_metadata()) { + return false; + } + + return true; +} + void* nmethod::operator new(size_t size, int nmethod_size, int comp_level) throw () { return CodeCache::allocate(nmethod_size, CodeCache::get_code_blob_type(comp_level)); } +void* nmethod::operator new(size_t size, int nmethod_size, CodeBlobType code_blob_type) throw () { + return CodeCache::allocate(nmethod_size, code_blob_type); +} + void* nmethod::operator new(size_t size, int nmethod_size, bool allow_NonNMethod_space) throw () { // Try MethodNonProfiled and MethodProfiled. void* return_value = CodeCache::allocate(nmethod_size, CodeBlobType::MethodNonProfiled); @@ -1458,11 +1705,6 @@ nmethod::nmethod( } else { _deopt_handler_offset = -1; } - if (offsets->value(CodeOffsets::DeoptMH) != -1) { - _deopt_mh_handler_offset = code_offset() + offsets->value(CodeOffsets::DeoptMH); - } else { - _deopt_mh_handler_offset = -1; - } } else #endif { @@ -1472,11 +1714,6 @@ nmethod::nmethod( _exception_offset = _stub_offset + offsets->value(CodeOffsets::Exceptions); _deopt_handler_offset = _stub_offset + offsets->value(CodeOffsets::Deopt); - if (offsets->value(CodeOffsets::DeoptMH) != -1) { - _deopt_mh_handler_offset = _stub_offset + offsets->value(CodeOffsets::DeoptMH); - } else { - _deopt_mh_handler_offset = -1; - } } if (offsets->value(CodeOffsets::UnwindHandler) != -1) { // C1 generates UnwindHandler at the end of instructions section. @@ -1514,9 +1751,9 @@ nmethod::nmethod( #if INCLUDE_JVMCI _speculations_offset = _scopes_data_offset + align_up(debug_info->data_size(), oopSize); - DEBUG_ONLY( int immutable_data_end_offset = _speculations_offset + align_up(speculations_len, oopSize); ) + DEBUG_ONLY( int immutable_data_end_offset = _speculations_offset + align_up(speculations_len, oopSize) + align_up(ImmutableDataReferencesCounterSize, oopSize); ) #else - DEBUG_ONLY( int immutable_data_end_offset = _scopes_data_offset + align_up(debug_info->data_size(), oopSize); ) + DEBUG_ONLY( int immutable_data_end_offset = _scopes_data_offset + align_up(debug_info->data_size(), oopSize) + align_up(ImmutableDataReferencesCounterSize, oopSize); ) #endif assert(immutable_data_end_offset <= immutable_data_size, "wrong read-only data size: %d > %d", immutable_data_end_offset, immutable_data_size); @@ -1549,6 +1786,7 @@ nmethod::nmethod( memcpy(speculations_begin(), speculations, speculations_len); } #endif + set_immutable_data_references_counter(1); post_init(); @@ -1615,6 +1853,40 @@ void nmethod::log_new_nmethod() const { } } + +void nmethod::log_relocated_nmethod(nmethod* original) const { + if (LogCompilation && xtty != nullptr) { + ttyLocker ttyl; + xtty->begin_elem("relocated nmethod"); + log_identity(xtty); + xtty->print(" entry='" INTPTR_FORMAT "' size='%d'", p2i(code_begin()), size()); + + const char* original_code_heap_name = CodeCache::get_code_heap_name(CodeCache::get_code_blob_type(original)); + xtty->print(" original_address='" INTPTR_FORMAT "'", p2i(original)); + xtty->print(" original_code_heap='%s'", original_code_heap_name); + + const char* new_code_heap_name = CodeCache::get_code_heap_name(CodeCache::get_code_blob_type(this)); + xtty->print(" new_address='" INTPTR_FORMAT "'", p2i(this)); + xtty->print(" new_code_heap='%s'", new_code_heap_name); + + LOG_OFFSET(xtty, relocation); + LOG_OFFSET(xtty, consts); + LOG_OFFSET(xtty, insts); + LOG_OFFSET(xtty, stub); + LOG_OFFSET(xtty, scopes_data); + LOG_OFFSET(xtty, scopes_pcs); + LOG_OFFSET(xtty, dependencies); + LOG_OFFSET(xtty, handler_table); + LOG_OFFSET(xtty, nul_chk_table); + LOG_OFFSET(xtty, oops); + LOG_OFFSET(xtty, metadata); + + xtty->method(method()); + xtty->stamp(); + xtty->end_elem(); + } +} + #undef LOG_OFFSET @@ -2147,9 +2419,18 @@ void nmethod::purge(bool unregister_nmethod) { delete[] _compiled_ic_data; if (_immutable_data != blob_end()) { - os::free(_immutable_data); + int reference_count = get_immutable_data_references_counter(); + assert(reference_count > 0, "immutable data has no references"); + + set_immutable_data_references_counter(reference_count - 1); + // Free memory if this is the last nmethod referencing immutable data + if (reference_count == 0) { + os::free(_immutable_data); + } + _immutable_data = blob_end(); // Valid not null address } + if (unregister_nmethod) { Universe::heap()->unregister_nmethod(this); } @@ -2693,15 +2974,6 @@ void nmethod::copy_scopes_pcs(PcDesc* pcs, int count) { "must end with a sentinel"); #endif //ASSERT - // Search for MethodHandle invokes and tag the nmethod. - for (int i = 0; i < count; i++) { - if (pcs[i].is_method_handle_invoke()) { - set_has_method_handle_invokes(true); - break; - } - } - assert(has_method_handle_invokes() == (_deopt_mh_handler_offset != -1), "must have deopt mh handler"); - int size = count * sizeof(PcDesc); assert(scopes_pcs_size() >= size, "oob"); memcpy(scopes_pcs_begin(), pcs, size); @@ -3711,7 +3983,6 @@ const char* nmethod::nmethod_section_label(address pos) const { if (pos == code_begin()) label = "[Instructions begin]"; if (pos == entry_point()) label = "[Entry Point]"; if (pos == verified_entry_point()) label = "[Verified Entry Point]"; - if (has_method_handle_invokes() && (pos == deopt_mh_handler_begin())) label = "[Deopt MH Handler Code]"; if (pos == consts_begin() && pos != insts_begin()) label = "[Constants]"; // Check stub_code before checking exception_handler or deopt_handler. if (pos == this->stub_begin()) label = "[Stub Code]"; diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp index 8dfccb07891..2332766a47c 100644 --- a/src/hotspot/share/code/nmethod.hpp +++ b/src/hotspot/share/code/nmethod.hpp @@ -154,6 +154,7 @@ public: // - Scopes data array // - Scopes pcs array // - JVMCI speculations array +// - Nmethod reference counter #if INCLUDE_JVMCI class FailedSpeculation; @@ -167,6 +168,8 @@ class nmethod : public CodeBlob { friend class JVMCINMethodData; friend class DeoptimizationScope; + #define ImmutableDataReferencesCounterSize ((int)sizeof(int)) + private: // Used to track in which deoptimize handshake this method will be deoptimized. @@ -226,9 +229,6 @@ class nmethod : public CodeBlob { // All deoptee's will resume execution at this location described by // this offset. int _deopt_handler_offset; - // All deoptee's at a MethodHandle call site will resume execution - // at this location described by this offset. - int _deopt_mh_handler_offset; // Offset (from insts_end) of the unwind handler if it exists int16_t _unwind_handler_offset; // Number of arguments passed on the stack @@ -267,7 +267,6 @@ class nmethod : public CodeBlob { // set during construction uint8_t _has_unsafe_access:1, // May fault due to unsafe access. - _has_method_handle_invokes:1,// Has this method MethodHandle invokes? _has_wide_vectors:1, // Preserve wide vectors at safepoints _has_monitors:1, // Fastpath monitor detection for continuations _has_scoped_access:1, // used by for shared scope closure (scopedMemoryAccess.cpp) @@ -334,8 +333,11 @@ class nmethod : public CodeBlob { #endif ); + nmethod(const nmethod &nm); + // helper methods void* operator new(size_t size, int nmethod_size, int comp_level) throw(); + void* operator new(size_t size, int nmethod_size, CodeBlobType code_blob_type) throw(); // For method handle intrinsics: Try MethodNonProfiled, MethodProfiled and NonNMethod. // Attention: Only allow NonNMethod space for special nmethods which don't need to be @@ -568,6 +570,12 @@ public: #endif ); + // Relocate the nmethod to the code heap identified by code_blob_type. + // Returns nullptr if the code heap does not have enough space, the + // nmethod is unrelocatable, or the nmethod is invalidated during relocation, + // otherwise the relocated nmethod. The original nmethod will be marked not entrant. + nmethod* relocate(CodeBlobType code_blob_type); + static nmethod* new_native_nmethod(const methodHandle& method, int compile_id, CodeBuffer *code_buffer, @@ -584,6 +592,8 @@ public: bool is_java_method () const { return _method != nullptr && !_method->is_native(); } bool is_osr_method () const { return _entry_bci != InvocationEntryBci; } + bool is_relocatable(); + // Compiler task identification. Note that all OSR methods // are numbered in an independent sequence if CICountOSR is true, // and native method wrappers are also numbered independently if @@ -606,7 +616,6 @@ public: address stub_end () const { return code_end() ; } address exception_begin () const { return header_begin() + _exception_offset ; } address deopt_handler_begin () const { return header_begin() + _deopt_handler_offset ; } - address deopt_mh_handler_begin() const { return header_begin() + _deopt_mh_handler_offset ; } address unwind_handler_begin () const { return _unwind_handler_offset != -1 ? (insts_end() - _unwind_handler_offset) : nullptr; } oop* oops_begin () const { return (oop*) data_begin(); } oop* oops_end () const { return (oop*) data_end(); } @@ -637,11 +646,13 @@ public: #if INCLUDE_JVMCI address scopes_data_end () const { return _immutable_data + _speculations_offset ; } address speculations_begin () const { return _immutable_data + _speculations_offset ; } - address speculations_end () const { return immutable_data_end(); } + address speculations_end () const { return immutable_data_end() - ImmutableDataReferencesCounterSize ; } #else - address scopes_data_end () const { return immutable_data_end(); } + address scopes_data_end () const { return immutable_data_end() - ImmutableDataReferencesCounterSize ; } #endif + address immutable_data_references_counter_begin () const { return immutable_data_end() - ImmutableDataReferencesCounterSize ; } + // Sizes int immutable_data_size() const { return _immutable_data_size; } int consts_size () const { return int( consts_end () - consts_begin ()); } @@ -745,9 +756,6 @@ public: bool has_scoped_access() const { return _has_scoped_access; } void set_has_scoped_access(bool z) { _has_scoped_access = z; } - bool has_method_handle_invokes() const { return _has_method_handle_invokes; } - void set_has_method_handle_invokes(bool z) { _has_method_handle_invokes = z; } - bool has_wide_vectors() const { return _has_wide_vectors; } void set_has_wide_vectors(bool z) { _has_wide_vectors = z; } @@ -818,12 +826,9 @@ public: ExceptionCache* exception_cache_entry_for_exception(Handle exception); - // MethodHandle - bool is_method_handle_return(address return_pc); // Deopt // Return true is the PC is one would expect if the frame is being deopted. inline bool is_deopt_pc(address pc); - inline bool is_deopt_mh_entry(address pc); inline bool is_deopt_entry(address pc); // Accessor/mutator for the original pc of a frame before a frame was deopted. @@ -957,6 +962,9 @@ public: bool load_reported() const { return _load_reported; } void set_load_reported() { _load_reported = true; } + inline int get_immutable_data_references_counter() { return *((int*)immutable_data_references_counter_begin()); } + inline void set_immutable_data_references_counter(int count) { *((int*)immutable_data_references_counter_begin()) = count; } + public: // ScopeDesc retrieval operation PcDesc* pc_desc_at(address pc) { return find_pc_desc(pc, false); } @@ -1025,6 +1033,7 @@ public: // Logging void log_identity(xmlStream* log) const; void log_new_nmethod() const; + void log_relocated_nmethod(nmethod* original) const; void log_state_change(InvalidationReason invalidation_reason) const; // Prints block-level comments, including nmethod specific block labels: diff --git a/src/hotspot/share/code/nmethod.inline.hpp b/src/hotspot/share/code/nmethod.inline.hpp index 62c8eb723ea..44331db669c 100644 --- a/src/hotspot/share/code/nmethod.inline.hpp +++ b/src/hotspot/share/code/nmethod.inline.hpp @@ -31,16 +31,12 @@ #include "runtime/atomicAccess.hpp" #include "runtime/frame.hpp" -inline bool nmethod::is_deopt_pc(address pc) { return is_deopt_entry(pc) || is_deopt_mh_entry(pc); } +inline bool nmethod::is_deopt_pc(address pc) { return is_deopt_entry(pc); } inline bool nmethod::is_deopt_entry(address pc) { return pc == deopt_handler_begin(); } -inline bool nmethod::is_deopt_mh_entry(address pc) { - return pc == deopt_mh_handler_begin(); -} - // class ExceptionCache methods inline int ExceptionCache::count() { return AtomicAccess::load_acquire(&_count); } diff --git a/src/hotspot/share/code/pcDesc.hpp b/src/hotspot/share/code/pcDesc.hpp index 8048c3909c7..d8abe19ad35 100644 --- a/src/hotspot/share/code/pcDesc.hpp +++ b/src/hotspot/share/code/pcDesc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,11 +40,10 @@ class PcDesc { enum { PCDESC_reexecute = 1 << 0, - PCDESC_is_method_handle_invoke = 1 << 1, - PCDESC_return_oop = 1 << 2, - PCDESC_rethrow_exception = 1 << 3, - PCDESC_has_ea_local_in_scope = 1 << 4, - PCDESC_arg_escape = 1 << 5 + PCDESC_return_oop = 1 << 1, + PCDESC_rethrow_exception = 1 << 2, + PCDESC_has_ea_local_in_scope = 1 << 3, + PCDESC_arg_escape = 1 << 4 }; int _flags; @@ -85,9 +84,6 @@ class PcDesc { _flags == pd->_flags; } - bool is_method_handle_invoke() const { return (_flags & PCDESC_is_method_handle_invoke) != 0; } - void set_is_method_handle_invoke(bool z) { set_flag(PCDESC_is_method_handle_invoke, z); } - bool return_oop() const { return (_flags & PCDESC_return_oop) != 0; } void set_return_oop(bool z) { set_flag(PCDESC_return_oop, z); } diff --git a/src/hotspot/share/code/relocInfo.cpp b/src/hotspot/share/code/relocInfo.cpp index 8fc22596d01..02a1e5faf16 100644 --- a/src/hotspot/share/code/relocInfo.cpp +++ b/src/hotspot/share/code/relocInfo.cpp @@ -406,11 +406,12 @@ void CallRelocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffer pd_set_call_destination(callee); } - #ifdef USE_TRAMPOLINE_STUB_FIX_OWNER void trampoline_stub_Relocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) { // Finalize owner destination only for nmethods if (dest->blob() != nullptr) return; + // We either relocate a nmethod residing in CodeCache or just generated code from CodeBuffer + assert(src->blob() == nullptr || nativeCall_at(owner())->raw_destination() == owner(), "destination should be empty"); pd_fix_owner_after_move(); } #endif diff --git a/src/hotspot/share/compiler/oopMap.cpp b/src/hotspot/share/compiler/oopMap.cpp index aa010872975..87467d06400 100644 --- a/src/hotspot/share/compiler/oopMap.cpp +++ b/src/hotspot/share/compiler/oopMap.cpp @@ -862,6 +862,12 @@ ImmutableOopMapSet* ImmutableOopMapSet::build_from(const OopMapSet* oopmap_set) return builder.build(); } +ImmutableOopMapSet* ImmutableOopMapSet::clone() const { + address buffer = NEW_C_HEAP_ARRAY(unsigned char, _size, mtCode); + memcpy(buffer, (address)this, _size); + return (ImmutableOopMapSet*)buffer; +} + void ImmutableOopMapSet::operator delete(void* p) { FREE_C_HEAP_ARRAY(unsigned char, p); } diff --git a/src/hotspot/share/compiler/oopMap.hpp b/src/hotspot/share/compiler/oopMap.hpp index ef8845ac9aa..f7a8cd8496c 100644 --- a/src/hotspot/share/compiler/oopMap.hpp +++ b/src/hotspot/share/compiler/oopMap.hpp @@ -348,6 +348,8 @@ public: static ImmutableOopMapSet* build_from(const OopMapSet* oopmap_set); + ImmutableOopMapSet* clone() const; + int find_slot_for_offset(int pc_offset) const; const ImmutableOopMap* find_map_at_offset(int pc_offset) const; const ImmutableOopMap* find_map_at_slot(int slot, int pc_offset) const; diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 2368b5b4fc4..bf254b4aab3 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -64,7 +64,6 @@ #include "gc/g1/g1RemSet.hpp" #include "gc/g1/g1ReviseYoungLengthTask.hpp" #include "gc/g1/g1RootClosures.hpp" -#include "gc/g1/g1RootProcessor.hpp" #include "gc/g1/g1SATBMarkQueueSet.hpp" #include "gc/g1/g1ServiceThread.hpp" #include "gc/g1/g1ThreadLocalData.hpp" @@ -2560,6 +2559,7 @@ void G1CollectedHeap::verify_after_young_collection(G1HeapVerifier::G1VerifyType log_debug(gc, verify)("Marking state"); _verifier->verify_marking_state(); } + _verifier->verify_free_regions_card_tables_clean(); phase_times()->record_verify_after_time_ms((Ticks::now() - start).seconds() * MILLIUNITS); } @@ -2669,8 +2669,7 @@ void G1CollectedHeap::do_collection_pause_at_safepoint_helper(size_t allocation_ } void G1CollectedHeap::complete_cleaning(bool class_unloading_occurred) { - uint num_workers = workers()->active_workers(); - G1ParallelCleaningTask unlink_task(num_workers, class_unloading_occurred); + G1ParallelCleaningTask unlink_task(class_unloading_occurred); workers()->run_task(&unlink_task); } diff --git a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp index 21b3545f7e0..a2a9bc8e857 100644 --- a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp +++ b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp @@ -343,7 +343,7 @@ void G1HeapVerifier::verify(VerifyOption vo) { G1VerifyCodeRootNMethodClosure blobsCl(&codeRootsCl); { - G1RootProcessor root_processor(_g1h, 1); + G1RootProcessor root_processor(_g1h, false /* is_parallel */); root_processor.process_all_roots(&rootsCl, &cldCl, &blobsCl); } @@ -669,6 +669,23 @@ void G1HeapVerifier::verify_card_tables_in_sync() { Threads::java_threads_do(&check_same_cl); } +void G1HeapVerifier::verify_free_regions_card_tables_clean() { + class G1VerifyFreeRegionsCleanClosure : public G1HeapRegionClosure { + private: + G1HeapVerifier* _verifier; + public: + G1VerifyFreeRegionsCleanClosure(G1HeapVerifier* verifier) : G1HeapRegionClosure(), _verifier(verifier) { } + virtual bool do_heap_region(G1HeapRegion* r) { + if (r->is_free()) { + _verifier->verify_ct_clean_region(r); + _verifier->verify_rt_clean_region(r); + } + return false; + } + } cl(this); + _g1h->heap_region_iterate(&cl); +} + class G1CheckRegionAttrTableClosure : public G1HeapRegionClosure { private: bool _failures; diff --git a/src/hotspot/share/gc/g1/g1HeapVerifier.hpp b/src/hotspot/share/gc/g1/g1HeapVerifier.hpp index 6a26c77ec0d..55f6646563f 100644 --- a/src/hotspot/share/gc/g1/g1HeapVerifier.hpp +++ b/src/hotspot/share/gc/g1/g1HeapVerifier.hpp @@ -88,6 +88,9 @@ public: // Verify that the global card table and the thread's card tables are in sync. void verify_card_tables_in_sync() PRODUCT_RETURN; + + // Verify that free regions's card table is all-clean. + void verify_free_regions_card_tables_clean() PRODUCT_RETURN; }; #endif // SHARE_GC_G1_G1HEAPVERIFIER_HPP diff --git a/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp b/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp index 87e3a1cc7c4..11d3ce71517 100644 --- a/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp +++ b/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp @@ -146,8 +146,9 @@ inline void G1ConcurrentRefineOopClosure::do_oop_work(T* p) { // reload the values things may have changed. // Also this check lets slip through references from a humongous continues region // to its humongous start region, as they are in different regions, and adds a - // remembered set entry. This is benign (apart from memory usage), as we never - // try to either evacuate or eager reclaim humonguous arrays of j.l.O. + // remembered set entry. + // This does not affect correctness, but can prevent eager reclaim of humongous + // j.l.O. arrays. return; } diff --git a/src/hotspot/share/gc/g1/g1ParallelCleaning.cpp b/src/hotspot/share/gc/g1/g1ParallelCleaning.cpp index 884a2b2ca55..8d5e2a3239c 100644 --- a/src/hotspot/share/gc/g1/g1ParallelCleaning.cpp +++ b/src/hotspot/share/gc/g1/g1ParallelCleaning.cpp @@ -50,11 +50,10 @@ void JVMCICleaningTask::work(bool unloading_occurred) { } #endif // INCLUDE_JVMCI -G1ParallelCleaningTask::G1ParallelCleaningTask(uint num_workers, - bool unloading_occurred) : +G1ParallelCleaningTask::G1ParallelCleaningTask(bool unloading_occurred) : WorkerTask("G1 Parallel Cleaning"), _unloading_occurred(unloading_occurred), - _code_cache_task(num_workers, unloading_occurred), + _code_cache_task(unloading_occurred), JVMCI_ONLY(_jvmci_cleaning_task() COMMA) _klass_cleaning_task() { } diff --git a/src/hotspot/share/gc/g1/g1ParallelCleaning.hpp b/src/hotspot/share/gc/g1/g1ParallelCleaning.hpp index df9b1dddf6c..1a4e4601827 100644 --- a/src/hotspot/share/gc/g1/g1ParallelCleaning.hpp +++ b/src/hotspot/share/gc/g1/g1ParallelCleaning.hpp @@ -54,8 +54,7 @@ private: public: // The constructor is run in the VMThread. - G1ParallelCleaningTask(uint num_workers, - bool unloading_occurred); + G1ParallelCleaningTask(bool unloading_occurred); void work(uint worker_id); }; diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index 16eef67d1fc..f0bacefd71c 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -219,7 +219,7 @@ class G1ClearCardTableTask : public G1AbstractSubTask { // The card table contains "dirty" card marks. Clear unconditionally. // // Humongous reclaim candidates are not in the dirty set. This is fine because - // their card and refinement table should always be clear as they are typeArrays. + // we clean their card and refinement tables when we reclaim separately. r->clear_card_table(); // There is no need to clear the refinement table here: at the start of the collection // we had to clear the refinement card table for collection set regions already, and any diff --git a/src/hotspot/share/gc/g1/g1RemSetTrackingPolicy.cpp b/src/hotspot/share/gc/g1/g1RemSetTrackingPolicy.cpp index 7b8d92b8fe4..0c9973c520d 100644 --- a/src/hotspot/share/gc/g1/g1RemSetTrackingPolicy.cpp +++ b/src/hotspot/share/gc/g1/g1RemSetTrackingPolicy.cpp @@ -53,10 +53,10 @@ bool G1RemSetTrackingPolicy::update_humongous_before_rebuild(G1HeapRegion* r) { assert(!r->rem_set()->is_updating(), "Remembered set of region %u is updating before rebuild", r->hrm_index()); bool selected_for_rebuild = false; - // Humongous regions containing type-array objs are remset-tracked to - // support eager-reclaim. However, their remset state can be reset after - // Full-GC. Try to re-enable remset-tracking for them if possible. - if (cast_to_oop(r->bottom())->is_typeArray() && !r->rem_set()->is_tracked()) { + // Humongous regions are remset-tracked to support eager-reclaim. However, their + // remset state can be reset after Full-GC. Try to re-enable remset-tracking for + // them if possible. + if (!r->rem_set()->is_tracked()) { auto on_humongous_region = [] (G1HeapRegion* r) { r->rem_set()->set_state_updating(); }; diff --git a/src/hotspot/share/gc/g1/g1RootProcessor.cpp b/src/hotspot/share/gc/g1/g1RootProcessor.cpp index cf28c53648d..dac237cb277 100644 --- a/src/hotspot/share/gc/g1/g1RootProcessor.cpp +++ b/src/hotspot/share/gc/g1/g1RootProcessor.cpp @@ -46,10 +46,12 @@ #include "utilities/enumIterator.hpp" #include "utilities/macros.hpp" -G1RootProcessor::G1RootProcessor(G1CollectedHeap* g1h, uint n_workers) : +G1RootProcessor::G1RootProcessor(G1CollectedHeap* g1h, bool is_parallel) : _g1h(g1h), _process_strong_tasks(G1RP_PS_NumElements), - _srs(n_workers) {} + _nmethod_marking_scope(), + _threads_claim_token_scope(), + _is_parallel(is_parallel) {} void G1RootProcessor::evacuate_roots(G1ParScanThreadState* pss, uint worker_id) { G1GCPhaseTimes* phase_times = _g1h->phase_times(); @@ -175,8 +177,7 @@ void G1RootProcessor::process_java_roots(G1RootClosures* closures, // oops_do_process_weak and oops_do_process_strong in nmethod.hpp { G1GCParPhaseTimesTracker x(phase_times, G1GCPhaseTimes::ThreadRoots, worker_id); - bool is_par = n_workers() > 1; - Threads::possibly_parallel_oops_do(is_par, + Threads::possibly_parallel_oops_do(_is_parallel, closures->strong_oops(), closures->strong_nmethods()); } @@ -209,7 +210,3 @@ void G1RootProcessor::process_code_cache_roots(NMethodClosure* nmethod_closure, CodeCache::nmethods_do(nmethod_closure); } } - -uint G1RootProcessor::n_workers() const { - return _srs.n_threads(); -} diff --git a/src/hotspot/share/gc/g1/g1RootProcessor.hpp b/src/hotspot/share/gc/g1/g1RootProcessor.hpp index 2313fd62f20..27c92d730cf 100644 --- a/src/hotspot/share/gc/g1/g1RootProcessor.hpp +++ b/src/hotspot/share/gc/g1/g1RootProcessor.hpp @@ -25,10 +25,11 @@ #ifndef SHARE_GC_G1_G1ROOTPROCESSOR_HPP #define SHARE_GC_G1_G1ROOTPROCESSOR_HPP +#include "code/nmethod.hpp" #include "gc/shared/oopStorageSetParState.hpp" -#include "gc/shared/strongRootsScope.hpp" #include "memory/allocation.hpp" #include "runtime/mutex.hpp" +#include "runtime/threads.hpp" class CLDClosure; class G1CollectedHeap; @@ -48,7 +49,9 @@ class SubTasksDone; class G1RootProcessor : public StackObj { G1CollectedHeap* _g1h; SubTasksDone _process_strong_tasks; - StrongRootsScope _srs; + NMethodMarkingScope _nmethod_marking_scope; + ThreadsClaimTokenScope _threads_claim_token_scope; + bool _is_parallel; OopStorageSetStrongParState _oop_storage_set_strong_par_state; enum G1H_process_roots_tasks { @@ -72,7 +75,7 @@ class G1RootProcessor : public StackObj { uint worker_id); public: - G1RootProcessor(G1CollectedHeap* g1h, uint n_workers); + G1RootProcessor(G1CollectedHeap* g1h, bool is_parallel); // Apply correct closures from pss to the strongly and weakly reachable roots in the system // in a single pass. @@ -88,9 +91,6 @@ public: void process_all_roots(OopClosure* oops, CLDClosure* clds, NMethodClosure* nmethods); - - // Number of worker threads used by the root processor. - uint n_workers() const; }; #endif // SHARE_GC_G1_G1ROOTPROCESSOR_HPP diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index 6a7d4717a6f..a2a088ae47a 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -25,6 +25,7 @@ #include "classfile/classLoaderDataGraph.inline.hpp" #include "classfile/javaClasses.inline.hpp" +#include "code/nmethod.hpp" #include "compiler/oopMap.hpp" #include "gc/g1/g1Allocator.hpp" #include "gc/g1/g1CardSetMemory.hpp" @@ -340,23 +341,69 @@ class G1PrepareEvacuationTask : public WorkerTask { // structures don't support efficiently performing the needed // additional tests or scrubbing of the mark stack. // - // However, we presently only nominate is_typeArray() objects. - // A humongous object containing references induces remembered - // set entries on other regions. In order to reclaim such an - // object, those remembered sets would need to be cleaned up. + // We handle humongous objects specially, because frequent allocation and + // dropping of large binary blobs is an important use case for eager reclaim, + // and this special handling increases needed headroom. + // It also helps with G1 allocating humongous objects as old generation + // objects although they might also die quite quickly. // - // We also treat is_typeArray() objects specially, allowing them - // to be reclaimed even if allocated before the start of - // concurrent mark. For this we rely on mark stack insertion to - // exclude is_typeArray() objects, preventing reclaiming an object + // TypeArray objects are allowed to be reclaimed even if allocated before + // the start of concurrent mark. For this we rely on mark stack insertion + // to exclude is_typeArray() objects, preventing reclaiming an object // that is in the mark stack. We also rely on the metadata for // such objects to be built-in and so ensured to be kept live. - // Frequent allocation and drop of large binary blobs is an - // important use case for eager reclaim, and this special handling - // may reduce needed headroom. + // + // Non-typeArrays that were allocated before marking are excluded from + // eager reclaim during marking. One issue is the problem described + // above with scrubbing the mark stack, but there is also a problem + // causing these humongous objects being collected incorrectly: + // + // E.g. if the mutator is running, we may have objects o1 and o2 in the same + // region, where o1 has already been scanned and o2 is only reachable by + // the candidate object h, which is humongous. + // + // If the mutator read the reference to o2 from h and installed it into o1, + // no remembered set entry would be created for keeping alive o2, as o1 and + // o2 are in the same region. Object h might be reclaimed by the next + // garbage collection. o1 still has the reference to o2, but since o1 had + // already been scanned we do not detect o2 to be still live and reclaim it. + // + // There is another minor problem with non-typeArray regions being the source + // of remembered set entries in other region's remembered sets. There are + // two cases: first, the remembered set entry is in a Free region after reclaim. + // We handle this case by ignoring these cards during merging the remembered + // sets. + // + // Second, there may be cases where eagerly reclaimed regions were already + // reallocated. This may cause scanning of these outdated remembered set + // entries, containing some objects. But apart from extra work this does + // not cause correctness issues. + // There is no difference between scanning cards covering an effectively + // dead humongous object vs. some other objects in reallocated regions. + // + // TAMSes are only reset after completing the entire mark cycle, during + // bitmap clearing. It is worth to not wait until then, and allow reclamation + // outside of actual (concurrent) SATB marking. + // This also applies to the concurrent start pause - we only set + // mark_in_progress() at the end of that GC: no mutator is running that can + // sneakily install a new reference to the potentially reclaimed humongous + // object. + // During the concurrent start pause the situation described above where we + // miss a reference can not happen. No mutator is modifying the object + // graph to install such an overlooked reference. + // + // After the pause, having reclaimed h, obviously the mutator can't fetch + // the reference from h any more. + if (!obj->is_typeArray()) { + // All regions that were allocated before marking have a TAMS != bottom. + bool allocated_before_mark_start = region->bottom() != _g1h->concurrent_mark()->top_at_mark_start(region); + bool mark_in_progress = _g1h->collector_state()->mark_in_progress(); - return obj->is_typeArray() && - _g1h->is_potential_eager_reclaim_candidate(region); + if (allocated_before_mark_start && mark_in_progress) { + return false; + } + } + return _g1h->is_potential_eager_reclaim_candidate(region); } public: @@ -392,7 +439,7 @@ class G1PrepareEvacuationTask : public WorkerTask { _g1h->register_region_with_region_attr(hr); } log_debug(gc, humongous)("Humongous region %u (object size %zu @ " PTR_FORMAT ") remset %zu code roots %zu " - "marked %d pinned count %zu reclaim candidate %d type array %d", + "marked %d pinned count %zu reclaim candidate %d type %s", index, cast_to_oop(hr->bottom())->size() * HeapWordSize, p2i(hr->bottom()), @@ -401,7 +448,8 @@ class G1PrepareEvacuationTask : public WorkerTask { _g1h->concurrent_mark()->mark_bitmap()->is_marked(hr->bottom()), hr->pinned_count(), _g1h->is_humongous_reclaim_candidate(index), - cast_to_oop(hr->bottom())->is_typeArray() + cast_to_oop(hr->bottom())->is_typeArray() ? "tA" + : (cast_to_oop(hr->bottom())->is_objArray() ? "oA" : "ob") ); _worker_humongous_total++; @@ -750,7 +798,7 @@ void G1YoungCollector::evacuate_initial_collection_set(G1ParScanThreadStateSet* Ticks start_processing = Ticks::now(); { - G1RootProcessor root_processor(_g1h, num_workers); + G1RootProcessor root_processor(_g1h, num_workers > 1 /* is_parallel */); G1EvacuateRegionsTask g1_par_task(_g1h, per_thread_states, task_queues(), @@ -793,14 +841,11 @@ public: }; void G1YoungCollector::evacuate_next_optional_regions(G1ParScanThreadStateSet* per_thread_states) { - // To access the protected constructor/destructor - class G1MarkScope : public MarkScope { }; - Tickspan task_time; Ticks start_processing = Ticks::now(); { - G1MarkScope code_mark_scope; + NMethodMarkingScope nmethod_marking_scope; G1EvacuateOptionalRegionsTask task(per_thread_states, task_queues(), workers()->active_workers()); task_time = run_task_timed(&task); // See comment in evacuate_initial_collection_set() for the reason of the scope. diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp index 2737def7e84..d875d9c8b8a 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp @@ -350,11 +350,11 @@ class G1FreeHumongousRegionClosure : public G1HeapRegionIndexClosure { // // - if it has not been a candidate at the start of collection, it will never // changed to be a candidate during the gc (and live). - // - any found outstanding (i.e. in the DCQ, or in its remembered set) - // references will set the candidate state to false. + // - any found outstanding (i.e. in its remembered set, or from the collection + // set) references will set the candidate state to false. // - there can be no references from within humongous starts regions referencing // the object because we never allocate other objects into them. - // (I.e. there can be no intra-region references) + // (I.e. there can be no intra-region references within humongous objects) // // It is not required to check whether the object has been found dead by marking // or not, in fact it would prevent reclamation within a concurrent cycle, as @@ -363,16 +363,13 @@ class G1FreeHumongousRegionClosure : public G1HeapRegionIndexClosure { // So if at this point in the collection we did not find a reference during gc // (or it had enough references to not be a candidate, having many remembered // set entries), nobody has a reference to it. - // At the start of collection we flush all refinement logs, and remembered sets - // are completely up-to-date wrt to references to the humongous object. // - // So there is no need to re-check remembered set size of the humongous region. + // Since remembered sets are only ever updated by concurrent refinement threads + // at mutator time, the remembered sets do not need to be checked again. // // Other implementation considerations: - // - never consider object arrays at this time because they would pose - // considerable effort for cleaning up the remembered sets. This is - // required because stale remembered sets might reference locations that - // are currently allocated into. + // - never consider non-typeArrays during marking as there is a considerable cost + // for maintaining the SATB invariant. bool is_reclaimable(uint region_idx) const { return G1CollectedHeap::heap()->is_humongous_reclaim_candidate(region_idx); } @@ -393,10 +390,15 @@ public: G1HeapRegion* r = _g1h->region_at(region_index); oop obj = cast_to_oop(r->bottom()); - guarantee(obj->is_typeArray(), - "Only eagerly reclaiming type arrays is supported, but the object " - PTR_FORMAT " is not.", p2i(r->bottom())); - + { + ResourceMark rm; + bool allocated_after_mark_start = r->bottom() == _g1h->concurrent_mark()->top_at_mark_start(r); + bool mark_in_progress = _g1h->collector_state()->mark_in_progress(); + guarantee(obj->is_typeArray() || (allocated_after_mark_start || !mark_in_progress), + "Only eagerly reclaiming primitive arrays is supported, other humongous objects only if allocated after mark start, but the object " + PTR_FORMAT " (%s) is not (mark %d allocated after mark: %d).", + p2i(r->bottom()), obj->klass()->name()->as_C_string(), mark_in_progress, allocated_after_mark_start); + } log_debug(gc, humongous)("Reclaimed humongous region %u (object size %zu @ " PTR_FORMAT ")", region_index, obj->size() * HeapWordSize, @@ -416,6 +418,9 @@ public: r->set_containing_set(nullptr); _humongous_regions_reclaimed++; G1HeapRegionPrinter::eager_reclaim(r); + // Humongous non-typeArrays may have dirty card tables. Need to be cleared. Do it + // for all types just in case. + r->clear_both_card_tables(); _g1h->free_humongous_region(r, nullptr); }; diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index ae2c36e44c5..cbea75879ae 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -152,17 +152,6 @@ void ParallelScavengeHeap::initialize_serviceability() { } -void ParallelScavengeHeap::safepoint_synchronize_begin() { - if (UseStringDeduplication) { - SuspendibleThreadSet::synchronize(); - } -} - -void ParallelScavengeHeap::safepoint_synchronize_end() { - if (UseStringDeduplication) { - SuspendibleThreadSet::desynchronize(); - } -} class PSIsScavengable : public BoolObjectClosure { bool do_object_b(oop obj) { return ParallelScavengeHeap::heap()->is_in_young(obj); @@ -282,9 +271,23 @@ HeapWord* ParallelScavengeHeap::mem_allocate_cas_noexpand(size_t size, bool is_t return result; } - // Try allocating from the old gen for non-TLAB in certain scenarios. + // Try allocating from the old gen for non-TLAB and large allocations. if (!is_tlab) { - if (!should_alloc_in_eden(size) || _is_heap_almost_full) { + if (!should_alloc_in_eden(size)) { + result = old_gen()->cas_allocate_noexpand(size); + if (result != nullptr) { + return result; + } + } + } + + // In extreme cases, try allocating in from space also. + if (_is_heap_almost_full) { + result = young_gen()->from_space()->cas_allocate(size); + if (result != nullptr) { + return result; + } + if (!is_tlab) { result = old_gen()->cas_allocate_noexpand(size); if (result != nullptr) { return result; diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp index fea827430ca..bf777bda29e 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp @@ -159,9 +159,6 @@ public: // Returns JNI_OK on success jint initialize() override; - void safepoint_synchronize_begin() override; - void safepoint_synchronize_end() override; - void post_initialize() override; void update_counters(); diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp index af812c652a6..4cdd27cf746 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp @@ -28,6 +28,7 @@ #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" #include "code/codeCache.hpp" +#include "code/nmethod.hpp" #include "compiler/oopMap.hpp" #include "gc/parallel/objectStartArray.inline.hpp" #include "gc/parallel/parallelArguments.hpp" @@ -56,12 +57,12 @@ #include "gc/shared/oopStorage.inline.hpp" #include "gc/shared/oopStorageSet.inline.hpp" #include "gc/shared/oopStorageSetParState.inline.hpp" +#include "gc/shared/parallelCleaning.hpp" #include "gc/shared/preservedMarks.inline.hpp" #include "gc/shared/referencePolicy.hpp" #include "gc/shared/referenceProcessor.hpp" #include "gc/shared/referenceProcessorPhaseTimes.hpp" #include "gc/shared/spaceDecorator.hpp" -#include "gc/shared/strongRootsScope.hpp" #include "gc/shared/taskTerminator.hpp" #include "gc/shared/weakProcessor.inline.hpp" #include "gc/shared/workerPolicy.hpp" @@ -1085,7 +1086,8 @@ void steal_marking_work(TaskTerminator& terminator, uint worker_id) { } class MarkFromRootsTask : public WorkerTask { - StrongRootsScope _strong_roots_scope; // needed for Threads::possibly_parallel_threads_do + NMethodMarkingScope _nmethod_marking_scope; + ThreadsClaimTokenScope _threads_claim_token_scope; OopStorageSetStrongParState _oop_storage_set_par_state; TaskTerminator _terminator; uint _active_workers; @@ -1093,7 +1095,8 @@ class MarkFromRootsTask : public WorkerTask { public: MarkFromRootsTask(uint active_workers) : WorkerTask("MarkFromRootsTask"), - _strong_roots_scope(active_workers), + _nmethod_marking_scope(), + _threads_claim_token_scope(), _terminator(active_workers, ParCompactionManager::marking_stacks()), _active_workers(active_workers) {} @@ -1154,6 +1157,40 @@ static void flush_marking_stats_cache(const uint num_workers) { } } +class PSParallelCleaningTask : public WorkerTask { + bool _unloading_occurred; + CodeCacheUnloadingTask _code_cache_task; + // Prune dead klasses from subklass/sibling/implementor lists. + KlassCleaningTask _klass_cleaning_task; + +public: + PSParallelCleaningTask(bool unloading_occurred) : + WorkerTask("PS Parallel Cleaning"), + _unloading_occurred(unloading_occurred), + _code_cache_task(unloading_occurred), + _klass_cleaning_task() {} + + void work(uint worker_id) { +#if INCLUDE_JVMCI + if (EnableJVMCI && worker_id == 0) { + // Serial work; only first worker. + // Clean JVMCI metadata handles. + JVMCI::do_unloading(_unloading_occurred); + } +#endif + + // Do first pass of code cache cleaning. + _code_cache_task.work(worker_id); + + // Clean all klasses that were not unloaded. + // The weak metadata in klass doesn't need to be + // processed if there was no unloading. + if (_unloading_occurred) { + _klass_cleaning_task.work(); + } + } +}; + void PSParallelCompact::marking_phase(ParallelOldTracer *gc_tracer) { // Recursively traverse all live objects and mark them GCTraceTime(Info, gc, phases) tm("Marking Phase", &_gc_timer); @@ -1202,19 +1239,18 @@ void PSParallelCompact::marking_phase(ParallelOldTracer *gc_tracer) { { GCTraceTime(Debug, gc, phases) tm_m("Class Unloading", &_gc_timer); - ClassUnloadingContext ctx(1 /* num_nmethod_unlink_workers */, + ClassUnloadingContext ctx(active_gc_threads /* num_nmethod_unlink_workers */, false /* unregister_nmethods_during_purge */, false /* lock_nmethod_free_separately */); - bool unloading_occurred; { CodeCache::UnlinkingScope scope(is_alive_closure()); // Follow system dictionary roots and unload classes. - unloading_occurred = SystemDictionary::do_unloading(&_gc_timer); + bool unloading_occurred = SystemDictionary::do_unloading(&_gc_timer); - // Unload nmethods. - CodeCache::do_unloading(unloading_occurred); + PSParallelCleaningTask task{unloading_occurred}; + ParallelScavengeHeap::heap()->workers().run_task(&task); } { @@ -1230,12 +1266,6 @@ void PSParallelCompact::marking_phase(ParallelOldTracer *gc_tracer) { GCTraceTime(Debug, gc, phases) t("Free Code Blobs", gc_timer()); ctx.free_nmethods(); } - - // Prune dead klasses from subklass/sibling/implementor lists. - Klass::clean_weak_klass_links(unloading_occurred); - - // Clean JVMCI metadata handles. - JVMCI_ONLY(JVMCI::do_unloading(unloading_occurred)); { // Delete metaspaces for unloaded class loaders and clean up loader_data graph GCTraceTime(Debug, gc, phases) t("Purge Class Loader Data", gc_timer()); @@ -1332,6 +1362,7 @@ void PSParallelCompact::adjust_pointers_in_spaces(uint worker_id, volatile uint* } class PSAdjustTask final : public WorkerTask { + ThreadsClaimTokenScope _threads_claim_token_scope; SubTasksDone _sub_tasks; WeakProcessor::Task _weak_proc_task; OopStorageSetStrongParState _oop_storage_iter; @@ -1347,16 +1378,12 @@ class PSAdjustTask final : public WorkerTask { public: PSAdjustTask(uint nworkers) : WorkerTask("PSAdjust task"), + _threads_claim_token_scope(), _sub_tasks(PSAdjustSubTask_num_elements), _weak_proc_task(nworkers), _nworkers(nworkers) { ClassLoaderDataGraph::verify_claimed_marks_cleared(ClassLoaderData::_claim_stw_fullgc_adjust); - Threads::change_thread_claim_token(); - } - - ~PSAdjustTask() { - Threads::assert_all_threads_claimed(); } void work(uint worker_id) { diff --git a/src/hotspot/share/gc/parallel/psPromotionManager.hpp b/src/hotspot/share/gc/parallel/psPromotionManager.hpp index 7d3a1682519..9808a55335d 100644 --- a/src/hotspot/share/gc/parallel/psPromotionManager.hpp +++ b/src/hotspot/share/gc/parallel/psPromotionManager.hpp @@ -102,8 +102,6 @@ class PSPromotionManager { void process_array_chunk(PartialArrayState* state, bool stolen); void push_objArray(oop old_obj, oop new_obj); - void push_depth(ScannerTask task); - inline void promotion_trace_event(oop new_obj, Klass* klass, size_t obj_size, uint age, bool tenured, const PSPromotionLAB* lab); diff --git a/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp b/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp index 4c12a4c357f..fb58c22cf29 100644 --- a/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp +++ b/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp @@ -50,10 +50,6 @@ inline PSPromotionManager* PSPromotionManager::manager_array(uint index) { return &_manager_array[index]; } -inline void PSPromotionManager::push_depth(ScannerTask task) { - claimed_stack_depth()->push(task); -} - template inline void PSPromotionManager::claim_or_forward_depth(T* p) { assert(ParallelScavengeHeap::heap()->is_in(p), "pointer outside heap"); @@ -62,7 +58,7 @@ inline void PSPromotionManager::claim_or_forward_depth(T* p) { oop obj = CompressedOops::decode_not_null(heap_oop); assert(!PSScavenge::is_obj_in_to_space(obj), "revisiting object?"); Prefetch::write(obj->base_addr(), oopDesc::mark_offset_in_bytes()); - push_depth(ScannerTask(p)); + claimed_stack_depth()->push(ScannerTask(p)); } } diff --git a/src/hotspot/share/gc/parallel/psYoungGen.cpp b/src/hotspot/share/gc/parallel/psYoungGen.cpp index 4fbe314d14c..c26fdf4740c 100644 --- a/src/hotspot/share/gc/parallel/psYoungGen.cpp +++ b/src/hotspot/share/gc/parallel/psYoungGen.cpp @@ -250,6 +250,12 @@ void PSYoungGen::space_invariants() { bool PSYoungGen::try_expand_to_hold(size_t word_size) { assert(eden_space()->free_in_words() < word_size, "precondition"); + if (UseNUMA && !_eden_space->is_empty()) { + // Eden expansion is not supported with NUMA, when eden is not empty. + // See also MutableNUMASpace::initialize. + return false; + } + // For logging purpose size_t original_committed_size = virtual_space()->committed_size(); diff --git a/src/hotspot/share/gc/serial/defNewGeneration.cpp b/src/hotspot/share/gc/serial/defNewGeneration.cpp index 42f8b191f5e..5b7bc744236 100644 --- a/src/hotspot/share/gc/serial/defNewGeneration.cpp +++ b/src/hotspot/share/gc/serial/defNewGeneration.cpp @@ -45,7 +45,6 @@ #include "gc/shared/scavengableNMethods.hpp" #include "gc/shared/space.hpp" #include "gc/shared/spaceDecorator.hpp" -#include "gc/shared/strongRootsScope.hpp" #include "gc/shared/weakProcessor.hpp" #include "logging/log.hpp" #include "memory/iterator.inline.hpp" diff --git a/src/hotspot/share/gc/serial/serialHeap.cpp b/src/hotspot/share/gc/serial/serialHeap.cpp index dbd54c302ea..3ab88da4633 100644 --- a/src/hotspot/share/gc/serial/serialHeap.cpp +++ b/src/hotspot/share/gc/serial/serialHeap.cpp @@ -56,7 +56,6 @@ #include "gc/shared/oopStorageSet.inline.hpp" #include "gc/shared/scavengableNMethods.hpp" #include "gc/shared/space.hpp" -#include "gc/shared/strongRootsScope.hpp" #include "gc/shared/suspendibleThreadSet.hpp" #include "gc/shared/weakProcessor.hpp" #include "gc/shared/workerThread.hpp" @@ -145,18 +144,6 @@ GrowableArray SerialHeap::memory_pools() { return memory_pools; } -void SerialHeap::safepoint_synchronize_begin() { - if (UseStringDeduplication) { - SuspendibleThreadSet::synchronize(); - } -} - -void SerialHeap::safepoint_synchronize_end() { - if (UseStringDeduplication) { - SuspendibleThreadSet::desynchronize(); - } -} - HeapWord* SerialHeap::allocate_loaded_archive_space(size_t word_size) { MutexLocker ml(Heap_lock); return old_gen()->allocate(word_size); diff --git a/src/hotspot/share/gc/serial/serialHeap.hpp b/src/hotspot/share/gc/serial/serialHeap.hpp index 388da13b1b0..27053b4cc81 100644 --- a/src/hotspot/share/gc/serial/serialHeap.hpp +++ b/src/hotspot/share/gc/serial/serialHeap.hpp @@ -258,9 +258,6 @@ public: void scan_evacuated_objs(YoungGenScanClosure* young_cl, OldGenScanClosure* old_cl); - void safepoint_synchronize_begin() override; - void safepoint_synchronize_end() override; - // Support for loading objects from CDS archive into the heap bool can_load_archived_objects() const override { return true; } HeapWord* allocate_loaded_archive_space(size_t size) override; diff --git a/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp b/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp index ac640fb88d2..a31078f7e67 100644 --- a/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp +++ b/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp @@ -38,6 +38,16 @@ #define __ gen->lir()-> #endif +// Return true iff an access to bt is single-copy atomic. + +// The JMM requires atomicity for all accesses to fields of primitive +// types other than double and long. In practice, HotSpot assumes that +// on all processors, accesses to memory operands of wordSize and +// smaller are atomic. +static bool access_is_atomic(BasicType bt) { + return type2aelembytes(bt) <= wordSize; +} + LIR_Opr BarrierSetC1::resolve_address(LIRAccess& access, bool resolve_in_register) { DecoratorSet decorators = access.decorators(); bool is_array = (decorators & IS_ARRAY) != 0; @@ -140,7 +150,7 @@ LIR_Opr BarrierSetC1::atomic_add_at(LIRAccess& access, LIRItem& value) { void BarrierSetC1::store_at_resolved(LIRAccess& access, LIR_Opr value) { DecoratorSet decorators = access.decorators(); bool is_volatile = (decorators & MO_SEQ_CST) != 0; - bool is_atomic = is_volatile || AlwaysAtomicAccesses; + bool needs_atomic = AlwaysAtomicAccesses && !access_is_atomic(value->type()); bool needs_patching = (decorators & C1_NEEDS_PATCHING) != 0; bool mask_boolean = (decorators & C1_MASK_BOOLEAN) != 0; LIRGenerator* gen = access.gen(); @@ -154,7 +164,7 @@ void BarrierSetC1::store_at_resolved(LIRAccess& access, LIR_Opr value) { } LIR_PatchCode patch_code = needs_patching ? lir_patch_normal : lir_patch_none; - if (is_atomic && !needs_patching) { + if ((is_volatile || needs_atomic) && !needs_patching) { gen->volatile_field_store(value, access.resolved_addr()->as_address_ptr(), access.access_emit_info()); } else { __ store(value, access.resolved_addr()->as_address_ptr(), access.access_emit_info(), patch_code); @@ -169,7 +179,7 @@ void BarrierSetC1::load_at_resolved(LIRAccess& access, LIR_Opr result) { LIRGenerator *gen = access.gen(); DecoratorSet decorators = access.decorators(); bool is_volatile = (decorators & MO_SEQ_CST) != 0; - bool is_atomic = is_volatile || AlwaysAtomicAccesses; + bool needs_atomic = AlwaysAtomicAccesses && !access_is_atomic(result->type()); bool needs_patching = (decorators & C1_NEEDS_PATCHING) != 0; bool mask_boolean = (decorators & C1_MASK_BOOLEAN) != 0; bool in_native = (decorators & IN_NATIVE) != 0; @@ -181,7 +191,7 @@ void BarrierSetC1::load_at_resolved(LIRAccess& access, LIR_Opr result) { LIR_PatchCode patch_code = needs_patching ? lir_patch_normal : lir_patch_none; if (in_native) { __ move_wide(access.resolved_addr()->as_address_ptr(), result); - } else if (is_atomic && !needs_patching) { + } else if ((is_volatile || needs_atomic) && !needs_patching) { gen->volatile_field_load(access.resolved_addr()->as_address_ptr(), result, access.access_emit_info()); } else { __ load(access.resolved_addr()->as_address_ptr(), result, access.access_emit_info(), patch_code); diff --git a/src/hotspot/share/gc/shared/parallelCleaning.cpp b/src/hotspot/share/gc/shared/parallelCleaning.cpp index d2f69bfa679..e302085d0cc 100644 --- a/src/hotspot/share/gc/shared/parallelCleaning.cpp +++ b/src/hotspot/share/gc/shared/parallelCleaning.cpp @@ -30,9 +30,8 @@ #include "oops/klass.inline.hpp" #include "runtime/atomicAccess.hpp" -CodeCacheUnloadingTask::CodeCacheUnloadingTask(uint num_workers, bool unloading_occurred) : +CodeCacheUnloadingTask::CodeCacheUnloadingTask(bool unloading_occurred) : _unloading_occurred(unloading_occurred), - _num_workers(num_workers), _first_nmethod(nullptr), _claimed_nmethod(nullptr) { // Get first alive nmethod diff --git a/src/hotspot/share/gc/shared/parallelCleaning.hpp b/src/hotspot/share/gc/shared/parallelCleaning.hpp index b47bf92e2ac..e1b07f5778d 100644 --- a/src/hotspot/share/gc/shared/parallelCleaning.hpp +++ b/src/hotspot/share/gc/shared/parallelCleaning.hpp @@ -33,14 +33,13 @@ class CodeCacheUnloadingTask { const bool _unloading_occurred; - const uint _num_workers; // Variables used to claim nmethods. nmethod* _first_nmethod; nmethod* volatile _claimed_nmethod; public: - CodeCacheUnloadingTask(uint num_workers, bool unloading_occurred); + CodeCacheUnloadingTask(bool unloading_occurred); ~CodeCacheUnloadingTask(); private: diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index 6d3b93ac406..456b9fe6d3c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -1158,7 +1158,7 @@ void ShenandoahConcurrentGC::op_final_update_refs() { // Clear cancelled GC, if set. On cancellation path, the block before would handle // everything. if (heap->cancelled_gc()) { - heap->clear_cancelled_gc(true /* clear oom handler */); + heap->clear_cancelled_gc(); } // Has to be done before cset is clear diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index b960255891f..590e7831f07 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -43,7 +43,8 @@ ShenandoahControlThread::ShenandoahControlThread() : ShenandoahController(), _requested_gc_cause(GCCause::_no_gc), - _degen_point(ShenandoahGC::_degenerated_outside_cycle) { + _degen_point(ShenandoahGC::_degenerated_outside_cycle), + _control_lock(Mutex::nosafepoint - 2, "ShenandoahGCRequest_lock", true) { set_name("Shenandoah Control Thread"); create_and_start(); } @@ -228,7 +229,9 @@ void ShenandoahControlThread::run_service() { sleep = MIN2(ShenandoahControlIntervalMax, MAX2(1, sleep * 2)); last_sleep_adjust_time = current; } - os::naked_short_sleep(sleep); + + MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag); + ml.wait(sleep); } } @@ -343,6 +346,16 @@ void ShenandoahControlThread::request_gc(GCCause::Cause cause) { } } +void ShenandoahControlThread::notify_control_thread(GCCause::Cause cause) { + // Although setting gc request is under _controller_lock, the read side (run_service()) + // does not take the lock. We need to enforce following order, so that read side sees + // latest requested gc cause when the flag is set. + MonitorLocker controller(&_control_lock, Mutex::_no_safepoint_check_flag); + _requested_gc_cause = cause; + _gc_requested.set(); + controller.notify(); +} + void ShenandoahControlThread::handle_requested_gc(GCCause::Cause cause) { if (should_terminate()) { log_info(gc)("Control thread is terminating, no more GCs"); @@ -354,8 +367,7 @@ void ShenandoahControlThread::handle_requested_gc(GCCause::Cause cause) { // The whitebox caller thread will arrange for itself to wait until the GC notifies // it that has reached the requested breakpoint (phase in the GC). if (cause == GCCause::_wb_breakpoint) { - _requested_gc_cause = cause; - _gc_requested.set(); + notify_control_thread(cause); return; } @@ -372,12 +384,7 @@ void ShenandoahControlThread::handle_requested_gc(GCCause::Cause cause) { size_t current_gc_id = get_gc_id(); size_t required_gc_id = current_gc_id + 1; while (current_gc_id < required_gc_id && !should_terminate()) { - // Although setting gc request is under _gc_waiters_lock, but read side (run_service()) - // does not take the lock. We need to enforce following order, so that read side sees - // latest requested gc cause when the flag is set. - _requested_gc_cause = cause; - _gc_requested.set(); - + notify_control_thread(cause); ml.wait(); current_gc_id = get_gc_id(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.hpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.hpp index 4975404bbaa..c9bb6419201 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.hpp @@ -45,6 +45,9 @@ private: GCCause::Cause _requested_gc_cause; ShenandoahGC::ShenandoahDegenPoint _degen_point; + // This lock is used to coordinate waking up the control thread + Monitor _control_lock; + public: ShenandoahControlThread(); @@ -54,6 +57,8 @@ public: void request_gc(GCCause::Cause cause) override; private: + // Sets the requested cause and flag and notifies the control thread + void notify_control_thread(GCCause::Cause cause); bool check_cancellation_or_degen(ShenandoahGC::ShenandoahDegenPoint point); void service_concurrent_normal_cycle(GCCause::Cause cause); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp index bbecd12f095..b918bf67b34 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp @@ -94,7 +94,7 @@ void ShenandoahDegenGC::op_degenerated() { // Degenerated GC is STW, but it can also fail. Current mechanics communicates // GC failure via cancelled_concgc() flag. So, if we detect the failure after // some phase, we have to upgrade the Degenerate GC to Full GC. - heap->clear_cancelled_gc(true /* clear oom handler */); + heap->clear_cancelled_gc(); #ifdef ASSERT if (heap->mode()->is_generational()) { @@ -115,8 +115,7 @@ void ShenandoahDegenGC::op_degenerated() { } #endif - ShenandoahMetricsSnapshot metrics; - metrics.snap_before(); + ShenandoahMetricsSnapshot metrics(heap->free_set()); switch (_degen_point) { // The cases below form the Duff's-like device: it describes the actual GC cycle, @@ -308,10 +307,8 @@ void ShenandoahDegenGC::op_degenerated() { Universe::verify(); } - metrics.snap_after(); - // Decide if this cycle made good progress, and, if not, should it upgrade to a full GC. - const bool progress = metrics.is_good_progress(_generation); + const bool progress = metrics.is_good_progress(); ShenandoahCollectorPolicy* policy = heap->shenandoah_policy(); policy->record_degenerated(_generation->is_young(), _abbreviated, progress); if (progress) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp index 27ff45e67de..2e486a23363 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp @@ -104,21 +104,18 @@ void ShenandoahFullGC::entry_full(GCCause::Cause cause) { } void ShenandoahFullGC::op_full(GCCause::Cause cause) { - ShenandoahMetricsSnapshot metrics; - metrics.snap_before(); + ShenandoahHeap* const heap = ShenandoahHeap::heap(); + + ShenandoahMetricsSnapshot metrics(heap->free_set()); // Perform full GC do_it(cause); - ShenandoahHeap* const heap = ShenandoahHeap::heap(); - if (heap->mode()->is_generational()) { ShenandoahGenerationalFullGC::handle_completion(heap); } - metrics.snap_after(); - - if (metrics.is_good_progress(heap->global_generation())) { + if (metrics.is_good_progress()) { heap->notify_gc_progress(); } else { // Nothing to do. Tell the allocation path that we have failed to make @@ -1168,7 +1165,7 @@ ShenandoahGenerationalHeap::TransferResult ShenandoahFullGC::phase5_epilog() { // Set mark incomplete because the marking bitmaps have been reset except pinned regions. heap->global_generation()->set_mark_incomplete(); - heap->clear_cancelled_gc(true /* clear oom handler */); + heap->clear_cancelled_gc(); } _preserved_marks->restore(heap->workers()); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index c6d1cf02ee2..88b3c086da0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -110,10 +110,9 @@ void ShenandoahGenerationalControlThread::check_for_request(ShenandoahGCRequest& // failure (degenerated cycle), or old marking was cancelled to run a young collection. // In either case, the correct generation for the next cycle can be determined by // the cancellation cause. - request.cause = _heap->cancelled_cause(); + request.cause = _heap->clear_cancellation(GCCause::_shenandoah_concurrent_gc); if (request.cause == GCCause::_shenandoah_concurrent_gc) { request.generation = _heap->young_generation(); - _heap->clear_cancelled_gc(false); } } else { request.cause = _requested_gc_cause; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index e6d41237c98..b2fd32d2fd0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -2259,8 +2259,7 @@ void ShenandoahHeap::stw_unload_classes(bool full_gc) { // Clean JVMCI metadata handles. JVMCI_ONLY(JVMCI::do_unloading(unloading_occurred)); - uint num_workers = _workers->active_workers(); - ShenandoahClassUnloadingTask unlink_task(phase, num_workers, unloading_occurred); + ShenandoahClassUnloadingTask unlink_task(phase, unloading_occurred); _workers->run_task(&unlink_task); } // Release unloaded nmethods's memory. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index 11a20d4a2f9..8bcb04e5766 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -456,9 +456,12 @@ public: // This indicates the reason the last GC cycle was cancelled. inline GCCause::Cause cancelled_cause() const; - // Clears the cancellation cause and optionally resets the oom handler (cancelling an - // old mark does _not_ touch the oom handler). - inline void clear_cancelled_gc(bool clear_oom_handler = true); + // Clears the cancellation cause and resets the oom handler + inline void clear_cancelled_gc(); + + // Clears the cancellation cause iff the current cancellation reason equals the given + // expected cancellation cause. Does not reset the oom handler. + inline GCCause::Cause clear_cancellation(GCCause::Cause expected); void cancel_concurrent_mark(); @@ -475,6 +478,8 @@ protected: ShenandoahRegionIterator _update_refs_iterator; private: + inline void reset_cancellation_time(); + // GC support // Evacuation virtual void evacuate_collection_set(bool concurrent); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp index ca368f5ed29..fc6c24a8423 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp @@ -31,6 +31,7 @@ #include "classfile/javaClasses.inline.hpp" #include "gc/shared/continuationGCSupport.inline.hpp" +#include "gc/shared/gcCause.hpp" #include "gc/shared/markBitMap.inline.hpp" #include "gc/shared/suspendibleThreadSet.hpp" #include "gc/shared/threadLocalAllocBuffer.inline.hpp" @@ -268,16 +269,25 @@ inline GCCause::Cause ShenandoahHeap::cancelled_cause() const { return _cancelled_gc.get(); } -inline void ShenandoahHeap::clear_cancelled_gc(bool clear_oom_handler) { +inline void ShenandoahHeap::clear_cancelled_gc() { _cancelled_gc.set(GCCause::_no_gc); + reset_cancellation_time(); + _oom_evac_handler.clear(); +} + +inline GCCause::Cause ShenandoahHeap::clear_cancellation(const GCCause::Cause expected) { + const GCCause::Cause cancellation_cause = _cancelled_gc.cmpxchg(GCCause::_no_gc, expected); + if (cancellation_cause == expected) { + reset_cancellation_time(); + } + return cancellation_cause; +} + +inline void ShenandoahHeap::reset_cancellation_time() { if (_cancel_requested_time > 0) { log_debug(gc)("GC cancellation took %.3fs", (os::elapsedTime() - _cancel_requested_time)); _cancel_requested_time = 0; } - - if (clear_oom_handler) { - _oom_evac_handler.clear(); - } } inline HeapWord* ShenandoahHeap::allocate_from_gclab(Thread* thread, size_t size) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMetrics.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMetrics.cpp index dc666a34c59..d774a8dba42 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMetrics.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMetrics.cpp @@ -28,69 +28,45 @@ #include "gc/shenandoah/shenandoahHeapRegion.hpp" #include "gc/shenandoah/shenandoahMetrics.hpp" -ShenandoahMetricsSnapshot::ShenandoahMetricsSnapshot() { - _heap = ShenandoahHeap::heap(); +ShenandoahMetricsSnapshot::ShenandoahMetricsSnapshot(ShenandoahFreeSet* free_set) + : _free_set(free_set) + , _used_before(free_set->used()) + , _if_before(free_set->internal_fragmentation()) + , _ef_before(free_set->external_fragmentation()) { } -void ShenandoahMetricsSnapshot::snap_before() { - _used_before = _heap->used(); - _if_before = _heap->free_set()->internal_fragmentation(); - _ef_before = _heap->free_set()->external_fragmentation(); -} -void ShenandoahMetricsSnapshot::snap_after() { - _used_after = _heap->used(); - _if_after = _heap->free_set()->internal_fragmentation(); - _ef_after = _heap->free_set()->external_fragmentation(); -} - -// For degenerated GC, generation is Young in generational mode, Global in non-generational mode. -// For full GC, generation is always Global. -// -// Note that the size of the chosen collection set is proportional to the relevant generation's collection set. -// Note also that the generation size may change following selection of the collection set, as a side effect -// of evacuation. Evacuation may promote objects, causing old to grow and young to shrink. Or this may be a -// mixed evacuation. When old regions are evacuated, this typically allows young to expand. In all of these -// various scenarios, the purpose of asking is_good_progress() is to determine if there is enough memory available -// within young generation to justify making an attempt to perform a concurrent collection. For this reason, we'll -// use the current size of the generation (which may not be different than when the collection set was chosen) to -// assess how much free memory we require in order to consider the most recent GC to have had good progress. - -bool ShenandoahMetricsSnapshot::is_good_progress(ShenandoahGeneration* generation) { +bool ShenandoahMetricsSnapshot::is_good_progress() const { // Under the critical threshold? - ShenandoahFreeSet* free_set = _heap->free_set(); - size_t free_actual = free_set->available(); + const size_t free_actual = _free_set->available(); assert(free_actual != ShenandoahFreeSet::FreeSetUnderConstruction, "Avoid this race"); - // ShenandoahCriticalFreeThreshold is expressed as a percentage. We multiple this percentage by 1/100th - // of the generation capacity to determine whether the available memory within the generation exceeds the - // critical threshold. - size_t free_expected = (ShenandoahHeap::heap()->soft_max_capacity() / 100) * ShenandoahCriticalFreeThreshold; - - bool prog_free = free_actual >= free_expected; - log_info(gc, ergo)("%s progress for free space: %zu%s, need %zu%s", - prog_free ? "Good" : "Bad", - byte_size_in_proper_unit(free_actual), proper_unit_for_byte_size(free_actual), - byte_size_in_proper_unit(free_expected), proper_unit_for_byte_size(free_expected)); + // ShenandoahCriticalFreeThreshold is expressed as a percentage. We multiply this percentage by 1/100th + // of the soft max capacity to determine whether the available memory within the mutator partition of the + // freeset exceeds the critical threshold. + const size_t free_expected = (ShenandoahHeap::heap()->soft_max_capacity() / 100) * ShenandoahCriticalFreeThreshold; + const bool prog_free = free_actual >= free_expected; + log_info(gc, ergo)("%s progress for free space: " PROPERFMT ", need " PROPERFMT, + prog_free ? "Good" : "Bad", PROPERFMTARGS(free_actual), PROPERFMTARGS(free_expected)); if (!prog_free) { return false; } // Freed up enough? - size_t progress_actual = (_used_before > _used_after) ? _used_before - _used_after : 0; - size_t progress_expected = ShenandoahHeapRegion::region_size_bytes(); - bool prog_used = progress_actual >= progress_expected; - log_info(gc, ergo)("%s progress for used space: %zu%s, need %zu%s", - prog_used ? "Good" : "Bad", - byte_size_in_proper_unit(progress_actual), proper_unit_for_byte_size(progress_actual), - byte_size_in_proper_unit(progress_expected), proper_unit_for_byte_size(progress_expected)); + const size_t used_after = _free_set->used(); + const size_t progress_actual = (_used_before > used_after) ? _used_before - used_after : 0; + const size_t progress_expected = ShenandoahHeapRegion::region_size_bytes(); + const bool prog_used = progress_actual >= progress_expected; + log_info(gc, ergo)("%s progress for used space: " PROPERFMT ", need " PROPERFMT, + prog_used ? "Good" : "Bad", PROPERFMTARGS(progress_actual), PROPERFMTARGS(progress_expected)); if (prog_used) { return true; } // Internal fragmentation is down? - double if_actual = _if_before - _if_after; - double if_expected = 0.01; // 1% should be enough - bool prog_if = if_actual >= if_expected; + const double if_after = _free_set->internal_fragmentation(); + const double if_actual = _if_before - if_after; + const double if_expected = 0.01; // 1% should be enough + const bool prog_if = if_actual >= if_expected; log_info(gc, ergo)("%s progress for internal fragmentation: %.1f%%, need %.1f%%", prog_if ? "Good" : "Bad", if_actual * 100, if_expected * 100); @@ -99,9 +75,10 @@ bool ShenandoahMetricsSnapshot::is_good_progress(ShenandoahGeneration* generatio } // External fragmentation is down? - double ef_actual = _ef_before - _ef_after; - double ef_expected = 0.01; // 1% should be enough - bool prog_ef = ef_actual >= ef_expected; + const double ef_after = _free_set->external_fragmentation(); + const double ef_actual = _ef_before - ef_after; + const double ef_expected = 0.01; // 1% should be enough + const bool prog_ef = ef_actual >= ef_expected; log_info(gc, ergo)("%s progress for external fragmentation: %.1f%%, need %.1f%%", prog_ef ? "Good" : "Bad", ef_actual * 100, ef_expected * 100); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMetrics.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMetrics.hpp index 20d8ebfd595..c554a065386 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMetrics.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMetrics.hpp @@ -25,22 +25,20 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHMETRICS_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHMETRICS_HPP -#include "gc/shenandoah/shenandoahHeap.hpp" +#include "gc/shenandoah/shenandoahFreeSet.hpp" class ShenandoahMetricsSnapshot : public StackObj { private: - ShenandoahHeap* _heap; - size_t _used_before, _used_after; - double _if_before, _if_after; - double _ef_before, _ef_after; + ShenandoahFreeSet* _free_set; + size_t _used_before; + double _if_before; + double _ef_before; public: - ShenandoahMetricsSnapshot(); + explicit ShenandoahMetricsSnapshot(ShenandoahFreeSet* free_set); - void snap_before(); - void snap_after(); - - bool is_good_progress(ShenandoahGeneration *generation); + // Decide if the GC made "good" progress (i.e., reduced fragmentation, freed up sufficient memory). + bool is_good_progress() const; }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHMETRICS_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.cpp b/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.cpp index 99c739026cc..6c82f970606 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.cpp @@ -31,12 +31,11 @@ #include "runtime/safepoint.hpp" ShenandoahClassUnloadingTask::ShenandoahClassUnloadingTask(ShenandoahPhaseTimings::Phase phase, - uint num_workers, bool unloading_occurred) : WorkerTask("Shenandoah Class Unloading"), _phase(phase), _unloading_occurred(unloading_occurred), - _code_cache_task(num_workers, unloading_occurred), + _code_cache_task(unloading_occurred), _klass_cleaning_task() { assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.hpp b/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.hpp index b4c4fb94a5a..c68a4fde4ce 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.hpp @@ -59,7 +59,6 @@ private: KlassCleaningTask _klass_cleaning_task; public: ShenandoahClassUnloadingTask(ShenandoahPhaseTimings::Phase phase, - uint num_workers, bool unloading_occurred); void work(uint worker_id); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp index 260c1e0276f..53391a3e224 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp @@ -24,8 +24,7 @@ */ - -#include "gc/shared/strongRootsScope.hpp" +#include "code/nmethod.hpp" #include "gc/shared/taskTerminator.hpp" #include "gc/shared/workerThread.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" @@ -36,10 +35,13 @@ #include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" #include "gc/shenandoah/shenandoahSTWMark.hpp" #include "gc/shenandoah/shenandoahVerifier.hpp" +#include "runtime/threads.hpp" class ShenandoahSTWMarkTask : public WorkerTask { private: ShenandoahSTWMark* const _mark; + NMethodMarkingScope _nmethod_marking_scope; + ThreadsClaimTokenScope _threads_claim_token_scope; public: ShenandoahSTWMarkTask(ShenandoahSTWMark* mark); @@ -48,7 +50,9 @@ public: ShenandoahSTWMarkTask::ShenandoahSTWMarkTask(ShenandoahSTWMark* mark) : WorkerTask("Shenandoah STW mark"), - _mark(mark) { + _mark(mark), + _nmethod_marking_scope(), + _threads_claim_token_scope() { } void ShenandoahSTWMarkTask::work(uint worker_id) { @@ -98,7 +102,6 @@ void ShenandoahSTWMark::mark() { _generation->scan_remembered_set(false /* is_concurrent */); } - StrongRootsScope scope(nworkers); ShenandoahSTWMarkTask task(this); heap->workers()->run_task(&task); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp b/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp index 83151313f75..b248fab7958 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp @@ -103,7 +103,7 @@ public: } virtual bool is_safe(nmethod* nm) { - if (SafepointSynchronize::is_at_safepoint() || nm->is_unloading()) { + if (SafepointSynchronize::is_at_safepoint() || nm->is_unloading() || (NMethodState_lock->owned_by_self() && nm->is_not_installed())) { return true; } diff --git a/src/hotspot/share/gc/z/zUnload.cpp b/src/hotspot/share/gc/z/zUnload.cpp index c8b32385fcd..5c50b3077dd 100644 --- a/src/hotspot/share/gc/z/zUnload.cpp +++ b/src/hotspot/share/gc/z/zUnload.cpp @@ -100,7 +100,7 @@ public: } virtual bool is_safe(nmethod* nm) { - if (SafepointSynchronize::is_at_safepoint() || nm->is_unloading()) { + if (SafepointSynchronize::is_at_safepoint() || nm->is_unloading() || (NMethodState_lock->owned_by_self() && nm->is_not_installed())) { return true; } diff --git a/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp b/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp index 63efb7404e4..2979f5c5c2d 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, Datadog, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +28,7 @@ #include "logging/log.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaThread.inline.hpp" +#include "runtime/threadWXSetters.inline.hpp" #include "utilities/exceptions.hpp" JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) { @@ -113,6 +115,7 @@ JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) { JavaThread* jt = JavaThread::thread_from_jni_environment(env); assert(jt != nullptr, "invariant"); assert(jt->thread_state() == _thread_in_native, "invariant"); + MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, jt)); ThreadInVMfromNative transition(jt); log_error(jfr, system)("RegisterNatives for JVM class failed!"); } diff --git a/src/hotspot/share/jfr/leakprofiler/chains/rootSetClosure.cpp b/src/hotspot/share/jfr/leakprofiler/chains/rootSetClosure.cpp index 04ba9477fab..d339568a7f7 100644 --- a/src/hotspot/share/jfr/leakprofiler/chains/rootSetClosure.cpp +++ b/src/hotspot/share/jfr/leakprofiler/chains/rootSetClosure.cpp @@ -26,7 +26,6 @@ #include "classfile/stringTable.hpp" #include "gc/shared/oopStorage.inline.hpp" #include "gc/shared/oopStorageSet.inline.hpp" -#include "gc/shared/strongRootsScope.hpp" #include "jfr/leakprofiler/chains/bfsClosure.hpp" #include "jfr/leakprofiler/chains/dfsClosure.hpp" #include "jfr/leakprofiler/chains/edgeQueue.hpp" @@ -61,8 +60,6 @@ void RootSetClosure::do_oop(narrowOop* ref) { } } -class RootSetClosureMarkScope : public MarkScope {}; - template class RawRootClosure : public OopClosure { Delegate* _delegate; @@ -89,8 +86,6 @@ public: template void RootSetClosure::process() { - RootSetClosureMarkScope mark_scope; - CLDToOopClosure cldt_closure(this, ClassLoaderData::_claim_none); ClassLoaderDataGraph::always_strong_cld_do(&cldt_closure); diff --git a/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp b/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp index 4d7ccba5262..f274939bfe2 100644 --- a/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp +++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp @@ -26,7 +26,6 @@ #include "classfile/stringTable.hpp" #include "gc/shared/oopStorage.inline.hpp" #include "gc/shared/oopStorageSet.hpp" -#include "gc/shared/strongRootsScope.hpp" #include "jfr/leakprofiler/checkpoint/rootResolver.hpp" #include "jfr/leakprofiler/utilities/unifiedOopRef.inline.hpp" #include "jfr/utilities/jfrThreadIterator.hpp" @@ -325,12 +324,7 @@ bool ReferenceToThreadRootClosure::do_thread_roots(JavaThread* jt) { return false; } -class RootResolverMarkScope : public MarkScope { -}; - void RootResolver::resolve(RootCallback& callback) { - RootResolverMarkScope mark_scope; - // thread local roots ReferenceToThreadRootClosure rtrc(callback); if (rtrc.complete()) { diff --git a/src/hotspot/share/jfr/metadata/metadata.xml b/src/hotspot/share/jfr/metadata/metadata.xml index 060a78f13e7..6d43123ae87 100644 --- a/src/hotspot/share/jfr/metadata/metadata.xml +++ b/src/hotspot/share/jfr/metadata/metadata.xml @@ -857,7 +857,7 @@ - + diff --git a/src/hotspot/share/jfr/periodic/jfrOSInterface.cpp b/src/hotspot/share/jfr/periodic/jfrOSInterface.cpp index 2d4b99d59ab..18b2d7c5785 100644 --- a/src/hotspot/share/jfr/periodic/jfrOSInterface.cpp +++ b/src/hotspot/share/jfr/periodic/jfrOSInterface.cpp @@ -32,8 +32,6 @@ #include "runtime/vm_version.hpp" #include "utilities/ostream.hpp" -#include // for environment variables - static JfrOSInterface* _instance = nullptr; JfrOSInterface& JfrOSInterface::instance() { @@ -81,10 +79,7 @@ class JfrOSInterface::JfrOSInterfaceImpl : public JfrCHeapObj { // os information int os_version(char** os_version) const; - // environment information - void generate_environment_variables_events(); - - // system processes information + // system processes information int system_processes(SystemProcess** system_processes, int* no_of_sys_processes); int network_utilization(NetworkInterface** network_interfaces); diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp index 2ce1a93455b..7507b9c994e 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp @@ -230,8 +230,7 @@ class JfrCPUSamplerThread : public NonJavaThread { volatile bool _is_async_processing_of_cpu_time_jfr_requests_triggered; volatile bool _warned_about_timer_creation_failure; volatile bool _signal_handler_installed; - DEBUG_ONLY(volatile bool _out_of_stack_walking_enabled;) - DEBUG_ONLY(volatile u8 _out_of_stack_walking_iterations;) + DEBUG_ONLY(volatile bool _out_of_stack_walking_enabled = true;) static const u4 STOP_SIGNAL_BIT = 0x80000000; @@ -283,10 +282,6 @@ public: void set_out_of_stack_walking_enabled(bool runnable) { AtomicAccess::release_store(&_out_of_stack_walking_enabled, runnable); } - - u8 out_of_stack_walking_iterations() const { - return AtomicAccess::load(&_out_of_stack_walking_iterations); - } #endif }; @@ -394,7 +389,6 @@ void JfrCPUSamplerThread::run() { } DEBUG_ONLY(if (AtomicAccess::load_acquire(&_out_of_stack_walking_enabled)) {) if (AtomicAccess::cmpxchg(&_is_async_processing_of_cpu_time_jfr_requests_triggered, true, false)) { - DEBUG_ONLY(AtomicAccess::inc(&_out_of_stack_walking_iterations);) stackwalk_threads_in_native(); } DEBUG_ONLY(}) @@ -588,18 +582,14 @@ void JfrCPUTimeThreadSampling::handle_timer_signal(siginfo_t* info, void* contex } #ifdef ASSERT -void JfrCPUTimeThreadSampling::set_out_of_stack_walking_enabled(bool runnable) { +bool JfrCPUTimeThreadSampling::set_out_of_stack_walking_enabled(bool runnable) { if (_instance != nullptr && _instance->_sampler != nullptr) { _instance->_sampler->set_out_of_stack_walking_enabled(runnable); + return true; + } else { + return false; } } - -u8 JfrCPUTimeThreadSampling::out_of_stack_walking_iterations() { - if (_instance != nullptr && _instance->_sampler != nullptr) { - return _instance->_sampler->out_of_stack_walking_iterations(); - } - return 0; -} #endif void JfrCPUSamplerThread::sample_thread(JfrSampleRequest& request, void* ucontext, JavaThread* jt, JfrThreadLocal* tl, JfrTicks& now) { @@ -872,8 +862,9 @@ void JfrCPUTimeThreadSampling::on_javathread_terminate(JavaThread* thread) { } #ifdef ASSERT -static void set_out_of_stack_walking_enabled(bool runnable) { +bool JfrCPUTimeThreadSampling::set_out_of_stack_walking_enabled(bool runnable) { warn(); + return false; } #endif diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp index e17e63fc3ed..e7c915fc8be 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp @@ -139,9 +139,7 @@ class JfrCPUTimeThreadSampling : public JfrCHeapObj { static void trigger_async_processing_of_cpu_time_jfr_requests(); - DEBUG_ONLY(static void set_out_of_stack_walking_enabled(bool runnable);) - - DEBUG_ONLY(static u8 out_of_stack_walking_iterations();) + DEBUG_ONLY(static bool set_out_of_stack_walking_enabled(bool runnable);) }; #else @@ -162,8 +160,7 @@ private: static void on_javathread_create(JavaThread* thread); static void on_javathread_terminate(JavaThread* thread); - DEBUG_ONLY(static void set_out_of_stack_walking_enabled(bool runnable)); - DEBUG_ONLY(static u8 out_of_stack_walking_iterations();) + DEBUG_ONLY(static bool set_out_of_stack_walking_enabled(bool runnable)); }; #endif // defined(LINUX) diff --git a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp index 370214f1440..5163bc7f6a5 100644 --- a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp +++ b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp @@ -37,6 +37,7 @@ #include "runtime/javaThread.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/os.hpp" +#include "runtime/thread.inline.hpp" #include "utilities/growableArray.hpp" #include "utilities/ostream.hpp" @@ -450,16 +451,13 @@ const char* JfrEmergencyDump::chunk_path(const char* repository_path) { * * If we end up deadlocking in the attempt of dumping out jfr data, * we rely on the WatcherThread task "is_error_reported()", -* to exit the VM after a hard-coded timeout (disallow WatcherThread to emergency dump). +* to exit the VM after a hard-coded timeout (the reason +* for disallowing the WatcherThread to issue an emergency dump). * This "safety net" somewhat explains the aggressiveness in this attempt. * */ -static bool prepare_for_emergency_dump(Thread* thread) { +static void release_locks(Thread* thread) { assert(thread != nullptr, "invariant"); - if (thread->is_Watcher_thread()) { - // need WatcherThread as a safeguard against potential deadlocks - return false; - } #ifdef ASSERT Mutex* owned_lock = thread->owned_locks(); @@ -517,13 +515,6 @@ static bool prepare_for_emergency_dump(Thread* thread) { if (JfrStacktrace_lock->owned_by_self()) { JfrStacktrace_lock->unlock(); } - return true; -} - -static volatile int jfr_shutdown_lock = 0; - -static bool guard_reentrancy() { - return AtomicAccess::cmpxchg(&jfr_shutdown_lock, 0, 1) == 0; } class JavaThreadInVMAndNative : public StackObj { @@ -571,20 +562,48 @@ static void post_events(bool emit_old_object_samples, bool emit_event_shutdown, event.commit(); } +static volatile traceid _jfr_shutdown_tid = 0; + +static bool guard_reentrancy() { + const traceid shutdown_tid = AtomicAccess::load(&_jfr_shutdown_tid); + if (shutdown_tid == max_julong) { + // Someone tried but did not have a proper thread for the purpose. + return false; + } + if (shutdown_tid == 0) { + Thread* const thread = Thread::current_or_null_safe(); + const traceid tid = thread != nullptr ? JFR_JVM_THREAD_ID(thread) : max_julong; + if (AtomicAccess::cmpxchg(&_jfr_shutdown_tid, shutdown_tid, tid) != shutdown_tid) { + if (thread != nullptr) { + JavaThreadInVMAndNative jtivm(thread); + release_locks(thread); + } + log_info(jfr, system)("A jfr emergency dump is already in progress, waiting for thread id " UINT64_FORMAT_X, AtomicAccess::load(&_jfr_shutdown_tid)); + os::infinite_sleep(); // stay here until we exit normally or crash. + ShouldNotReachHere(); + } + return tid != max_julong; + } + // Recursive case + assert(JFR_JVM_THREAD_ID(Thread::current_or_null_safe()) == shutdown_tid, "invariant"); + return false; +} + void JfrEmergencyDump::on_vm_shutdown(bool emit_old_object_samples, bool emit_event_shutdown) { if (!guard_reentrancy()) { return; } - Thread* thread = Thread::current_or_null_safe(); - if (thread == nullptr) { + Thread* const thread = Thread::current_or_null_safe(); + assert(thread != nullptr, "invariant"); + if (thread->is_Watcher_thread()) { + log_info(jfr, system)("The Watcher thread crashed so no jfr emergency dump will be generated."); return; } // Ensure a JavaThread is _thread_in_vm when we make this call JavaThreadInVMAndNative jtivm(thread); - if (!prepare_for_emergency_dump(thread)) { - return; - } + release_locks(thread); post_events(emit_old_object_samples, emit_event_shutdown, thread); + // if JavaThread, transition to _thread_in_native to issue a final flushpoint NoHandleMark nhm; jtivm.transition_to_native(); diff --git a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp index 0e68e8a6032..3f0b132f1a4 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp @@ -507,6 +507,7 @@ void JfrRecorderService::rotate(int msgs) { return; } if (msgs & MSGBIT(MSG_VM_ERROR)) { + stop(); vm_error_rotation(); return; } diff --git a/src/hotspot/share/jfr/utilities/jfrTime.cpp b/src/hotspot/share/jfr/utilities/jfrTime.cpp index 75c3bc5cd28..4fdc087805a 100644 --- a/src/hotspot/share/jfr/utilities/jfrTime.cpp +++ b/src/hotspot/share/jfr/utilities/jfrTime.cpp @@ -34,7 +34,7 @@ bool JfrTime::initialize() { static bool initialized = false; if (!initialized) { #if defined(X86) && !defined(ZERO) - _ft_enabled = Rdtsc::initialize(); + _ft_enabled = Rdtsc::enabled(); #else _ft_enabled = false; #endif diff --git a/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp b/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp index 3a9fbc54bf9..ce617a4a514 100644 --- a/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp +++ b/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp @@ -1141,7 +1141,7 @@ int CodeInstaller::map_jvmci_bci(int bci) { return bci; } -void CodeInstaller::record_scope(jint pc_offset, HotSpotCompiledCodeStream* stream, u1 debug_info_flags, bool full_info, bool is_mh_invoke, bool return_oop, JVMCI_TRAPS) { +void CodeInstaller::record_scope(jint pc_offset, HotSpotCompiledCodeStream* stream, u1 debug_info_flags, bool full_info, bool return_oop, JVMCI_TRAPS) { if (full_info) { read_virtual_objects(stream, JVMCI_CHECK); } @@ -1184,7 +1184,7 @@ void CodeInstaller::record_scope(jint pc_offset, HotSpotCompiledCodeStream* stre // has_ea_local_in_scope and arg_escape should be added to JVMCI const bool has_ea_local_in_scope = false; const bool arg_escape = false; - _debug_recorder->describe_scope(pc_offset, method, nullptr, bci, reexecute, rethrow_exception, is_mh_invoke, return_oop, + _debug_recorder->describe_scope(pc_offset, method, nullptr, bci, reexecute, rethrow_exception, return_oop, has_ea_local_in_scope, arg_escape, locals_token, stack_token, monitors_token); } @@ -1242,14 +1242,8 @@ void CodeInstaller::site_Call(CodeBuffer& buffer, u1 tag, jint pc_offset, HotSpo _debug_recorder->add_safepoint(next_pc_offset, map); if (!method.is_null()) { - vmIntrinsics::ID iid = method->intrinsic_id(); - bool is_mh_invoke = false; - if (direct_call) { - is_mh_invoke = !method->is_static() && (iid == vmIntrinsics::_compiledLambdaForm || - (MethodHandles::is_signature_polymorphic(iid) && MethodHandles::is_signature_polymorphic_intrinsic(iid))); - } bool return_oop = method->is_returning_oop(); - record_scope(next_pc_offset, stream, flags, true, is_mh_invoke, return_oop, JVMCI_CHECK); + record_scope(next_pc_offset, stream, flags, true, return_oop, JVMCI_CHECK); } else { record_scope(next_pc_offset, stream, flags, true, JVMCI_CHECK); } @@ -1339,9 +1333,6 @@ void CodeInstaller::site_Mark(CodeBuffer& buffer, jint pc_offset, HotSpotCompile case DEOPT_HANDLER_ENTRY: _offsets.set_value(CodeOffsets::Deopt, pc_offset); break; - case DEOPT_MH_HANDLER_ENTRY: - _offsets.set_value(CodeOffsets::DeoptMH, pc_offset); - break; case FRAME_COMPLETE: _offsets.set_value(CodeOffsets::Frame_Complete, pc_offset); break; @@ -1369,6 +1360,7 @@ void CodeInstaller::site_Mark(CodeBuffer& buffer, jint pc_offset, HotSpotCompile case VERIFY_OOP_BITS: case VERIFY_OOP_MASK: case VERIFY_OOP_COUNT_ADDRESS: + case DEOPT_MH_HANDLER_ENTRY: break; default: diff --git a/src/hotspot/share/jvmci/jvmciCodeInstaller.hpp b/src/hotspot/share/jvmci/jvmciCodeInstaller.hpp index a8279e99dc2..bdebed2d9e8 100644 --- a/src/hotspot/share/jvmci/jvmciCodeInstaller.hpp +++ b/src/hotspot/share/jvmci/jvmciCodeInstaller.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -411,10 +411,10 @@ protected: void record_oop_patch(HotSpotCompiledCodeStream* stream, address dest, u1 read_tag, bool narrow, JVMCI_TRAPS); // full_info: if false, only BytecodePosition is in stream otherwise all DebugInfo is in stream - void record_scope(jint pc_offset, HotSpotCompiledCodeStream* stream, u1 debug_info_flags, bool full_info, bool is_mh_invoke, bool return_oop, JVMCI_TRAPS); + void record_scope(jint pc_offset, HotSpotCompiledCodeStream* stream, u1 debug_info_flags, bool full_info, bool return_oop, JVMCI_TRAPS); void record_scope(jint pc_offset, HotSpotCompiledCodeStream* stream, u1 debug_info_flags, bool full_info, JVMCI_TRAPS) { - record_scope(pc_offset, stream, debug_info_flags, full_info, false /* is_mh_invoke */, false /* return_oop */, JVMCIENV); + record_scope(pc_offset, stream, debug_info_flags, full_info, false /* return_oop */, JVMCIENV); } void record_object_value(ObjectValue* sv, HotSpotCompiledCodeStream* stream, JVMCI_TRAPS); diff --git a/src/hotspot/share/jvmci/jvmciRuntime.cpp b/src/hotspot/share/jvmci/jvmciRuntime.cpp index e75527235f0..a178ac66344 100644 --- a/src/hotspot/share/jvmci/jvmciRuntime.cpp +++ b/src/hotspot/share/jvmci/jvmciRuntime.cpp @@ -228,9 +228,6 @@ extern void vm_exit(int code); // unpack_with_exception entry instead. This makes life for the exception blob easier // because making that same check and diverting is painful from assembly language. JRT_ENTRY_NO_ASYNC(static address, exception_handler_for_pc_helper(JavaThread* current, oopDesc* ex, address pc, nmethod*& nm)) - // Reset method handle flag. - current->set_is_method_handle_return(false); - Handle exception(current, ex); // The frame we rethrow the exception to might not have been processed by the GC yet. @@ -305,8 +302,6 @@ JRT_ENTRY_NO_ASYNC(static address, exception_handler_for_pc_helper(JavaThread* c if (guard_pages_enabled) { address fast_continuation = nm->handler_for_exception_and_pc(exception, pc); if (fast_continuation != nullptr) { - // Set flag if return address is a method handle call site. - current->set_is_method_handle_return(nm->is_method_handle_return(pc)); return fast_continuation; } } @@ -343,9 +338,6 @@ JRT_ENTRY_NO_ASYNC(static address, exception_handler_for_pc_helper(JavaThread* c } } - // Set flag if return address is a method handle call site. - current->set_is_method_handle_return(nm->is_method_handle_return(pc)); - if (log_is_enabled(Info, exceptions)) { ResourceMark rm; log_info(exceptions)("Thread " PTR_FORMAT " continuing at PC " PTR_FORMAT diff --git a/src/hotspot/share/jvmci/jvmciRuntime.hpp b/src/hotspot/share/jvmci/jvmciRuntime.hpp index f4c322e831c..885ff0dbf9b 100644 --- a/src/hotspot/share/jvmci/jvmciRuntime.hpp +++ b/src/hotspot/share/jvmci/jvmciRuntime.hpp @@ -137,6 +137,11 @@ public: // Gets the JVMCI name of the nmethod (which may be null). const char* name() { return has_name() ? (char*)(((address) this) + sizeof(JVMCINMethodData)) : nullptr; } + // Returns true if this nmethod has a mirror + bool has_mirror() const { + return _nmethod_mirror_index != -1; + } + // Clears the HotSpotNmethod.address field in the mirror. If nm // is dead, the HotSpotNmethod.entryPoint field is also cleared. void invalidate_nmethod_mirror(nmethod* nm, nmethod::InvalidationReason invalidation_reason); diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index b91de1c9b35..7ef16f6e32c 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -242,7 +242,6 @@ nonstatic_field(JavaThread, _stack_overflow_state._stack_overflow_limit, address) \ volatile_nonstatic_field(JavaThread, _exception_oop, oop) \ volatile_nonstatic_field(JavaThread, _exception_pc, address) \ - volatile_nonstatic_field(JavaThread, _is_method_handle_return, int) \ volatile_nonstatic_field(JavaThread, _doing_unsafe_access, bool) \ nonstatic_field(JavaThread, _osthread, OSThread*) \ nonstatic_field(JavaThread, _saved_exception_pc, address) \ @@ -256,7 +255,6 @@ nonstatic_field(JavaThread, _should_post_on_exceptions_flag, int) \ nonstatic_field(JavaThread, _jni_environment, JNIEnv) \ nonstatic_field(JavaThread, _stack_overflow_state._reserved_stack_activation, address) \ - nonstatic_field(JavaThread, _held_monitor_count, intx) \ nonstatic_field(JavaThread, _lock_stack, LockStack) \ nonstatic_field(JavaThread, _om_cache, OMCache) \ nonstatic_field(JavaThread, _cont_entry, ContinuationEntry*) \ diff --git a/src/hotspot/share/memory/metaspace/chunkManager.cpp b/src/hotspot/share/memory/metaspace/chunkManager.cpp index 2c787046ce8..436b22aad94 100644 --- a/src/hotspot/share/memory/metaspace/chunkManager.cpp +++ b/src/hotspot/share/memory/metaspace/chunkManager.cpp @@ -117,10 +117,6 @@ Metachunk* ChunkManager::get_chunk(chunklevel_t preferred_level, chunklevel_t ma c = get_chunk_locked(preferred_level, max_level, min_committed_words); } - if (c != nullptr) { - ASAN_UNPOISON_MEMORY_REGION(c->base(), c->word_size() * BytesPerWord); - } - return c; } @@ -243,9 +239,6 @@ Metachunk* ChunkManager::get_chunk_locked(chunklevel_t preferred_level, chunklev // !! Note: this may invalidate the chunk. Do not access the chunk after // this function returns !! void ChunkManager::return_chunk(Metachunk* c) { - // It is valid to poison the chunk payload area at this point since its physically separated from - // the chunk meta info. - ASAN_POISON_MEMORY_REGION(c->base(), c->word_size() * BytesPerWord); MutexLocker fcl(Metaspace_lock, Mutex::_no_safepoint_check_flag); return_chunk_locked(c); } @@ -303,9 +296,6 @@ bool ChunkManager::attempt_enlarge_chunk(Metachunk* c) { enlarged = c->vsnode()->attempt_enlarge_chunk(c, &_chunks); } - if (enlarged) { - ASAN_UNPOISON_MEMORY_REGION(c->base() + old_word_size, (c->word_size() - old_word_size) * BytesPerWord); - } return enlarged; } diff --git a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp index d14cc5699fa..d21c6546cf5 100644 --- a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp +++ b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp @@ -113,6 +113,8 @@ bool VirtualSpaceNode::commit_range(MetaWord* p, size_t word_size) { vm_exit_out_of_memory(word_size * BytesPerWord, OOM_MMAP_ERROR, "Failed to commit metaspace."); } + ASAN_UNPOISON_MEMORY_REGION((char*)p, word_size * BytesPerWord); + if (AlwaysPreTouch) { os::pretouch_memory(p, p + word_size); } @@ -193,6 +195,8 @@ void VirtualSpaceNode::uncommit_range(MetaWord* p, size_t word_size) { fatal("Failed to uncommit metaspace."); } + ASAN_POISON_MEMORY_REGION((char*)p, word_size * BytesPerWord); + UL2(debug, "... uncommitted %zu words.", committed_words_in_range); // ... tell commit limiter... @@ -238,10 +242,6 @@ VirtualSpaceNode::VirtualSpaceNode(ReservedSpace rs, bool owns_rs, CommitLimiter assert_is_aligned(_base, chunklevel::MAX_CHUNK_BYTE_SIZE); assert_is_aligned(_word_size, chunklevel::MAX_CHUNK_WORD_SIZE); - // Poison the memory region. It will be unpoisoned later on a per-chunk base for chunks that are - // handed to arenas. - ASAN_POISON_MEMORY_REGION(rs.base(), rs.size()); - // Register memory region related to Metaspace. The Metaspace contains lots of pointers to malloc // memory. LSAN_REGISTER_ROOT_REGION(rs.base(), rs.size()); @@ -290,10 +290,6 @@ VirtualSpaceNode::~VirtualSpaceNode() { // Unregister memory region related to Metaspace. LSAN_UNREGISTER_ROOT_REGION(_rs.base(), _rs.size()); - // Undo the poisoning before potentially unmapping memory. This ensures that future mappings at - // the same address do not unexpectedly fail with use-after-poison. - ASAN_UNPOISON_MEMORY_REGION(_rs.base(), _rs.size()); - UL(debug, ": dies."); if (_owns_rs) { diff --git a/src/hotspot/share/oops/klass.cpp b/src/hotspot/share/oops/klass.cpp index b3386694b79..208a274d766 100644 --- a/src/hotspot/share/oops/klass.cpp +++ b/src/hotspot/share/oops/klass.cpp @@ -872,11 +872,10 @@ void Klass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protec // modify the CLD list outside a safepoint. if (class_loader_data() == nullptr) { set_class_loader_data(loader_data); - - // Add to class loader list first before creating the mirror - // (same order as class file parsing) - loader_data->add_class(this); } + // Add to class loader list first before creating the mirror + // (same order as class file parsing) + loader_data->add_class(this); Handle loader(THREAD, loader_data->class_loader()); ModuleEntry* module_entry = nullptr; diff --git a/src/hotspot/share/oops/trainingData.cpp b/src/hotspot/share/oops/trainingData.cpp index dcf95cf4653..24f82a53843 100644 --- a/src/hotspot/share/oops/trainingData.cpp +++ b/src/hotspot/share/oops/trainingData.cpp @@ -471,7 +471,6 @@ void TrainingData::init_dumptime_table(TRAPS) { _dumptime_training_data_dictionary = new DumptimeTrainingDataDictionary(); TrainingDataLocker l; TrainingDataLocker::snapshot(); - ResourceMark rm; Visitor visitor(training_data_set()->size()); training_data_set()->iterate([&](TrainingData* td) { diff --git a/src/hotspot/share/oops/trainingData.hpp b/src/hotspot/share/oops/trainingData.hpp index fbecb5c46bf..7ab5e179eea 100644 --- a/src/hotspot/share/oops/trainingData.hpp +++ b/src/hotspot/share/oops/trainingData.hpp @@ -97,7 +97,9 @@ public: // It supports recursive locking and a read-only mode (in which case no locks are taken). // It is also a part of the TD collection termination protocol (see the "snapshot" field). class TrainingDataLocker { +#if INCLUDE_CDS static volatile bool _snapshot; // If true we're not allocating new training data +#endif static int _lock_mode; const bool _recursive; static void lock() { @@ -152,7 +154,9 @@ public: #endif } static void assert_locked_or_snapshotted() { +#if INCLUDE_CDS assert(safely_locked() || _snapshot, "use under TrainingDataLocker or after snapshot"); +#endif } static void assert_locked() { assert(safely_locked(), "use under TrainingDataLocker"); diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 92fb54a3a13..6075317d86e 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -396,9 +396,182 @@ Node* AddNode::IdealIL(PhaseGVN* phase, bool can_reshape, BasicType bt) { } } + // Collapse addition of the same terms into multiplications. + Node* collapsed = Ideal_collapse_variable_times_con(phase, bt); + if (collapsed != nullptr) { + return collapsed; // Skip AddNode::Ideal() since it may now be a multiplication node. + } + return AddNode::Ideal(phase, can_reshape); } +// Try to collapse addition of the same terms into a single multiplication. On success, a new MulNode is returned. +// Examples of this conversion includes: +// - a + a + ... + a => CON*a +// - (a * CON) + a => (CON + 1) * a +// - a + (a * CON) => (CON + 1) * a +// +// We perform such conversions incrementally during IGVN by transforming left most nodes first and work up to the root +// of the expression. In other words, we convert, at each iteration: +// a + a + a + ... + a +// => 2*a + a + ... + a +// => 3*a + ... + a +// => n*a +// +// Due to the iterative nature of IGVN, MulNode transformed from first few AddNode terms may be further transformed into +// power-of-2 pattern. (e.g., 2 * a => a << 1, 3 * a => (a << 2) + a). We can't guarantee we'll always pick up +// transformed power-of-2 patterns when term `a` is complex. +// +// Note this also converts, for example, original expression `(a*3) + a` into `4*a` and `(a<<2) + a` into `5*a`. A more +// generalized pattern `(a*b) + (a*c)` into `a*(b + c)` is handled by AddNode::IdealIL(). +Node* AddNode::Ideal_collapse_variable_times_con(PhaseGVN* phase, BasicType bt) { + // We need to make sure that the current AddNode is not part of a MulNode that has already been optimized to a + // power-of-2 addition (e.g., 3 * a => (a << 2) + a). Without this check, GVN would keep trying to optimize the same + // node and can't progress. For example, 3 * a => (a << 2) + a => 3 * a => (a << 2) + a => ... + if (Multiplication::find_power_of_two_addition_pattern(this, bt).is_valid()) { + return nullptr; + } + + Node* lhs = in(1); + Node* rhs = in(2); + + Multiplication mul = Multiplication::find_collapsible_addition_patterns(lhs, rhs, bt); + if (!mul.is_valid_with(rhs)) { + // Swap lhs and rhs then try again + mul = Multiplication::find_collapsible_addition_patterns(rhs, lhs, bt); + if (!mul.is_valid_with(lhs)) { + return nullptr; + } + } + + Node* con; + if (bt == T_INT) { + con = phase->intcon(java_add(static_cast(mul.multiplier()), 1)); + } else { + con = phase->longcon(java_add(mul.multiplier(), CONST64(1))); + } + + return MulNode::make(con, mul.variable(), bt); +} + +// Find a pattern of collapsable additions that can be converted to a multiplication. +// When matching the LHS `a * CON`, we match with best efforts by looking for the following patterns: +// - (1) Simple addition: LHS = a + a +// - (2) Simple lshift: LHS = a << CON +// - (3) Simple multiplication: LHS = CON * a +// - (4) Power-of-two addition: LHS = (a << CON1) + (a << CON2) +AddNode::Multiplication AddNode::Multiplication::find_collapsible_addition_patterns(const Node* a, const Node* pattern, BasicType bt) { + // (1) Simple addition pattern (e.g., lhs = a + a) + Multiplication mul = find_simple_addition_pattern(a, bt); + if (mul.is_valid_with(pattern)) { + return mul; + } + + // (2) Simple lshift pattern (e.g., lhs = a << CON) + mul = find_simple_lshift_pattern(a, bt); + if (mul.is_valid_with(pattern)) { + return mul; + } + + // (3) Simple multiplication pattern (e.g., lhs = CON * a) + mul = find_simple_multiplication_pattern(a, bt); + if (mul.is_valid_with(pattern)) { + return mul; + } + + // (4) Power-of-two addition pattern (e.g., lhs = (a << CON1) + (a << CON2)) + // While multiplications can be potentially optimized to power-of-2 subtractions (e.g., a * 7 => (a << 3) - a), + // (x - y) + y => x is already handled by the Identity() methods. So, we don't need to check for that pattern here. + mul = find_power_of_two_addition_pattern(a, bt); + if (mul.is_valid_with(pattern)) { + return mul; + } + + // We've tried everything. + return make_invalid(); +} + +// Try to match `n = a + a`. On success, return a struct with `.valid = true`, `variable = a`, and `multiplier = 2`. +// The method matches `n` for pattern: a + a. +AddNode::Multiplication AddNode::Multiplication::find_simple_addition_pattern(const Node* n, BasicType bt) { + if (n->Opcode() == Op_Add(bt) && n->in(1) == n->in(2)) { + return Multiplication(n->in(1), 2); + } + + return make_invalid(); +} + +// Try to match `n = a << CON`. On success, return a struct with `.valid = true`, `variable = a`, and +// `multiplier = 1 << CON`. +// Match `n` for pattern: a << CON. +// Note that the power-of-2 multiplication optimization could potentially convert a MulNode to this pattern. +AddNode::Multiplication AddNode::Multiplication::find_simple_lshift_pattern(const Node* n, BasicType bt) { + // Note that power-of-2 multiplication optimization could potentially convert a MulNode to this pattern + if (n->Opcode() == Op_LShift(bt) && n->in(2)->is_Con()) { + Node* con = n->in(2); + if (!con->is_top()) { + return Multiplication(n->in(1), java_shift_left(1, con->get_int(), bt)); + } + } + + return make_invalid(); +} + +// Try to match `n = CON * a`. On success, return a struct with `.valid = true`, `variable = a`, and `multiplier = CON`. +// Match `n` for patterns: CON * a +// Note that `CON` will always be the second input node of a Mul node canonicalized by Ideal(). If this is not the case, +// `n` has not been processed by iGVN. So we skip the optimization for the current add node and wait for to be added to +// the queue again. +AddNode::Multiplication AddNode::Multiplication::find_simple_multiplication_pattern(const Node* n, BasicType bt) { + if (n->Opcode() == Op_Mul(bt) && n->in(2)->is_Con()) { + Node* con = n->in(2); + Node* base = n->in(1); + + if (!con->is_top()) { + return Multiplication(base, con->get_integer_as_long(bt)); + } + } + + return make_invalid(); +} + +// Try to match `n = (a << CON1) + (a << CON2)`. On success, return a struct with `.valid = true`, `variable = a`, and +// `multiplier = (1 << CON1) + (1 << CON2)`. +// Match `n` for patterns: +// - (1) (a << CON) + (a << CON) +// - (2) (a << CON) + a +// - (3) a + (a << CON) +// - (4) a + a +// Note that one or both of the term of the addition could simply be `a` (i.e., a << 0) as in pattern (4). +AddNode::Multiplication AddNode::Multiplication::find_power_of_two_addition_pattern(const Node* n, BasicType bt) { + if (n->Opcode() == Op_Add(bt) && n->in(1) != n->in(2)) { + const Multiplication lhs = find_simple_lshift_pattern(n->in(1), bt); + const Multiplication rhs = find_simple_lshift_pattern(n->in(2), bt); + + // Pattern (1) + { + const Multiplication res = lhs.add(rhs); + if (res.is_valid()) { + return res; + } + } + + // Pattern (2) + if (lhs.is_valid_with(n->in(2))) { + return Multiplication(lhs.variable(), java_add(lhs.multiplier(), CONST64(1))); + } + + // Pattern (3) + if (rhs.is_valid_with(n->in(1))) { + return Multiplication(rhs.variable(), java_add(rhs.multiplier(), CONST64(1))); + } + + // Pattern (4), which is equivalent to a simple addition pattern + return find_simple_addition_pattern(n, bt); + } + + return make_invalid(); +} Node* AddINode::Ideal(PhaseGVN* phase, bool can_reshape) { Node* in1 = in(1); diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp index 456a8d9f9a0..28ed73121ed 100644 --- a/src/hotspot/share/opto/addnode.hpp +++ b/src/hotspot/share/opto/addnode.hpp @@ -42,7 +42,51 @@ typedef const Pair ConstAddOperands; // by virtual functions. class AddNode : public Node { virtual uint hash() const; -public: + + class Multiplication { + bool _is_valid = false; + + Node* _variable = nullptr; + jlong _multiplier = 0; + + private: + Multiplication() {} + + public: + Multiplication(Node* variable, jlong multiplier) : + _is_valid(true), + _variable(variable), + _multiplier(multiplier) {} + + static Multiplication make_invalid() { + static Multiplication invalid = Multiplication(); + return invalid; + } + + static Multiplication find_collapsible_addition_patterns(const Node* a, const Node* pattern, BasicType bt); + static Multiplication find_simple_addition_pattern(const Node* n, BasicType bt); + static Multiplication find_simple_lshift_pattern(const Node* n, BasicType bt); + static Multiplication find_simple_multiplication_pattern(const Node* n, BasicType bt); + static Multiplication find_power_of_two_addition_pattern(const Node* n, BasicType bt); + + Multiplication add(const Multiplication rhs) const { + if (is_valid_with(rhs.variable()) && rhs.is_valid_with(variable())) { + return Multiplication(variable(), java_add(multiplier(), rhs.multiplier())); + } + + return make_invalid(); + } + + bool is_valid() const { return _is_valid; } + bool is_valid_with(const Node* variable) const { + return _is_valid && this->_variable == variable; + } + + Node* variable() const { return _variable; } + jlong multiplier() const { return _multiplier; } + }; + + public: AddNode( Node *in1, Node *in2 ) : Node(nullptr,in1,in2) { init_class_id(Class_Add); } @@ -55,6 +99,7 @@ public: // and flatten expressions (so that 1+x+2 becomes x+3). virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); Node* IdealIL(PhaseGVN* phase, bool can_reshape, BasicType bt); + Node* Ideal_collapse_variable_times_con(PhaseGVN* phase, BasicType bt); // Compute a new Type for this node. Basically we just do the pre-check, // then call the virtual add() to set the type. diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp index e09d8cabe2c..483cb731103 100644 --- a/src/hotspot/share/opto/callGenerator.cpp +++ b/src/hotspot/share/opto/callGenerator.cpp @@ -169,10 +169,6 @@ JVMState* DirectCallGenerator::generate(JVMState* jvms) { } // Mark the call node as virtual, sort of: call->set_optimized_virtual(true); - if (method()->is_method_handle_intrinsic() || - method()->is_compiled_lambda_form()) { - call->set_method_handle_invoke(true); - } } kit.set_arguments_for_java_call(call); kit.set_edges_for_java_call(call, false, _separate_io_proj); @@ -469,6 +465,10 @@ class LateInlineVirtualCallGenerator : public VirtualCallGenerator { // Convert the CallDynamicJava into an inline virtual void do_late_inline(); + virtual ciMethod* callee_method() { + return _callee; + } + virtual void set_callee_method(ciMethod* m) { assert(_callee == nullptr || _callee == m, "repeated inline attempt with different callee"); _callee = m; diff --git a/src/hotspot/share/opto/callGenerator.hpp b/src/hotspot/share/opto/callGenerator.hpp index 82b195e0c76..e24ea5e5356 100644 --- a/src/hotspot/share/opto/callGenerator.hpp +++ b/src/hotspot/share/opto/callGenerator.hpp @@ -88,6 +88,7 @@ class CallGenerator : public ArenaObj { virtual void set_unique_id(jlong id) { fatal("unique id only for late inlines"); }; virtual jlong unique_id() const { fatal("unique id only for late inlines"); return 0; }; + virtual ciMethod* callee_method() { ShouldNotReachHere(); } virtual void set_callee_method(ciMethod* callee) { ShouldNotReachHere(); } // Note: It is possible for a CG to be both inline and virtual. diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index 995208ba24f..ef1ebc5cef9 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -1228,33 +1228,37 @@ Node* CallDynamicJavaNode::Ideal(PhaseGVN* phase, bool can_reshape) { assert(IncrementalInlineVirtual, "required"); assert(cg->call_node() == this, "mismatch"); - // Recover symbolic info for method resolution. - ciMethod* caller = jvms()->method(); - ciBytecodeStream iter(caller); - iter.force_bci(jvms()->bci()); + if (cg->callee_method() == nullptr) { + // Recover symbolic info for method resolution. + ciMethod* caller = jvms()->method(); + ciBytecodeStream iter(caller); + iter.force_bci(jvms()->bci()); - bool not_used1; - ciSignature* not_used2; - ciMethod* orig_callee = iter.get_method(not_used1, ¬_used2); // callee in the bytecode - ciKlass* holder = iter.get_declared_method_holder(); - if (orig_callee->is_method_handle_intrinsic()) { - assert(_override_symbolic_info, "required"); - orig_callee = method(); - holder = method()->holder(); + bool not_used1; + ciSignature* not_used2; + ciMethod* orig_callee = iter.get_method(not_used1, ¬_used2); // callee in the bytecode + ciKlass* holder = iter.get_declared_method_holder(); + if (orig_callee->is_method_handle_intrinsic()) { + assert(_override_symbolic_info, "required"); + orig_callee = method(); + holder = method()->holder(); + } + + ciInstanceKlass* klass = ciEnv::get_instance_klass_for_declared_method_holder(holder); + + Node* receiver_node = in(TypeFunc::Parms); + const TypeOopPtr* receiver_type = phase->type(receiver_node)->isa_oopptr(); + + int not_used3; + bool call_does_dispatch; + ciMethod* callee = phase->C->optimize_virtual_call(caller, klass, holder, orig_callee, receiver_type, true /*is_virtual*/, + call_does_dispatch, not_used3); // out-parameters + if (!call_does_dispatch) { + cg->set_callee_method(callee); + } } - - ciInstanceKlass* klass = ciEnv::get_instance_klass_for_declared_method_holder(holder); - - Node* receiver_node = in(TypeFunc::Parms); - const TypeOopPtr* receiver_type = phase->type(receiver_node)->isa_oopptr(); - - int not_used3; - bool call_does_dispatch; - ciMethod* callee = phase->C->optimize_virtual_call(caller, klass, holder, orig_callee, receiver_type, true /*is_virtual*/, - call_does_dispatch, not_used3); // out-parameters - if (!call_does_dispatch) { + if (cg->callee_method() != nullptr) { // Register for late inlining. - cg->set_callee_method(callee); register_for_late_inline(); // MH late inlining prepends to the list, so do the same } } else { diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp index 093c47194ef..9029a009989 100644 --- a/src/hotspot/share/opto/callnode.hpp +++ b/src/hotspot/share/opto/callnode.hpp @@ -778,7 +778,6 @@ protected: ciMethod* _method; // Method being direct called bool _optimized_virtual; - bool _method_handle_invoke; bool _override_symbolic_info; // Override symbolic call site info from bytecode bool _arg_escape; // ArgEscape in parameter list public: @@ -786,7 +785,6 @@ public: : CallNode(tf, addr, TypePtr::BOTTOM), _method(method), _optimized_virtual(false), - _method_handle_invoke(false), _override_symbolic_info(false), _arg_escape(false) { @@ -798,8 +796,6 @@ public: void set_method(ciMethod *m) { _method = m; } void set_optimized_virtual(bool f) { _optimized_virtual = f; } bool is_optimized_virtual() const { return _optimized_virtual; } - void set_method_handle_invoke(bool f) { _method_handle_invoke = f; } - bool is_method_handle_invoke() const { return _method_handle_invoke; } void set_override_symbolic_info(bool f) { _override_symbolic_info = f; } bool override_symbolic_info() const { return _override_symbolic_info; } void set_arg_escape(bool f) { _arg_escape = f; } diff --git a/src/hotspot/share/opto/cfgnode.hpp b/src/hotspot/share/opto/cfgnode.hpp index a0e780c0e57..fffe00a4114 100644 --- a/src/hotspot/share/opto/cfgnode.hpp +++ b/src/hotspot/share/opto/cfgnode.hpp @@ -313,7 +313,7 @@ public: init_class_id(Class_MultiBranch); } // returns required number of users to be well formed. - virtual int required_outcnt() const = 0; + virtual uint required_outcnt() const = 0; }; //------------------------------IfNode----------------------------------------- @@ -435,7 +435,7 @@ public: virtual const Type *bottom_type() const { return TypeTuple::IFBOTH; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual const Type* Value(PhaseGVN* phase) const; - virtual int required_outcnt() const { return 2; } + virtual uint required_outcnt() const { return 2; } virtual const RegMask &out_RegMask() const; Node* fold_compares(PhaseIterGVN* phase); static Node* up_one_dom(Node* curr, bool linear_only = false); @@ -591,7 +591,7 @@ public: virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual const Type *bottom_type() const; virtual bool pinned() const { return true; } - virtual int required_outcnt() const { return _size; } + virtual uint required_outcnt() const { return _size; } }; //------------------------------JumpNode--------------------------------------- @@ -716,7 +716,7 @@ public: virtual const Type *bottom_type() const { return TypeTuple::IFBOTH; } virtual const Type* Value(PhaseGVN* phase) const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual int required_outcnt() const { return 2; } + virtual uint required_outcnt() const { return 2; } virtual void emit(C2_MacroAssembler *masm, PhaseRegAlloc *ra_) const { } virtual uint size(PhaseRegAlloc *ra_) const { return 0; } #ifndef PRODUCT diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index f828dfb6928..6babc13e1b3 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -650,7 +650,6 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, _igv_idx(0), _trace_opto_output(directive->TraceOptoOutputOption), #endif - _has_method_handle_invokes(false), _clinit_barrier_on_entry(false), _stress_seed(0), _comp_arena(mtCompiler, Arena::Tag::tag_comp), @@ -925,7 +924,6 @@ Compile::Compile(ciEnv* ci_env, _igv_idx(0), _trace_opto_output(directive->TraceOptoOutputOption), #endif - _has_method_handle_invokes(false), _clinit_barrier_on_entry(false), _stress_seed(0), _comp_arena(mtCompiler, Arena::Tag::tag_comp), @@ -2106,6 +2104,12 @@ bool Compile::inline_incrementally_one() { bool is_scheduled_for_igvn_before = C->igvn_worklist()->member(cg->call_node()); bool does_dispatch = cg->is_virtual_late_inline() || cg->is_mh_late_inline(); if (inlining_incrementally() || does_dispatch) { // a call can be either inlined or strength-reduced to a direct call + if (should_stress_inlining()) { + // randomly add repeated inline attempt if stress-inlining + cg->call_node()->set_generator(cg); + C->igvn_worklist()->push(cg->call_node()); + continue; + } cg->do_late_inline(); assert(_late_inlines.at(i) == cg, "no insertions before current position allowed"); if (failing()) { @@ -5374,3 +5378,158 @@ Node* Compile::narrow_value(BasicType bt, Node* value, const Type* type, PhaseGV void Compile::record_method_not_compilable_oom() { record_method_not_compilable(CompilationMemoryStatistic::failure_reason_memlimit()); } + +#ifndef PRODUCT +// Collects all the control inputs from nodes on the worklist and from their data dependencies +static void find_candidate_control_inputs(Unique_Node_List& worklist, Unique_Node_List& candidates) { + // Follow non-control edges until we reach CFG nodes + for (uint i = 0; i < worklist.size(); i++) { + const Node* n = worklist.at(i); + for (uint j = 0; j < n->req(); j++) { + Node* in = n->in(j); + if (in == nullptr || in->is_Root()) { + continue; + } + if (in->is_CFG()) { + if (in->is_Call()) { + // The return value of a call is only available if the call did not result in an exception + Node* control_proj_use = in->as_Call()->proj_out(TypeFunc::Control)->unique_out(); + if (control_proj_use->is_Catch()) { + Node* fall_through = control_proj_use->as_Catch()->proj_out(CatchProjNode::fall_through_index); + candidates.push(fall_through); + continue; + } + } + + if (in->is_Multi()) { + // We got here by following data inputs so we should only have one control use + // (no IfNode, etc) + assert(!n->is_MultiBranch(), "unexpected node type: %s", n->Name()); + candidates.push(in->as_Multi()->proj_out(TypeFunc::Control)); + } else { + candidates.push(in); + } + } else { + worklist.push(in); + } + } + } +} + +// Returns the candidate node that is a descendant to all the other candidates +static Node* pick_control(Unique_Node_List& candidates) { + Unique_Node_List worklist; + worklist.copy(candidates); + + // Traverse backwards through the CFG + for (uint i = 0; i < worklist.size(); i++) { + const Node* n = worklist.at(i); + if (n->is_Root()) { + continue; + } + for (uint j = 0; j < n->req(); j++) { + // Skip backedge of loops to avoid cycles + if (n->is_Loop() && j == LoopNode::LoopBackControl) { + continue; + } + + Node* pred = n->in(j); + if (pred != nullptr && pred != n && pred->is_CFG()) { + worklist.push(pred); + // if pred is an ancestor of n, then pred is an ancestor to at least one candidate + candidates.remove(pred); + } + } + } + + assert(candidates.size() == 1, "unexpected control flow"); + return candidates.at(0); +} + +// Initialize a parameter input for a debug print call, using a placeholder for jlong and jdouble +static void debug_print_init_parm(Node* call, Node* parm, Node* half, int* pos) { + call->init_req((*pos)++, parm); + const BasicType bt = parm->bottom_type()->basic_type(); + if (bt == T_LONG || bt == T_DOUBLE) { + call->init_req((*pos)++, half); + } +} + +Node* Compile::make_debug_print_call(const char* str, address call_addr, PhaseGVN* gvn, + Node* parm0, Node* parm1, + Node* parm2, Node* parm3, + Node* parm4, Node* parm5, + Node* parm6) const { + Node* str_node = gvn->transform(new ConPNode(TypeRawPtr::make(((address) str)))); + const TypeFunc* type = OptoRuntime::debug_print_Type(parm0, parm1, parm2, parm3, parm4, parm5, parm6); + Node* call = new CallLeafNode(type, call_addr, "debug_print", TypeRawPtr::BOTTOM); + + // find the most suitable control input + Unique_Node_List worklist, candidates; + if (parm0 != nullptr) { worklist.push(parm0); + if (parm1 != nullptr) { worklist.push(parm1); + if (parm2 != nullptr) { worklist.push(parm2); + if (parm3 != nullptr) { worklist.push(parm3); + if (parm4 != nullptr) { worklist.push(parm4); + if (parm5 != nullptr) { worklist.push(parm5); + if (parm6 != nullptr) { worklist.push(parm6); + /* close each nested if ===> */ } } } } } } } + find_candidate_control_inputs(worklist, candidates); + Node* control = nullptr; + if (candidates.size() == 0) { + control = C->start()->proj_out(TypeFunc::Control); + } else { + control = pick_control(candidates); + } + + // find all the previous users of the control we picked + GrowableArray users_of_control; + for (DUIterator_Fast kmax, i = control->fast_outs(kmax); i < kmax; i++) { + Node* use = control->fast_out(i); + if (use->is_CFG() && use != control) { + users_of_control.push(use); + } + } + + // we do not actually care about IO and memory as it uses neither + call->init_req(TypeFunc::Control, control); + call->init_req(TypeFunc::I_O, top()); + call->init_req(TypeFunc::Memory, top()); + call->init_req(TypeFunc::FramePtr, C->start()->proj_out(TypeFunc::FramePtr)); + call->init_req(TypeFunc::ReturnAdr, top()); + + int pos = TypeFunc::Parms; + call->init_req(pos++, str_node); + if (parm0 != nullptr) { debug_print_init_parm(call, parm0, top(), &pos); + if (parm1 != nullptr) { debug_print_init_parm(call, parm1, top(), &pos); + if (parm2 != nullptr) { debug_print_init_parm(call, parm2, top(), &pos); + if (parm3 != nullptr) { debug_print_init_parm(call, parm3, top(), &pos); + if (parm4 != nullptr) { debug_print_init_parm(call, parm4, top(), &pos); + if (parm5 != nullptr) { debug_print_init_parm(call, parm5, top(), &pos); + if (parm6 != nullptr) { debug_print_init_parm(call, parm6, top(), &pos); + /* close each nested if ===> */ } } } } } } } + assert(call->in(call->req()-1) != nullptr, "must initialize all parms"); + + call = gvn->transform(call); + Node* call_control_proj = gvn->transform(new ProjNode(call, TypeFunc::Control)); + + // rewire previous users to have the new call as control instead + PhaseIterGVN* igvn = gvn->is_IterGVN(); + for (int i = 0; i < users_of_control.length(); i++) { + Node* use = users_of_control.at(i); + for (uint j = 0; j < use->req(); j++) { + if (use->in(j) == control) { + if (igvn != nullptr) { + igvn->replace_input_of(use, j, call_control_proj); + } else { + gvn->hash_delete(use); + use->set_req(j, call_control_proj); + gvn->hash_insert(use); + } + } + } + } + + return call; +} +#endif // !PRODUCT diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 1cfcdca9610..66a5497a7ad 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -354,8 +354,6 @@ class Compile : public Phase { bool _parsed_irreducible_loop; // True if ciTypeFlow detected irreducible loops during parsing #endif bool _has_irreducible_loop; // Found irreducible loops - // JSR 292 - bool _has_method_handle_invokes; // True if this method has MethodHandle invokes. bool _has_monitors; // Metadata transfered to nmethod to enable Continuations lock-detection fastpath bool _has_scoped_access; // For shared scope closure bool _clinit_barrier_on_entry; // True if clinit barrier is needed on nmethod entry @@ -666,10 +664,6 @@ public: bool has_irreducible_loop() const { return _has_irreducible_loop; } void set_has_irreducible_loop(bool z) { _has_irreducible_loop = z; } - // JSR 292 - bool has_method_handle_invokes() const { return _has_method_handle_invokes; } - void set_has_method_handle_invokes(bool z) { _has_method_handle_invokes = z; } - Ticks _latest_stage_start_counter; void begin_method(); @@ -1100,7 +1094,8 @@ public: bool inline_incrementally_one(); void inline_incrementally_cleanup(PhaseIterGVN& igvn); void inline_incrementally(PhaseIterGVN& igvn); - bool should_delay_inlining() { return AlwaysIncrementalInline || (StressIncrementalInlining && (random() % 2) == 0); } + bool should_stress_inlining() { return StressIncrementalInlining && (random() % 2) == 0; } + bool should_delay_inlining() { return AlwaysIncrementalInline || should_stress_inlining(); } void inline_string_calls(bool parse_time); void inline_boxing_calls(PhaseIterGVN& igvn); bool optimize_loops(PhaseIterGVN& igvn, LoopOptsMode mode); @@ -1321,6 +1316,28 @@ public: BasicType out_bt, BasicType in_bt); static Node* narrow_value(BasicType bt, Node* value, const Type* type, PhaseGVN* phase, bool transform_res); + +#ifndef PRODUCT +private: + // getting rid of the template makes things easier + Node* make_debug_print_call(const char* str, address call_addr, PhaseGVN* gvn, + Node* parm0 = nullptr, Node* parm1 = nullptr, + Node* parm2 = nullptr, Node* parm3 = nullptr, + Node* parm4 = nullptr, Node* parm5 = nullptr, + Node* parm6 = nullptr) const; + +public: + // Creates a CallLeafNode for a runtime call that prints a static string and the values of the + // nodes passed as arguments. + // This function also takes care of doing the necessary wiring, including finding a suitable control + // based on the nodes that need to be printed. Note that passing nodes that have incompatible controls + // is undefined behavior. + template + Node* make_debug_print(const char* str, PhaseGVN* gvn, NN... in) { + address call_addr = CAST_FROM_FN_PTR(address, SharedRuntime::debug_print); + return make_debug_print_call(str, call_addr, gvn, in...); + } +#endif }; #endif // SHARE_OPTO_COMPILE_HPP diff --git a/src/hotspot/share/opto/countbitsnode.cpp b/src/hotspot/share/opto/countbitsnode.cpp index 4023678b51c..aac874e94b1 100644 --- a/src/hotspot/share/opto/countbitsnode.cpp +++ b/src/hotspot/share/opto/countbitsnode.cpp @@ -26,6 +26,7 @@ #include "opto/opcodes.hpp" #include "opto/phaseX.hpp" #include "opto/type.hpp" +#include "utilities/population_count.hpp" //------------------------------Value------------------------------------------ const Type* CountLeadingZerosINode::Value(PhaseGVN* phase) const { @@ -116,3 +117,42 @@ const Type* CountTrailingZerosLNode::Value(PhaseGVN* phase) const { } return TypeInt::INT; } +// We use the KnownBits information from the integer types to derive how many one bits +// we have at least and at most. +// From the definition of KnownBits, we know: +// zeros: Indicates which bits must be 0: zeros[i]=1 -> t[i]=0 +// ones: Indicates which bits must be 1: ones[i]=1 -> t[i]=1 +// +// From this, we derive: +// numer_of_zeros_in_t >= pop_count(zeros) +// -> number_of_ones_in_t <= bits_per_type - pop_count(zeros) = pop_count(~zeros) +// number_of_ones_in_t >= pop_count(ones) +// +// By definition: +// pop_count(t) = number_of_ones_in_t +// +// It follows: +// pop_count(ones) <= pop_count(t) <= pop_count(~zeros) +// +// Note: signed _lo and _hi, as well as unsigned _ulo and _uhi bounds of the integer types +// are already reflected in the KnownBits information, see TypeInt / TypeLong definitions. +const Type* PopCountINode::Value(PhaseGVN* phase) const { + const Type* t = phase->type(in(1)); + if (t == Type::TOP) { + return Type::TOP; + } + const TypeInt* tint = t->is_int(); + KnownBits bits = tint->_bits; + return TypeInt::make(population_count(bits._ones), population_count(~bits._zeros), tint->_widen); + +} + +const Type* PopCountLNode::Value(PhaseGVN* phase) const { + const Type* t = phase->type(in(1)); + if (t == Type::TOP) { + return Type::TOP; + } + const TypeLong* tlong = t->is_long(); + KnownBits bits = tlong->_bits; + return TypeInt::make(population_count(bits._ones), population_count(~bits._zeros), tlong->_widen); +} diff --git a/src/hotspot/share/opto/countbitsnode.hpp b/src/hotspot/share/opto/countbitsnode.hpp index 410d5129882..35465b1835b 100644 --- a/src/hotspot/share/opto/countbitsnode.hpp +++ b/src/hotspot/share/opto/countbitsnode.hpp @@ -80,6 +80,7 @@ class PopCountINode : public CountBitsNode { public: PopCountINode(Node* in1) : CountBitsNode(in1) {} virtual int Opcode() const; + virtual const Type* Value(PhaseGVN* phase) const; }; //---------- PopCountLNode ----------------------------------------------------- @@ -88,6 +89,7 @@ class PopCountLNode : public CountBitsNode { public: PopCountLNode(Node* in1) : CountBitsNode(in1) {} virtual int Opcode() const; + virtual const Type* Value(PhaseGVN* phase) const; }; diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index e6a593770b4..cbf0666c00e 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -201,7 +201,7 @@ bool ConnectionGraph::compute_escape() { if (!UseStoreStoreForCtor || n->req() > MemBarNode::Precedent) { storestore_worklist.append(n->as_MemBarStoreStore()); } - break; + // If MemBarStoreStore has a precedent edge add it to the worklist (like MemBarRelease) case Op_MemBarRelease: if (n->req() > MemBarNode::Precedent) { record_for_optimizer(n); diff --git a/src/hotspot/share/opto/lcm.cpp b/src/hotspot/share/opto/lcm.cpp index ca1863b685e..fd7644f8587 100644 --- a/src/hotspot/share/opto/lcm.cpp +++ b/src/hotspot/share/opto/lcm.cpp @@ -946,17 +946,6 @@ uint PhaseCFG::sched_call(Block* block, uint node_cnt, Node_List& worklist, Grow // references but there no way to handle oops differently than other // pointers as far as the kill mask goes. bool exclude_soe = op == Op_CallRuntime; - - // If the call is a MethodHandle invoke, we need to exclude the - // register which is used to save the SP value over MH invokes from - // the mask. Otherwise this register could be used for - // deoptimization information. - if (op == Op_CallStaticJava) { - MachCallStaticJavaNode* mcallstaticjava = (MachCallStaticJavaNode*) mcall; - if (mcallstaticjava->_method_handle_invoke) - proj->_rout.OR(Matcher::method_handle_invoke_SP_save_mask()); - } - add_call_kills(proj, regs, save_policy, exclude_soe); return node_cnt; diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 5f5e0520e7e..f92833e9e1c 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -1668,6 +1668,30 @@ void PhaseIdealLoop::insert_vector_post_loop(IdealLoopTree *loop, Node_List &old loop->record_for_igvn(); } +Node* PhaseIdealLoop::find_last_store_in_outer_loop(Node* store, const IdealLoopTree* outer_loop) { + assert(store != nullptr && store->is_Store(), "starting point should be a store node"); + // Follow the memory uses until we get out of the loop. + // Store nodes in the outer loop body were moved by PhaseIdealLoop::try_move_store_after_loop. + // Because of the conditions in try_move_store_after_loop (no other usage in the loop body + // except for the phi node associated with the loop head), we have the guarantee of a + // linear memory subgraph within the outer loop body. + Node* last = store; + Node* unique_next = store; + do { + last = unique_next; + for (DUIterator_Fast imax, l = last->fast_outs(imax); l < imax; l++) { + Node* use = last->fast_out(l); + if (use->is_Store() && use->in(MemNode::Memory) == last) { + if (is_member(outer_loop, get_ctrl(use))) { + assert(unique_next == last, "memory node should only have one usage in the loop body"); + unique_next = use; + } + } + } + } while (last != unique_next); + return last; +} + //------------------------------insert_post_loop------------------------------- // Insert post loops. Add a post loop to the given loop passed. Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree* loop, Node_List& old_new, @@ -1758,6 +1782,26 @@ Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree* loop, Node_List& old_new, cur_phi->set_req(LoopNode::EntryControl, fallnew); } } + // Store nodes that were moved to the outer loop by PhaseIdealLoop::try_move_store_after_loop + // do not have an associated Phi node. Such nodes are attached to the false projection of the CountedLoopEnd node, + // right after the execution of the inner CountedLoop. + // We have to make sure that such stores in the post loop have the right memory inputs from the main loop + // The moved store node is always attached right after the inner loop exit, and just before the safepoint + const Node* if_false = main_end->proj_out(false); + for (DUIterator j = if_false->outs(); if_false->has_out(j); j++) { + Node* store = if_false->out(j); + if (store->is_Store()) { + // We only make changes if the memory input of the store is outside the outer loop body, + // as this is when we would normally expect a Phi as input. If the memory input + // is in the loop body as well, then we can safely assume it is still correct as the entire + // body was cloned as a unit + if (!is_member(outer_loop, get_ctrl(store->in(MemNode::Memory)))) { + Node* mem_out = find_last_store_in_outer_loop(store, outer_loop); + Node* store_new = old_new[store->_idx]; + store_new->set_req(MemNode::Memory, mem_out); + } + } + } DEBUG_ONLY(ensure_zero_trip_guard_proj(post_head->in(LoopNode::EntryControl), false);) initialize_assertion_predicates_for_post_loop(main_head, post_head, first_node_index_in_cloned_loop_body); diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index a3e3be66583..4cb1862cbb9 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -5287,16 +5287,6 @@ void PhaseIdealLoop::build_and_optimize() { } } - // Move UnorderedReduction out of counted loop. Can be introduced by AutoVectorization. - if (C->has_loops() && !C->major_progress()) { - for (LoopTreeIterator iter(_ltree_root); !iter.done(); iter.next()) { - IdealLoopTree* lpt = iter.current(); - if (lpt->is_counted() && lpt->is_innermost()) { - move_unordered_reduction_out_of_loop(lpt); - } - } - } - // Keep loop predicates and perform optimizations with them // until no more loop optimizations could be done. // After that switch predicates off and do more loop optimizations. diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index bdccffa18b2..1101de81595 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -1380,6 +1380,9 @@ public: // during RCE, unrolling and aligning loops. void insert_pre_post_loops( IdealLoopTree *loop, Node_List &old_new, bool peel_only ); + // Find the last store in the body of an OuterStripMinedLoop when following memory uses + Node *find_last_store_in_outer_loop(Node* store, const IdealLoopTree* outer_loop); + // Add post loop after the given loop. Node *insert_post_loop(IdealLoopTree* loop, Node_List& old_new, CountedLoopNode* main_head, CountedLoopEndNode* main_end, @@ -1547,9 +1550,6 @@ public: IfTrueNode* create_new_if_for_multiversion(IfTrueNode* multiversioning_fast_proj); bool try_resume_optimizations_for_delayed_slow_loop(IdealLoopTree* lpt); - // Move an unordered Reduction out of loop if possible - void move_unordered_reduction_out_of_loop(IdealLoopTree* loop); - // Create a scheduled list of nodes control dependent on ctrl set. void scheduled_nodelist( IdealLoopTree *loop, VectorSet& ctrl, Node_List &sched ); // Has a use in the vector set diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index a9baac394a2..ae7b318ece4 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -4548,211 +4548,6 @@ void PhaseIdealLoop::maybe_multiversion_for_auto_vectorization_runtime_checks(Id do_multiversioning(lpt, old_new); } -// Returns true if the Reduction node is unordered. -static bool is_unordered_reduction(Node* n) { - return n->is_Reduction() && !n->as_Reduction()->requires_strict_order(); -} - -// Having ReductionNodes in the loop is expensive. They need to recursively -// fold together the vector values, for every vectorized loop iteration. If -// we encounter the following pattern, we can vector accumulate the values -// inside the loop, and only have a single UnorderedReduction after the loop. -// -// Note: UnorderedReduction represents a ReductionNode which does not require -// calculating in strict order. -// -// CountedLoop init -// | | -// +------+ | +-----------------------+ -// | | | | -// PhiNode (s) | -// | | -// | Vector | -// | | | -// UnorderedReduction (first_ur) | -// | | -// ... Vector | -// | | | -// UnorderedReduction (last_ur) | -// | | -// +---------------------+ -// -// We patch the graph to look like this: -// -// CountedLoop identity_vector -// | | -// +-------+ | +---------------+ -// | | | | -// PhiNode (v) | -// | | -// | Vector | -// | | | -// VectorAccumulator | -// | | -// ... Vector | -// | | | -// init VectorAccumulator | -// | | | | -// UnorderedReduction +-----------+ -// -// We turned the scalar (s) Phi into a vectorized one (v). In the loop, we -// use vector_accumulators, which do the same reductions, but only element -// wise. This is a single operation per vector_accumulator, rather than many -// for a UnorderedReduction. We can then reduce the last vector_accumulator -// after the loop, and also reduce the init value into it. -// -// We can not do this with all reductions. Some reductions do not allow the -// reordering of operations (for example float addition/multiplication require -// strict order). -void PhaseIdealLoop::move_unordered_reduction_out_of_loop(IdealLoopTree* loop) { - assert(!C->major_progress() && loop->is_counted() && loop->is_innermost(), "sanity"); - - // Find all Phi nodes with an unordered Reduction on backedge. - CountedLoopNode* cl = loop->_head->as_CountedLoop(); - for (DUIterator_Fast jmax, j = cl->fast_outs(jmax); j < jmax; j++) { - Node* phi = cl->fast_out(j); - // We have a phi with a single use, and an unordered Reduction on the backedge. - if (!phi->is_Phi() || phi->outcnt() != 1 || !is_unordered_reduction(phi->in(2))) { - continue; - } - - ReductionNode* last_ur = phi->in(2)->as_Reduction(); - assert(!last_ur->requires_strict_order(), "must be"); - - // Determine types - const TypeVect* vec_t = last_ur->vect_type(); - uint vector_length = vec_t->length(); - BasicType bt = vec_t->element_basic_type(); - - // Convert opcode from vector-reduction -> scalar -> normal-vector-op - const int sopc = VectorNode::scalar_opcode(last_ur->Opcode(), bt); - const int vopc = VectorNode::opcode(sopc, bt); - if (!Matcher::match_rule_supported_vector(vopc, vector_length, bt)) { - DEBUG_ONLY( last_ur->dump(); ) - assert(false, "do not have normal vector op for this reduction"); - continue; // not implemented -> fails - } - - // Traverse up the chain of unordered Reductions, checking that it loops back to - // the phi. Check that all unordered Reductions only have a single use, except for - // the last (last_ur), which only has phi as a use in the loop, and all other uses - // are outside the loop. - ReductionNode* current = last_ur; - ReductionNode* first_ur = nullptr; - while (true) { - assert(!current->requires_strict_order(), "sanity"); - - // Expect no ctrl and a vector_input from within the loop. - Node* ctrl = current->in(0); - Node* vector_input = current->in(2); - if (ctrl != nullptr || get_ctrl(vector_input) != cl) { - DEBUG_ONLY( current->dump(1); ) - assert(false, "reduction has ctrl or bad vector_input"); - break; // Chain traversal fails. - } - - assert(current->vect_type() != nullptr, "must have vector type"); - if (current->vect_type() != last_ur->vect_type()) { - // Reductions do not have the same vector type (length and element type). - break; // Chain traversal fails. - } - - // Expect single use of an unordered Reduction, except for last_ur. - if (current == last_ur) { - // Expect all uses to be outside the loop, except phi. - for (DUIterator_Fast kmax, k = current->fast_outs(kmax); k < kmax; k++) { - Node* use = current->fast_out(k); - if (use != phi && ctrl_or_self(use) == cl) { - DEBUG_ONLY( current->dump(-1); ) - assert(false, "reduction has use inside loop"); - // Should not be allowed by SuperWord::mark_reductions - return; // bail out of optimization - } - } - } else { - if (current->outcnt() != 1) { - break; // Chain traversal fails. - } - } - - // Expect another unordered Reduction or phi as the scalar input. - Node* scalar_input = current->in(1); - if (is_unordered_reduction(scalar_input) && - scalar_input->Opcode() == current->Opcode()) { - // Move up the unordered Reduction chain. - current = scalar_input->as_Reduction(); - assert(!current->requires_strict_order(), "must be"); - } else if (scalar_input == phi) { - // Chain terminates at phi. - first_ur = current; - current = nullptr; - break; // Success. - } else { - // scalar_input is neither phi nor a matching reduction - // Can for example be scalar reduction when we have - // partial vectorization. - break; // Chain traversal fails. - } - } - if (current != nullptr) { - // Chain traversal was not successful. - continue; - } - assert(first_ur != nullptr, "must have successfully terminated chain traversal"); - - Node* identity_scalar = ReductionNode::make_identity_con_scalar(_igvn, sopc, bt); - set_root_as_ctrl(identity_scalar); - VectorNode* identity_vector = VectorNode::scalar2vector(identity_scalar, vector_length, bt); - register_new_node(identity_vector, C->root()); - assert(vec_t == identity_vector->vect_type(), "matching vector type"); - VectorNode::trace_new_vector(identity_vector, "Unordered Reduction"); - - // Turn the scalar phi into a vector phi. - _igvn.rehash_node_delayed(phi); - Node* init = phi->in(1); // Remember init before replacing it. - phi->set_req_X(1, identity_vector, &_igvn); - phi->as_Type()->set_type(vec_t); - _igvn.set_type(phi, vec_t); - - // Traverse down the chain of unordered Reductions, and replace them with vector_accumulators. - current = first_ur; - while (true) { - // Create vector_accumulator to replace current. - Node* last_vector_accumulator = current->in(1); - Node* vector_input = current->in(2); - VectorNode* vector_accumulator = VectorNode::make(vopc, last_vector_accumulator, vector_input, vec_t); - register_new_node(vector_accumulator, cl); - _igvn.replace_node(current, vector_accumulator); - VectorNode::trace_new_vector(vector_accumulator, "Unordered Reduction"); - if (current == last_ur) { - break; - } - current = vector_accumulator->unique_out()->as_Reduction(); - assert(!current->requires_strict_order(), "must be"); - } - - // Create post-loop reduction. - Node* last_accumulator = phi->in(2); - Node* post_loop_reduction = ReductionNode::make(sopc, nullptr, init, last_accumulator, bt); - - // Take over uses of last_accumulator that are not in the loop. - for (DUIterator i = last_accumulator->outs(); last_accumulator->has_out(i); i++) { - Node* use = last_accumulator->out(i); - if (use != phi && use != post_loop_reduction) { - assert(ctrl_or_self(use) != cl, "use must be outside loop"); - use->replace_edge(last_accumulator, post_loop_reduction, &_igvn); - --i; - } - } - register_new_node(post_loop_reduction, get_late_ctrl(post_loop_reduction, cl)); - VectorNode::trace_new_vector(post_loop_reduction, "Unordered Reduction"); - - assert(last_accumulator->outcnt() == 2, "last_accumulator has 2 uses: phi and post_loop_reduction"); - assert(post_loop_reduction->outcnt() > 0, "should have taken over all non loop uses of last_accumulator"); - assert(phi->outcnt() == 1, "accumulator is the only use of phi"); - } -} - void DataNodeGraph::clone_data_nodes(Node* new_ctrl) { for (uint i = 0; i < _data_nodes.size(); i++) { clone(_data_nodes[i], new_ctrl); diff --git a/src/hotspot/share/opto/machnode.cpp b/src/hotspot/share/opto/machnode.cpp index e5acad98c09..5da929e4748 100644 --- a/src/hotspot/share/opto/machnode.cpp +++ b/src/hotspot/share/opto/machnode.cpp @@ -772,8 +772,6 @@ bool MachCallJavaNode::cmp( const Node &n ) const { } #ifndef PRODUCT void MachCallJavaNode::dump_spec(outputStream *st) const { - if (_method_handle_invoke) - st->print("MethodHandle "); if (_method) { _method->print_short_name(st); st->print(" "); @@ -794,10 +792,7 @@ const RegMask &MachCallJavaNode::in_RegMask(uint idx) const { } // Values outside the domain represent debug info Matcher* m = Compile::current()->matcher(); - // If this call is a MethodHandle invoke we have to use a different - // debugmask which does not include the register we use to save the - // SP over MH invokes. - RegMask** debugmask = _method_handle_invoke ? m->idealreg2mhdebugmask : m->idealreg2debugmask; + RegMask** debugmask = m->idealreg2debugmask; return *debugmask[in(idx)->ideal_reg()]; } diff --git a/src/hotspot/share/opto/machnode.hpp b/src/hotspot/share/opto/machnode.hpp index 5490f717a25..43e9a35df34 100644 --- a/src/hotspot/share/opto/machnode.hpp +++ b/src/hotspot/share/opto/machnode.hpp @@ -960,7 +960,6 @@ public: ciMethod* _method; // Method being direct called bool _override_symbolic_info; // Override symbolic call site info from bytecode bool _optimized_virtual; // Tells if node is a static call or an optimized virtual - bool _method_handle_invoke; // Tells if the call has to preserve SP bool _arg_escape; // ArgEscape in parameter list MachCallJavaNode() : MachCallNode(), _override_symbolic_info(false) { init_class_id(Class_MachCallJava); diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp index b43bc05c465..7d73487cf88 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -50,8 +50,6 @@ const RegMask *Matcher::idealreg2regmask[_last_machine_leaf]; RegMask Matcher::mreg2regmask[_last_Mach_Reg]; RegMask Matcher::caller_save_regmask; RegMask Matcher::caller_save_regmask_exclude_soe; -RegMask Matcher::mh_caller_save_regmask; -RegMask Matcher::mh_caller_save_regmask_exclude_soe; RegMask Matcher::STACK_ONLY_mask; RegMask Matcher::c_frame_ptr_mask; const uint Matcher::_begin_rematerialize = _BEGIN_REMATERIALIZE; @@ -114,21 +112,6 @@ Matcher::Matcher() idealreg2debugmask [Op_RegFlags] = nullptr; idealreg2debugmask [Op_RegVectMask] = nullptr; - idealreg2mhdebugmask[Op_RegI] = nullptr; - idealreg2mhdebugmask[Op_RegN] = nullptr; - idealreg2mhdebugmask[Op_RegL] = nullptr; - idealreg2mhdebugmask[Op_RegF] = nullptr; - idealreg2mhdebugmask[Op_RegD] = nullptr; - idealreg2mhdebugmask[Op_RegP] = nullptr; - idealreg2mhdebugmask[Op_VecA] = nullptr; - idealreg2mhdebugmask[Op_VecS] = nullptr; - idealreg2mhdebugmask[Op_VecD] = nullptr; - idealreg2mhdebugmask[Op_VecX] = nullptr; - idealreg2mhdebugmask[Op_VecY] = nullptr; - idealreg2mhdebugmask[Op_VecZ] = nullptr; - idealreg2mhdebugmask[Op_RegFlags] = nullptr; - idealreg2mhdebugmask[Op_RegVectMask] = nullptr; - DEBUG_ONLY(_mem_node = nullptr;) // Ideal memory node consumed by mach node } @@ -465,7 +448,7 @@ int Matcher::scalable_predicate_reg_slots() { return round_up_power_of_2(slots); } -#define NOF_STACK_MASKS (3*13) +#define NOF_STACK_MASKS (2*13) // Create the initial stack mask used by values spilling to the stack. // Disallow any debug info in outgoing argument areas by setting the @@ -480,51 +463,12 @@ void Matcher::init_first_stack_mask() { new (rms + i) RegMask(C->comp_arena()); } - idealreg2spillmask [Op_RegN] = &rms[0]; - idealreg2spillmask [Op_RegI] = &rms[1]; - idealreg2spillmask [Op_RegL] = &rms[2]; - idealreg2spillmask [Op_RegF] = &rms[3]; - idealreg2spillmask [Op_RegD] = &rms[4]; - idealreg2spillmask [Op_RegP] = &rms[5]; - - idealreg2debugmask [Op_RegN] = &rms[6]; - idealreg2debugmask [Op_RegI] = &rms[7]; - idealreg2debugmask [Op_RegL] = &rms[8]; - idealreg2debugmask [Op_RegF] = &rms[9]; - idealreg2debugmask [Op_RegD] = &rms[10]; - idealreg2debugmask [Op_RegP] = &rms[11]; - - idealreg2mhdebugmask[Op_RegN] = &rms[12]; - idealreg2mhdebugmask[Op_RegI] = &rms[13]; - idealreg2mhdebugmask[Op_RegL] = &rms[14]; - idealreg2mhdebugmask[Op_RegF] = &rms[15]; - idealreg2mhdebugmask[Op_RegD] = &rms[16]; - idealreg2mhdebugmask[Op_RegP] = &rms[17]; - - idealreg2spillmask [Op_VecA] = &rms[18]; - idealreg2spillmask [Op_VecS] = &rms[19]; - idealreg2spillmask [Op_VecD] = &rms[20]; - idealreg2spillmask [Op_VecX] = &rms[21]; - idealreg2spillmask [Op_VecY] = &rms[22]; - idealreg2spillmask [Op_VecZ] = &rms[23]; - - idealreg2debugmask [Op_VecA] = &rms[24]; - idealreg2debugmask [Op_VecS] = &rms[25]; - idealreg2debugmask [Op_VecD] = &rms[26]; - idealreg2debugmask [Op_VecX] = &rms[27]; - idealreg2debugmask [Op_VecY] = &rms[28]; - idealreg2debugmask [Op_VecZ] = &rms[29]; - - idealreg2mhdebugmask[Op_VecA] = &rms[30]; - idealreg2mhdebugmask[Op_VecS] = &rms[31]; - idealreg2mhdebugmask[Op_VecD] = &rms[32]; - idealreg2mhdebugmask[Op_VecX] = &rms[33]; - idealreg2mhdebugmask[Op_VecY] = &rms[34]; - idealreg2mhdebugmask[Op_VecZ] = &rms[35]; - - idealreg2spillmask [Op_RegVectMask] = &rms[36]; - idealreg2debugmask [Op_RegVectMask] = &rms[37]; - idealreg2mhdebugmask[Op_RegVectMask] = &rms[38]; + int index = 0; + for (int i = Op_RegN; i <= Op_RegVectMask; ++i) { + idealreg2spillmask[i] = &rms[index++]; + idealreg2debugmask[i] = &rms[index++]; + } + assert(index == NOF_STACK_MASKS, "wrong size"); // At first, start with the empty mask C->FIRST_STACK_mask().Clear(); @@ -710,26 +654,10 @@ void Matcher::init_first_stack_mask() { *idealreg2debugmask [Op_VecY] = *idealreg2spillmask[Op_VecY]; *idealreg2debugmask [Op_VecZ] = *idealreg2spillmask[Op_VecZ]; - *idealreg2mhdebugmask[Op_RegN] = *idealreg2spillmask[Op_RegN]; - *idealreg2mhdebugmask[Op_RegI] = *idealreg2spillmask[Op_RegI]; - *idealreg2mhdebugmask[Op_RegL] = *idealreg2spillmask[Op_RegL]; - *idealreg2mhdebugmask[Op_RegF] = *idealreg2spillmask[Op_RegF]; - *idealreg2mhdebugmask[Op_RegD] = *idealreg2spillmask[Op_RegD]; - *idealreg2mhdebugmask[Op_RegP] = *idealreg2spillmask[Op_RegP]; - *idealreg2mhdebugmask[Op_RegVectMask] = *idealreg2spillmask[Op_RegVectMask]; - - *idealreg2mhdebugmask[Op_VecA] = *idealreg2spillmask[Op_VecA]; - *idealreg2mhdebugmask[Op_VecS] = *idealreg2spillmask[Op_VecS]; - *idealreg2mhdebugmask[Op_VecD] = *idealreg2spillmask[Op_VecD]; - *idealreg2mhdebugmask[Op_VecX] = *idealreg2spillmask[Op_VecX]; - *idealreg2mhdebugmask[Op_VecY] = *idealreg2spillmask[Op_VecY]; - *idealreg2mhdebugmask[Op_VecZ] = *idealreg2spillmask[Op_VecZ]; - // Prevent stub compilations from attempting to reference // callee-saved (SOE) registers from debug info bool exclude_soe = !Compile::current()->is_method_compilation(); RegMask* caller_save_mask = exclude_soe ? &caller_save_regmask_exclude_soe : &caller_save_regmask; - RegMask* mh_caller_save_mask = exclude_soe ? &mh_caller_save_regmask_exclude_soe : &mh_caller_save_regmask; idealreg2debugmask[Op_RegN]->SUBTRACT(*caller_save_mask); idealreg2debugmask[Op_RegI]->SUBTRACT(*caller_save_mask); @@ -745,21 +673,6 @@ void Matcher::init_first_stack_mask() { idealreg2debugmask[Op_VecX]->SUBTRACT(*caller_save_mask); idealreg2debugmask[Op_VecY]->SUBTRACT(*caller_save_mask); idealreg2debugmask[Op_VecZ]->SUBTRACT(*caller_save_mask); - - idealreg2mhdebugmask[Op_RegN]->SUBTRACT(*mh_caller_save_mask); - idealreg2mhdebugmask[Op_RegI]->SUBTRACT(*mh_caller_save_mask); - idealreg2mhdebugmask[Op_RegL]->SUBTRACT(*mh_caller_save_mask); - idealreg2mhdebugmask[Op_RegF]->SUBTRACT(*mh_caller_save_mask); - idealreg2mhdebugmask[Op_RegD]->SUBTRACT(*mh_caller_save_mask); - idealreg2mhdebugmask[Op_RegP]->SUBTRACT(*mh_caller_save_mask); - idealreg2mhdebugmask[Op_RegVectMask]->SUBTRACT(*mh_caller_save_mask); - - idealreg2mhdebugmask[Op_VecA]->SUBTRACT(*mh_caller_save_mask); - idealreg2mhdebugmask[Op_VecS]->SUBTRACT(*mh_caller_save_mask); - idealreg2mhdebugmask[Op_VecD]->SUBTRACT(*mh_caller_save_mask); - idealreg2mhdebugmask[Op_VecX]->SUBTRACT(*mh_caller_save_mask); - idealreg2mhdebugmask[Op_VecY]->SUBTRACT(*mh_caller_save_mask); - idealreg2mhdebugmask[Op_VecZ]->SUBTRACT(*mh_caller_save_mask); } //---------------------------is_save_on_entry---------------------------------- @@ -984,23 +897,15 @@ void Matcher::init_spill_mask( Node *ret ) { if (_register_save_policy[i] == 'C' || _register_save_policy[i] == 'A') { caller_save_regmask.Insert(i); - mh_caller_save_regmask.Insert(i); } // Exclude save-on-entry registers from debug masks for stub compilations. if (_register_save_policy[i] == 'C' || _register_save_policy[i] == 'A' || _register_save_policy[i] == 'E') { caller_save_regmask_exclude_soe.Insert(i); - mh_caller_save_regmask_exclude_soe.Insert(i); } } - // Also exclude the register we use to save the SP for MethodHandle - // invokes to from the corresponding MH debug masks - const RegMask sp_save_mask = method_handle_invoke_SP_save_mask(); - mh_caller_save_regmask.OR(sp_save_mask); - mh_caller_save_regmask_exclude_soe.OR(sp_save_mask); - // Grab the Frame Pointer Node *fp = ret->in(TypeFunc::FramePtr); // Share frame pointer while making spill ops @@ -1272,7 +1177,6 @@ MachNode *Matcher::match_sfpt( SafePointNode *sfpt ) { CallNode *call; const TypeTuple *domain; ciMethod* method = nullptr; - bool is_method_handle_invoke = false; // for special kill effects if( sfpt->is_Call() ) { call = sfpt->as_Call(); domain = call->tf()->domain(); @@ -1298,13 +1202,8 @@ MachNode *Matcher::match_sfpt( SafePointNode *sfpt ) { method = call_java->method(); mcall_java->_method = method; mcall_java->_optimized_virtual = call_java->is_optimized_virtual(); - is_method_handle_invoke = call_java->is_method_handle_invoke(); - mcall_java->_method_handle_invoke = is_method_handle_invoke; mcall_java->_override_symbolic_info = call_java->override_symbolic_info(); mcall_java->_arg_escape = call_java->arg_escape(); - if (is_method_handle_invoke) { - C->set_has_method_handle_invokes(true); - } if( mcall_java->is_MachCallStaticJava() ) mcall_java->as_MachCallStaticJava()->_name = call_java->as_CallStaticJava()->_name; diff --git a/src/hotspot/share/opto/matcher.hpp b/src/hotspot/share/opto/matcher.hpp index f4b0a7db873..0b609b70ab5 100644 --- a/src/hotspot/share/opto/matcher.hpp +++ b/src/hotspot/share/opto/matcher.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -177,7 +177,6 @@ public: static const RegMask *idealreg2regmask[]; RegMask *idealreg2spillmask [_last_machine_leaf]; RegMask *idealreg2debugmask [_last_machine_leaf]; - RegMask *idealreg2mhdebugmask[_last_machine_leaf]; void init_spill_mask( Node *ret ); // Convert machine register number to register mask static uint mreg2regmask_max; @@ -185,8 +184,6 @@ public: static RegMask STACK_ONLY_mask; static RegMask caller_save_regmask; static RegMask caller_save_regmask_exclude_soe; - static RegMask mh_caller_save_regmask; - static RegMask mh_caller_save_regmask_exclude_soe; MachNode* mach_null() const { return _mach_null; } @@ -424,8 +421,6 @@ public: // a code which use multiply for division by constant. static bool use_asm_for_ldiv_by_con( jlong divisor ); - static const RegMask method_handle_invoke_SP_save_mask(); - // Java-Interpreter calling convention // (what you use when calling between compiled-Java and Interpreted-Java diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index f358729dfb2..2080b7cbeb5 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -4229,10 +4229,7 @@ MemBarNode* MemBarNode::make(Compile* C, int opcode, int atp, Node* pn) { } void MemBarNode::remove(PhaseIterGVN *igvn) { - if (outcnt() != 2) { - assert(Opcode() == Op_Initialize, "Only seen when there are no use of init memory"); - assert(outcnt() == 1, "Only control then"); - } + assert(outcnt() > 0 && outcnt() <= 2, "Only one or two out edges allowed"); if (trailing_store() || trailing_load_store()) { MemBarNode* leading = leading_membar(); if (leading != nullptr) { diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp index 90d24b609a7..84c01c68e38 100644 --- a/src/hotspot/share/opto/output.cpp +++ b/src/hotspot/share/opto/output.cpp @@ -992,7 +992,6 @@ void PhaseOutput::Process_OopMap_Node(MachNode *mach, int current_offset) { MachCallNode *mcall; int safepoint_pc_offset = current_offset; - bool is_method_handle_invoke = false; bool return_oop = false; bool has_ea_local_in_scope = sfn->_has_ea_local_in_scope; bool arg_escape = false; @@ -1004,12 +1003,7 @@ void PhaseOutput::Process_OopMap_Node(MachNode *mach, int current_offset) { } else { mcall = mach->as_MachCall(); - // Is the call a MethodHandle call? if (mcall->is_MachCallJava()) { - if (mcall->as_MachCallJava()->_method_handle_invoke) { - assert(C->has_method_handle_invokes(), "must have been set during call generation"); - is_method_handle_invoke = true; - } arg_escape = mcall->as_MachCallJava()->_arg_escape; } @@ -1192,7 +1186,6 @@ void PhaseOutput::Process_OopMap_Node(MachNode *mach, int current_offset) { jvms->bci(), jvms->should_reexecute(), rethrow_exception, - is_method_handle_invoke, return_oop, has_ea_local_in_scope, arg_escape, @@ -1370,9 +1363,6 @@ CodeBuffer* PhaseOutput::init_buffer() { exception_handler_req + deopt_handler_req; // deopt handler - if (C->has_method_handle_invokes()) - total_req += deopt_handler_req; // deopt MH handler - CodeBuffer* cb = code_buffer(); cb->set_const_section_alignment(constant_table().alignment()); cb->initialize(total_req, _buf_sizes._reloc); @@ -1806,13 +1796,6 @@ void PhaseOutput::fill_buffer(C2_MacroAssembler* masm, uint* blk_starts) { } // Emit the deopt handler code. _code_offsets.set_value(CodeOffsets::Deopt, HandlerImpl::emit_deopt_handler(masm)); - - // Emit the MethodHandle deopt handler code (if required). - if (C->has_method_handle_invokes() && !C->failing()) { - // We can use the same code as for the normal deopt handler, we - // just need a different entry point address. - _code_offsets.set_value(CodeOffsets::DeoptMH, HandlerImpl::emit_deopt_handler(masm)); - } } // One last check for failed CodeBuffer::expand: diff --git a/src/hotspot/share/opto/phasetype.hpp b/src/hotspot/share/opto/phasetype.hpp index 5c733c7dc0a..f24938b51c1 100644 --- a/src/hotspot/share/opto/phasetype.hpp +++ b/src/hotspot/share/opto/phasetype.hpp @@ -89,10 +89,9 @@ flags(PHASEIDEALLOOP2, "PhaseIdealLoop 2") \ flags(PHASEIDEALLOOP3, "PhaseIdealLoop 3") \ flags(AUTO_VECTORIZATION1_BEFORE_APPLY, "AutoVectorization 1, before Apply") \ - flags(AUTO_VECTORIZATION2_AFTER_REORDER, "AutoVectorization 2, after Apply Memop Reordering") \ - flags(AUTO_VECTORIZATION3_AFTER_ADJUST_LIMIT, "AutoVectorization 3, after Adjusting Pre-loop Limit") \ - flags(AUTO_VECTORIZATION4_AFTER_SPECULATIVE_RUNTIME_CHECKS, "AutoVectorization 4, after Adding Speculative Runtime Checks") \ - flags(AUTO_VECTORIZATION5_AFTER_APPLY, "AutoVectorization 5, after Apply") \ + flags(AUTO_VECTORIZATION3_AFTER_ADJUST_LIMIT, "AutoVectorization 2, after Adjusting Pre-loop Limit") \ + flags(AUTO_VECTORIZATION4_AFTER_SPECULATIVE_RUNTIME_CHECKS, "AutoVectorization 3, after Adding Speculative Runtime Checks") \ + flags(AUTO_VECTORIZATION5_AFTER_APPLY, "AutoVectorization 4, after Apply") \ flags(BEFORE_CCP1, "Before PhaseCCP 1") \ flags(CCP1, "PhaseCCP 1") \ flags(ITER_GVN2, "Iter GVN 2") \ diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp index 0d148edda6e..9de9fe5da03 100644 --- a/src/hotspot/share/opto/runtime.cpp +++ b/src/hotspot/share/opto/runtime.cpp @@ -1780,6 +1780,62 @@ static const TypeFunc* make_osr_end_Type() { return TypeFunc::make(domain, range); } +#ifndef PRODUCT +static void debug_print_convert_type(const Type** fields, int* argp, Node *parm) { + const BasicType bt = parm->bottom_type()->basic_type(); + fields[(*argp)++] = Type::get_const_basic_type(bt); + if (bt == T_LONG || bt == T_DOUBLE) { + fields[(*argp)++] = Type::HALF; + } +} + +static void update_arg_cnt(const Node* parm, int* arg_cnt) { + (*arg_cnt)++; + const BasicType bt = parm->bottom_type()->basic_type(); + if (bt == T_LONG || bt == T_DOUBLE) { + (*arg_cnt)++; + } +} + +const TypeFunc* OptoRuntime::debug_print_Type(Node* parm0, Node* parm1, + Node* parm2, Node* parm3, + Node* parm4, Node* parm5, + Node* parm6) { + int argcnt = 1; + if (parm0 != nullptr) { update_arg_cnt(parm0, &argcnt); + if (parm1 != nullptr) { update_arg_cnt(parm1, &argcnt); + if (parm2 != nullptr) { update_arg_cnt(parm2, &argcnt); + if (parm3 != nullptr) { update_arg_cnt(parm3, &argcnt); + if (parm4 != nullptr) { update_arg_cnt(parm4, &argcnt); + if (parm5 != nullptr) { update_arg_cnt(parm5, &argcnt); + if (parm6 != nullptr) { update_arg_cnt(parm6, &argcnt); + /* close each nested if ===> */ } } } } } } } + + // create input type (domain) + const Type** fields = TypeTuple::fields(argcnt); + int argp = TypeFunc::Parms; + fields[argp++] = TypePtr::NOTNULL; // static string pointer + + if (parm0 != nullptr) { debug_print_convert_type(fields, &argp, parm0); + if (parm1 != nullptr) { debug_print_convert_type(fields, &argp, parm1); + if (parm2 != nullptr) { debug_print_convert_type(fields, &argp, parm2); + if (parm3 != nullptr) { debug_print_convert_type(fields, &argp, parm3); + if (parm4 != nullptr) { debug_print_convert_type(fields, &argp, parm4); + if (parm5 != nullptr) { debug_print_convert_type(fields, &argp, parm5); + if (parm6 != nullptr) { debug_print_convert_type(fields, &argp, parm6); + /* close each nested if ===> */ } } } } } } } + + assert(argp == TypeFunc::Parms+argcnt, "correct decoding"); + const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms+argcnt, fields); + + // no result type needed + fields = TypeTuple::fields(1); + fields[TypeFunc::Parms+0] = nullptr; // void + const TypeTuple* range = TypeTuple::make(TypeFunc::Parms, fields); + return TypeFunc::make(domain, range); +} +#endif // PRODUCT + //------------------------------------------------------------------------------------- // register policy @@ -1913,9 +1969,6 @@ JRT_ENTRY_NO_ASYNC(address, OptoRuntime::handle_exception_C_helper(JavaThread* c current->set_exception_pc(pc); current->set_exception_handler_pc(handler_address); - - // Check if the exception PC is a MethodHandle call site. - current->set_is_method_handle_return(nm->is_method_handle_return(pc)); } // Restore correct return pc. Was saved above. diff --git a/src/hotspot/share/opto/runtime.hpp b/src/hotspot/share/opto/runtime.hpp index 40e436c0d5c..76e69fd9d36 100644 --- a/src/hotspot/share/opto/runtime.hpp +++ b/src/hotspot/share/opto/runtime.hpp @@ -737,6 +737,16 @@ private: return _dtrace_object_alloc_Type; } +#ifndef PRODUCT + // Signature for runtime calls in debug printing nodes, which depends on which nodes are actually passed + // Note: we do not allow more than 7 node arguments as GraphKit::make_runtime_call only allows 8, and we need + // one for the static string + static const TypeFunc* debug_print_Type(Node* parm0 = nullptr, Node* parm1 = nullptr, + Node* parm2 = nullptr, Node* parm3 = nullptr, + Node* parm4 = nullptr, Node* parm5 = nullptr, + Node* parm6 = nullptr); +#endif // PRODUCT + private: static NamedCounter * volatile _named_counters; diff --git a/src/hotspot/share/opto/stringopts.cpp b/src/hotspot/share/opto/stringopts.cpp index 25aa82870c3..420423dd246 100644 --- a/src/hotspot/share/opto/stringopts.cpp +++ b/src/hotspot/share/opto/stringopts.cpp @@ -53,6 +53,11 @@ class StringConcat : public ResourceObj { Node_List _uncommon_traps; // Uncommon traps that needs to be rewritten // to restart at the initial JVMState. + static constexpr uint STACKED_CONCAT_UPPER_BOUND = 256; // argument limit for a merged concat. + // The value 256 was derived by measuring + // compilation time on variable length sequences + // of stackable concatenations and chosen to keep + // a safe margin to any critical point. public: // Mode for converting arguments to Strings enum { @@ -295,6 +300,8 @@ StringConcat* StringConcat::merge(StringConcat* other, Node* arg) { } assert(result->_control.contains(other->_end), "what?"); assert(result->_control.contains(_begin), "what?"); + + uint arguments_appended = 0; for (int x = 0; x < num_arguments(); x++) { Node* argx = argument_uncast(x); if (argx == arg) { @@ -303,8 +310,21 @@ StringConcat* StringConcat::merge(StringConcat* other, Node* arg) { for (int y = 0; y < other->num_arguments(); y++) { result->append(other->argument(y), other->mode(y)); } + arguments_appended += other->num_arguments(); } else { result->append(argx, mode(x)); + arguments_appended++; + } + // Check if this concatenation would result in an excessive number of arguments + // -- leading to high memory use, compilation time, and later, a large number of IR nodes + // -- and bail out in that case. + if (arguments_appended > STACKED_CONCAT_UPPER_BOUND) { +#ifndef PRODUCT + if (PrintOptimizeStringConcat) { + tty->print_cr("Merge candidate of length %d exceeds argument limit", arguments_appended); + } +#endif + return nullptr; } } result->set_allocation(other->_begin); @@ -680,7 +700,7 @@ PhaseStringOpts::PhaseStringOpts(PhaseGVN* gvn): #endif StringConcat* merged = sc->merge(other, arg); - if (merged->validate_control_flow() && merged->validate_mem_flow()) { + if (merged != nullptr && merged->validate_control_flow() && merged->validate_mem_flow()) { #ifndef PRODUCT AtomicAccess::inc(&_stropts_merged); if (PrintOptimizeStringConcat) { diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index 2b3928781b8..c0f005048ec 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -40,7 +40,7 @@ SuperWord::SuperWord(const VLoopAnalyzer &vloop_analyzer) : NOT_PRODUCT(COMMA is_trace_superword_packset()) NOT_PRODUCT(COMMA is_trace_superword_rejections()) ), - _mem_ref_for_main_loop_alignment(nullptr), + _vpointer_for_main_loop_alignment(nullptr), _aw_for_main_loop_alignment(0), _do_vector_loop(phase()->C->do_vector_loop()), // whether to do vectorization/simd style _num_work_vecs(0), // amount of vector work we have @@ -455,11 +455,16 @@ bool SuperWord::transform_loop() { // // 8) The pairs are combined into vector sized packs. // -// 9) Reorder the memory slices to co-locate members of the memory packs. +// 9) The packs are split and filtered, to ensure correctness and that +// all packs have corresponding vector nodes implemented in the backend. // -// 10) Generate ideal vector nodes for the final set of packs and where necessary, -// inserting scalar promotion, vector creation from multiple scalars, and -// extraction of scalar values from vectors. +// 10) VTransform (see vtransform.hpp) +// - construct from PackSet +// - schedule (detect circles) +// - apply +// - align main loop +// - add runtime checks (aliasing and alignment) +// - build new loop with vector C2 nodes // // Runtime Checks: // Some required properties cannot be proven statically, and require a @@ -498,7 +503,7 @@ bool SuperWord::SLP_extract() { DEBUG_ONLY(verify_packs();) DEBUG_ONLY(verify_no_extract()); - return schedule_and_apply(); + return do_vtransform(); } int SuperWord::MemOp::cmp_by_group(MemOp* a, MemOp* b) { @@ -660,39 +665,9 @@ void SuperWord::create_adjacent_memop_pairs_in_one_group(const GrowableArrayfast_outs(imax); i < imax; i++) { - PhiNode* phi = cl->fast_out(i)->isa_Phi(); - if (phi != nullptr && _vloop.in_bb(phi) && phi->is_memory_phi()) { - Node* phi_tail = phi->in(LoopNode::LoopBackControl); - if (phi_tail != phi->in(LoopNode::EntryControl)) { - _heads.push(phi); - _tails.push(phi_tail->as_Mem()); - } - } - } - - NOT_PRODUCT( if (_vloop.is_trace_memory_slices()) { print(); } ) -} - -#ifndef PRODUCT -void VLoopMemorySlices::print() const { - tty->print_cr("\nVLoopMemorySlices::print: %s", - heads().length() > 0 ? "" : "NONE"); - for (int m = 0; m < heads().length(); m++) { - tty->print("%6d ", m); heads().at(m)->dump(); - tty->print(" "); tails().at(m)->dump(); - } -} -#endif - // Get all memory nodes of a slice, in reverse order void VLoopMemorySlices::get_slice_in_reverse_order(PhiNode* head, MemNode* tail, GrowableArray &slice) const { + assert(head != nullptr && tail != nullptr, "must be slice with memory state loop"); assert(slice.is_empty(), "start empty"); Node* n = tail; Node* prev = nullptr; @@ -1576,7 +1551,7 @@ void SuperWord::filter_packs_for_alignment() { MemNode const* mem = current->as_constrained()->mem_ref(); Node_List* pack = get_pack(mem); assert(pack != nullptr, "memop of final solution must still be packed"); - _mem_ref_for_main_loop_alignment = mem; + _vpointer_for_main_loop_alignment = &vpointer(mem); _aw_for_main_loop_alignment = pack->size() * mem->memory_size(); } } @@ -1631,7 +1606,7 @@ bool SuperWord::implemented(const Node_List* pack, const uint size) const { // 3 instructions (1 shuffle and two reduction ops). // However, this optimization assumes that these reductions stay in the loop // which may not be true any more in most cases after the introduction of: - // PhaseIdealLoop::move_unordered_reduction_out_of_loop + // See: VTransformReductionVectorNode::optimize_move_non_strict_order_reductions_out_of_loop // Hence, this heuristic has room for improvement. bool is_two_element_int_or_long_reduction = (size == 2) && (arith_type->basic_type() == T_INT || @@ -1807,7 +1782,7 @@ bool SuperWord::profitable(const Node_List* p) const { // This heuristic is a bit simplistic, and assumes that the reduction // vector stays in the loop. But in some cases, we can move the // reduction out of the loop, replacing it with a single vector op. - // See: PhaseIdealLoop::move_unordered_reduction_out_of_loop + // See: VTransformReductionVectorNode::optimize_move_non_strict_order_reductions_out_of_loop // Hence, this heuristic has room for improvement. #ifndef PRODUCT if (is_trace_superword_rejections()) { @@ -1946,7 +1921,9 @@ void PackSet::verify() const { } #endif -bool SuperWord::schedule_and_apply() const { +// Build VTransform from SuperWord Packset, and eventually apply it (create new vectorized C2 loop). +// See description at top of "vtransform.hpp". +bool SuperWord::do_vtransform() const { if (_packset.is_empty()) { return false; } // Make an empty transform. @@ -1959,7 +1936,7 @@ bool SuperWord::schedule_and_apply() const { is_trace_superword_info()); #endif VTransform vtransform(_vloop_analyzer, - _mem_ref_for_main_loop_alignment, + _vpointer_for_main_loop_alignment, _aw_for_main_loop_alignment NOT_PRODUCT(COMMA trace) ); @@ -1970,6 +1947,8 @@ bool SuperWord::schedule_and_apply() const { SuperWordVTransformBuilder builder(_packset, vtransform); } + vtransform.optimize(); + if (!vtransform.schedule()) { return false; } if (vtransform.has_store_to_load_forwarding_failure()) { return false; } @@ -1988,6 +1967,7 @@ bool SuperWord::schedule_and_apply() const { // Apply the vectorization, i.e. we irreversibly edit the C2 graph. At this point, all // correctness and profitability checks have passed, and the graph was successfully scheduled. +// See description at top of "vtransform.hpp". void VTransform::apply() { #ifndef PRODUCT if (_trace._info || TraceLoopOpts) { @@ -2002,9 +1982,6 @@ void VTransform::apply() { Compile* C = phase()->C; C->print_method(PHASE_AUTO_VECTORIZATION1_BEFORE_APPLY, 4, cl()); - _graph.apply_memops_reordering_with_schedule(); - C->print_method(PHASE_AUTO_VECTORIZATION2_AFTER_REORDER, 4, cl()); - adjust_pre_loop_limit_to_align_main_loop_vectors(); C->print_method(PHASE_AUTO_VECTORIZATION3_AFTER_ADJUST_LIMIT, 4, cl()); @@ -2016,102 +1993,11 @@ void VTransform::apply() { C->print_method(PHASE_AUTO_VECTORIZATION5_AFTER_APPLY, 4, cl()); } -// We prepare the memory graph for the replacement of scalar memops with vector memops. -// We reorder all slices in parallel, ensuring that the memops inside each slice are -// ordered according to the _schedule. This means that all packed memops are consecutive -// in the memory graph after the reordering. -void VTransformGraph::apply_memops_reordering_with_schedule() const { -#ifndef PRODUCT - assert(is_scheduled(), "must be already scheduled"); - if (_trace._info) { - print_memops_schedule(); - } -#endif - - ResourceMark rm; - int max_slices = phase()->C->num_alias_types(); - // When iterating over the schedule, we keep track of the current memory state, - // which is the Phi or a store in the loop. - GrowableArray current_state_in_slice(max_slices, max_slices, nullptr); - // The memory state after the loop is the last store inside the loop. If we reorder the - // loop we may have a different last store, and we need to adjust the uses accordingly. - GrowableArray old_last_store_in_slice(max_slices, max_slices, nullptr); - - const GrowableArray& mem_slice_head = _vloop_analyzer.memory_slices().heads(); - - // (1) Set up the initial memory state from Phi. And find the old last store. - for (int i = 0; i < mem_slice_head.length(); i++) { - Node* phi = mem_slice_head.at(i); - assert(phi->is_Phi(), "must be phi"); - int alias_idx = phase()->C->get_alias_index(phi->adr_type()); - current_state_in_slice.at_put(alias_idx, phi); - - // If we have a memory phi, we have a last store in the loop, find it over backedge. - StoreNode* last_store = phi->in(2)->as_Store(); - old_last_store_in_slice.at_put(alias_idx, last_store); - } - - // (2) Walk over schedule, append memops to the current state - // of that slice. If it is a Store, we take it as the new state. - for_each_memop_in_schedule([&] (MemNode* n) { - assert(n->is_Load() || n->is_Store(), "only loads or stores"); - int alias_idx = phase()->C->get_alias_index(n->adr_type()); - Node* current_state = current_state_in_slice.at(alias_idx); - if (current_state == nullptr) { - // If there are only loads in a slice, we never update the memory - // state in the loop, hence there is no phi for the memory state. - // We just keep the old memory state that was outside the loop. - assert(n->is_Load() && !in_bb(n->in(MemNode::Memory)), - "only loads can have memory state from outside loop"); - } else { - igvn().replace_input_of(n, MemNode::Memory, current_state); - if (n->is_Store()) { - current_state_in_slice.at_put(alias_idx, n); - } - } - }); - - // (3) For each slice, we add the current state to the backedge - // in the Phi. Further, we replace uses of the old last store - // with uses of the new last store (current_state). - GrowableArray uses_after_loop; - for (int i = 0; i < mem_slice_head.length(); i++) { - Node* phi = mem_slice_head.at(i); - int alias_idx = phase()->C->get_alias_index(phi->adr_type()); - Node* current_state = current_state_in_slice.at(alias_idx); - assert(current_state != nullptr, "slice is mapped"); - assert(current_state != phi, "did some work in between"); - assert(current_state->is_Store(), "sanity"); - igvn().replace_input_of(phi, 2, current_state); - - // Replace uses of old last store with current_state (new last store) - // Do it in two loops: first find all the uses, and change the graph - // in as second loop so that we do not break the iterator. - Node* last_store = old_last_store_in_slice.at(alias_idx); - assert(last_store != nullptr, "we have a old last store"); - uses_after_loop.clear(); - for (DUIterator_Fast kmax, k = last_store->fast_outs(kmax); k < kmax; k++) { - Node* use = last_store->fast_out(k); - if (!in_bb(use)) { - uses_after_loop.push(use); - } - } - for (int k = 0; k < uses_after_loop.length(); k++) { - Node* use = uses_after_loop.at(k); - for (uint j = 0; j < use->req(); j++) { - Node* def = use->in(j); - if (def == last_store) { - igvn().replace_input_of(use, j, current_state); - } - } - } - } -} - void VTransformGraph::apply_vectorization_for_each_vtnode(uint& max_vector_length, uint& max_vector_width) const { ResourceMark rm; VTransformApplyState apply_state(_vloop_analyzer, _vtnodes.length()); + // Apply: transform the node and connect with inputs (no backedges). for (int i = 0; i < _schedule.length(); i++) { VTransformNode* vtn = _schedule.at(i); VTransformApplyResult result = vtn->apply(apply_state); @@ -2121,6 +2007,16 @@ void VTransformGraph::apply_vectorization_for_each_vtnode(uint& max_vector_lengt max_vector_length = MAX2(max_vector_length, result.vector_length()); max_vector_width = MAX2(max_vector_width, result.vector_width()); } + + // Cleanup: connect backedges + for (int i = 0; i < _schedule.length(); i++) { + VTransformNode* vtn = _schedule.at(i); + vtn->apply_backedge(apply_state); + } + + // Memory uses after the loop: used to connect to old last store, + // now need to connect to new last store. + apply_state.fix_memory_state_uses_after_loop(); } // We call "apply" on every VTransformNode, which replaces the packed scalar nodes with vector nodes. @@ -2774,10 +2670,10 @@ bool VLoopMemorySlices::same_memory_slice(MemNode* m1, MemNode* m2) const { _vloop.phase()->C->get_alias_index(m2->adr_type()); } -LoadNode::ControlDependency VTransformLoadVectorNode::control_dependency() const { +LoadNode::ControlDependency SuperWordVTransformBuilder::load_control_dependency(const Node_List* pack) const { LoadNode::ControlDependency dep = LoadNode::DependsOnlyOnTest; - for (int i = 0; i < nodes().length(); i++) { - Node* n = nodes().at(i); + for (uint i = 0; i < pack->size(); i++) { + Node* n = pack->at(i); assert(n->is_Load(), "only meaningful for loads"); if (!n->depends_only_on_test()) { if (n->as_Load()->has_unknown_control_dependency() && @@ -2795,22 +2691,24 @@ LoadNode::ControlDependency VTransformLoadVectorNode::control_dependency() const // Find the memop pack with the maximum vector width, unless they were already // determined (e.g. by SuperWord::filter_packs_for_alignment()). -void VTransform::determine_mem_ref_and_aw_for_main_loop_alignment() { - if (_mem_ref_for_main_loop_alignment != nullptr) { - assert(VLoop::vectors_should_be_aligned(), "mem_ref only set if filtered for alignment"); +void VTransform::determine_vpointer_and_aw_for_main_loop_alignment() { + if (_vpointer_for_main_loop_alignment != nullptr) { + assert(VLoop::vectors_should_be_aligned(), "vpointer_for_main_loop_alignment only set if filtered for alignment"); return; } - MemNode const* mem_ref = nullptr; + VPointer const* vpointer = nullptr; int max_aw = 0; + bool vpointer_is_load = false; const GrowableArray& vtnodes = _graph.vtnodes(); for (int i = 0; i < vtnodes.length(); i++) { VTransformMemVectorNode* vtn = vtnodes.at(i)->isa_MemVector(); if (vtn == nullptr) { continue; } - MemNode* p0 = vtn->nodes().at(0)->as_Mem(); - int vw = p0->memory_size() * vtn->nodes().length(); + int vw = vtn->vpointer().size(); + bool vtn_is_load = vtn->is_load_in_loop(); + // Generally, we prefer to align with the largest memory op (load or store). // If there are multiple, then SuperWordAutomaticAlignment determines if we // prefer loads or stores. @@ -2820,15 +2718,16 @@ void VTransform::determine_mem_ref_and_aw_for_main_loop_alignment() { // it is worse if a store is split, and less bad if a load is split. // By default, we have SuperWordAutomaticAlignment=1, i.e. we align with a // store if possible, to avoid splitting that store. - bool prefer_store = mem_ref != nullptr && SuperWordAutomaticAlignment == 1 && mem_ref->is_Load() && p0->is_Store(); - bool prefer_load = mem_ref != nullptr && SuperWordAutomaticAlignment == 2 && mem_ref->is_Store() && p0->is_Load(); + bool prefer_store = SuperWordAutomaticAlignment == 1 && vpointer_is_load && !vtn_is_load; + bool prefer_load = SuperWordAutomaticAlignment == 2 && !vpointer_is_load && vtn_is_load; if (vw > max_aw || (vw == max_aw && (prefer_load || prefer_store))) { + vpointer = &vtn->vpointer(); max_aw = vw; - mem_ref = p0; + vpointer_is_load = vtn_is_load; } } - assert(mem_ref != nullptr && max_aw > 0, "found mem_ref and aw"); - _mem_ref_for_main_loop_alignment = mem_ref; + assert(vpointer != nullptr && max_aw > 0, "found vpointer and aw"); + _vpointer_for_main_loop_alignment = vpointer; _aw_for_main_loop_alignment = max_aw; } @@ -2842,13 +2741,17 @@ void VTransform::determine_mem_ref_and_aw_for_main_loop_alignment() { } \ // Ensure that the main loop vectors are aligned by adjusting the pre loop limit. We memory-align -// the address of "_mem_ref_for_main_loop_alignment" to "_aw_for_main_loop_alignment", which is a +// the address of "_vpointer_for_main_loop_alignment" to "_aw_for_main_loop_alignment", which is a // sufficiently large alignment width. We adjust the pre-loop iteration count by adjusting the // pre-loop limit. void VTransform::adjust_pre_loop_limit_to_align_main_loop_vectors() { - determine_mem_ref_and_aw_for_main_loop_alignment(); - const MemNode* align_to_ref = _mem_ref_for_main_loop_alignment; - const int aw = _aw_for_main_loop_alignment; + determine_vpointer_and_aw_for_main_loop_alignment(); + + assert(cl()->is_main_loop(), "can only do alignment for main loop"); + assert(_vpointer_for_main_loop_alignment != nullptr && + _vpointer_for_main_loop_alignment->is_valid() && + _aw_for_main_loop_alignment > 0, + "must have alignment reference and aw"); if (!VLoop::vectors_should_be_aligned() && SuperWordAutomaticAlignment == 0) { #ifdef ASSERT @@ -2859,8 +2762,8 @@ void VTransform::adjust_pre_loop_limit_to_align_main_loop_vectors() { return; } - assert(align_to_ref != nullptr && aw > 0, "must have alignment reference and aw"); - assert(cl()->is_main_loop(), "can only do alignment for main loop"); + const VPointer& p = *_vpointer_for_main_loop_alignment; + const int aw = _aw_for_main_loop_alignment; // The opaque node for the limit, where we adjust the input Opaque1Node* pre_opaq = _vloop.pre_loop_end()->limit()->as_Opaque1(); @@ -2875,10 +2778,7 @@ void VTransform::adjust_pre_loop_limit_to_align_main_loop_vectors() { Node* orig_limit = pre_opaq->original_loop_limit(); assert(orig_limit != nullptr && igvn().type(orig_limit) != Type::TOP, ""); - const VPointer& p = vpointer(align_to_ref); - assert(p.is_valid(), "sanity"); - - // For the main-loop, we want the address of align_to_ref to be memory aligned + // For the main-loop, we want the address of vpointer p to be memory aligned // with some alignment width (aw, a power of 2). When we enter the main-loop, // we know that iv is equal to the pre-loop limit. If we adjust the pre-loop // limit by executing adjust_pre_iter many extra iterations, we can change the @@ -3013,9 +2913,7 @@ void VTransform::adjust_pre_loop_limit_to_align_main_loop_vectors() { #ifdef ASSERT if (_trace._align_vector) { tty->print_cr("\nVTransform::adjust_pre_loop_limit_to_align_main_loop_vectors:"); - tty->print(" align_to_ref:"); - align_to_ref->dump(); - tty->print(" "); + tty->print(" vpointer_for_main_loop_alignment"); p.print_on(tty); tty->print_cr(" aw: %d", aw); tty->print_cr(" iv_stride: %d", iv_stride); diff --git a/src/hotspot/share/opto/superword.hpp b/src/hotspot/share/opto/superword.hpp index 0940e752f85..118e0aa042c 100644 --- a/src/hotspot/share/opto/superword.hpp +++ b/src/hotspot/share/opto/superword.hpp @@ -410,9 +410,9 @@ class SuperWord : public ResourceObj { PairSet _pairset; PackSet _packset; - // Memory reference, and the alignment width (aw) for which we align the main-loop, + // VPointer, and the alignment width (aw) for which we align the main-loop, // by adjusting the pre-loop limit. - MemNode const* _mem_ref_for_main_loop_alignment; + VPointer const* _vpointer_for_main_loop_alignment; int _aw_for_main_loop_alignment; public: @@ -657,7 +657,7 @@ private: bool is_velt_basic_type_compatible_use_def(Node* use, Node* def) const; - bool schedule_and_apply() const; + bool do_vtransform() const; }; #endif // SHARE_OPTO_SUPERWORD_HPP diff --git a/src/hotspot/share/opto/superwordVTransformBuilder.cpp b/src/hotspot/share/opto/superwordVTransformBuilder.cpp index dbc96c234a9..45c919ccffa 100644 --- a/src/hotspot/share/opto/superwordVTransformBuilder.cpp +++ b/src/hotspot/share/opto/superwordVTransformBuilder.cpp @@ -37,6 +37,10 @@ void SuperWordVTransformBuilder::build() { VectorSet vtn_memory_dependencies; // Shared, but cleared for every vtnode. build_inputs_for_vector_vtnodes(vtn_memory_dependencies); build_inputs_for_scalar_vtnodes(vtn_memory_dependencies); + + // Build vtnodes for all uses of nodes from the loop, and connect them + // as outputs to the nodes in the loop. + build_uses_after_loop(); } void SuperWordVTransformBuilder::build_vector_vtnodes_for_packed_nodes() { @@ -50,8 +54,8 @@ void SuperWordVTransformBuilder::build_vector_vtnodes_for_packed_nodes() { } void SuperWordVTransformBuilder::build_scalar_vtnodes_for_non_packed_nodes() { - for (int i = 0; i < _vloop_analyzer.body().body().length(); i++) { - Node* n = _vloop_analyzer.body().body().at(i); + for (uint i = 0; i < _vloop.lpt()->_body.size(); i++) { + Node* n = _vloop.lpt()->_body.at(i); if (_packset.get_pack(n) != nullptr) { continue; } VTransformNode* vtn = nullptr; @@ -61,6 +65,8 @@ void SuperWordVTransformBuilder::build_scalar_vtnodes_for_non_packed_nodes() { vtn = new (_vtransform.arena()) VTransformMemopScalarNode(_vtransform, mem, mem_p); } else if (n->is_Phi()) { vtn = new (_vtransform.arena()) VTransformLoopPhiNode(_vtransform, n->as_Phi()); + } else if (n->is_CountedLoop()) { + vtn = new (_vtransform.arena()) VTransformCountedLoopNode(_vtransform, n->as_CountedLoop()); } else if (n->is_CFG()) { vtn = new (_vtransform.arena()) VTransformCFGNode(_vtransform, n); } else { @@ -121,8 +127,8 @@ void SuperWordVTransformBuilder::build_inputs_for_vector_vtnodes(VectorSet& vtn_ } void SuperWordVTransformBuilder::build_inputs_for_scalar_vtnodes(VectorSet& vtn_memory_dependencies) { - for (int i = 0; i < _vloop_analyzer.body().body().length(); i++) { - Node* n = _vloop_analyzer.body().body().at(i); + for (uint i = 0; i < _vloop.lpt()->_body.size(); i++) { + Node* n = _vloop.lpt()->_body.at(i); VTransformNode* vtn = get_vtnode(n); if (vtn->isa_Vector() != nullptr) { continue; } vtn_memory_dependencies.clear(); // Add every dependency only once per vtn. @@ -135,18 +141,41 @@ void SuperWordVTransformBuilder::build_inputs_for_scalar_vtnodes(VectorSet& vtn_ init_req_with_scalar(n, vtn, MemNode::ValueIn); add_memory_dependencies_of_node_to_vtnode(n, vtn, vtn_memory_dependencies); } else if (n->is_CountedLoop()) { - continue; // Is "root", has no dependency. - } else if (n->is_Phi()) { - // CountedLoop Phi's: ignore backedge (and entry value). - assert(n->in(0) == _vloop.cl(), "only Phi's from the CountedLoop allowed"); - init_req_with_scalar(n, vtn, 0); - continue; + // Avoid self-loop, it only creates unnecessary issues in scheduling. + init_req_with_scalar(n, vtn, LoopNode::EntryControl); + init_req_with_scalar(n, vtn, LoopNode::LoopBackControl); } else { init_all_req_with_scalars(n, vtn); } } } +// Build vtnodes for all uses of nodes from the loop, and connect them +// as outputs to the nodes in the loop. +void SuperWordVTransformBuilder::build_uses_after_loop() { + for (uint i = 0; i < _vloop.lpt()->_body.size(); i++) { + Node* n = _vloop.lpt()->_body.at(i); + VTransformNode* vtn = get_vtnode(n); + + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node* use = n->fast_out(i); + + if (!_vloop.in_bb(use)) { + VTransformNode* vtn_use = get_vtnode_or_wrap_as_outer(use); + + // Set all edges + for (uint j = 0; j < use->req(); j++) { + Node* def = use->in(j); + if (n == def && vtn_use->in_req(j) != vtn) { + assert(vtn_use->in_req(j) == nullptr, "should not yet be set"); + vtn_use->init_req(j, vtn); + } + } + } + } + } +} + // Create a vtnode for each pack. No in/out edges set yet. VTransformVectorNode* SuperWordVTransformBuilder::make_vector_vtnode_for_pack(const Node_List* pack) const { Node* p0 = pack->at(0); @@ -159,7 +188,8 @@ VTransformVectorNode* SuperWordVTransformBuilder::make_vector_vtnode_for_pack(co if (p0->is_Load()) { const VPointer& scalar_p = _vloop_analyzer.vpointers().vpointer(p0->as_Load()); const VPointer vector_p(scalar_p.make_with_size(scalar_p.size() * vlen)); - vtn = new (_vtransform.arena()) VTransformLoadVectorNode(_vtransform, properties, vector_p, p0->adr_type()); + const LoadNode::ControlDependency control_dependency = load_control_dependency(pack); + vtn = new (_vtransform.arena()) VTransformLoadVectorNode(_vtransform, properties, vector_p, p0->adr_type(), control_dependency); } else if (p0->is_Store()) { const VPointer& scalar_p = _vloop_analyzer.vpointers().vpointer(p0->as_Store()); const VPointer vector_p(scalar_p.make_with_size(scalar_p.size() * vlen)); @@ -209,7 +239,6 @@ VTransformVectorNode* SuperWordVTransformBuilder::make_vector_vtnode_for_pack(co int vopc = VectorNode::opcode(sopc, bt); vtn = new (_vtransform.arena()) VTransformElementWiseVectorNode(_vtransform, p0->req(), properties, vopc); } - vtn->set_nodes(pack); return vtn; } @@ -276,15 +305,15 @@ VTransformNode* SuperWordVTransformBuilder::get_or_make_vtnode_vector_input_at_i // case of a ConvL2I, it can be int or some narrower type such // as short etc. But given we replicate the input of the Convert // node, we have to use the input type instead. - BasicType element_type = p0->is_Convert() ? p0->in(1)->bottom_type()->basic_type() : _vloop_analyzer.types().velt_basic_type(p0); - if (index == 2 && VectorNode::is_scalar_rotate(p0) && element_type == T_LONG) { + BasicType element_bt = p0->is_Convert() ? p0->in(1)->bottom_type()->basic_type() : _vloop_analyzer.types().velt_basic_type(p0); + if (index == 2 && VectorNode::is_scalar_rotate(p0) && element_bt == T_LONG) { // Scalar rotate has int rotation value, but the scalar rotate expects longs. assert(same_input->bottom_type()->isa_int(), "scalar rotate expects int rotation"); VTransformNode* conv = new (_vtransform.arena()) VTransformConvI2LNode(_vtransform); conv->init_req(1, same_input_vtn); same_input_vtn = conv; } - VTransformNode* replicate = new (_vtransform.arena()) VTransformReplicateNode(_vtransform, pack->size(), element_type); + VTransformNode* replicate = new (_vtransform.arena()) VTransformReplicateNode(_vtransform, pack->size(), element_bt); replicate->init_req(1, same_input_vtn); return replicate; } @@ -307,6 +336,7 @@ VTransformNode* SuperWordVTransformBuilder::get_vtnode_or_wrap_as_outer(Node* n) assert(!_vloop.in_bb(n), "only nodes outside the loop can be input nodes to the loop"); vtn = new (_vtransform.arena()) VTransformOuterNode(_vtransform, n); map_node_to_vtnode(n, vtn); + assert(vtn == get_vtnode_or_null(n), "consistency"); return vtn; } diff --git a/src/hotspot/share/opto/superwordVTransformBuilder.hpp b/src/hotspot/share/opto/superwordVTransformBuilder.hpp index 6ed8480209a..cc9dd225f01 100644 --- a/src/hotspot/share/opto/superwordVTransformBuilder.hpp +++ b/src/hotspot/share/opto/superwordVTransformBuilder.hpp @@ -56,6 +56,7 @@ private: void build_scalar_vtnodes_for_non_packed_nodes(); void build_inputs_for_vector_vtnodes(VectorSet& vtn_memory_dependencies); void build_inputs_for_scalar_vtnodes(VectorSet& vtn_memory_dependencies); + void build_uses_after_loop(); // Helper methods for building VTransform. VTransformNode* get_vtnode_or_null(Node* n) const { @@ -82,6 +83,7 @@ private: void init_all_req_with_scalars(Node* n, VTransformNode* vtn); void init_all_req_with_vectors(const Node_List* pack, VTransformNode* vtn); void add_memory_dependencies_of_node_to_vtnode(Node* n, VTransformNode* vtn, VectorSet& vtn_memory_dependencies); + LoadNode::ControlDependency load_control_dependency(const Node_List* pack) const; }; #endif // SHARE_OPTO_SUPERWORD_VTRANSFORM_BUILDER_HPP diff --git a/src/hotspot/share/opto/traceAutoVectorizationTag.hpp b/src/hotspot/share/opto/traceAutoVectorizationTag.hpp index 6713ed6cac6..d996173aeb4 100644 --- a/src/hotspot/share/opto/traceAutoVectorizationTag.hpp +++ b/src/hotspot/share/opto/traceAutoVectorizationTag.hpp @@ -45,10 +45,11 @@ flags(SW_PACKSET, "Trace SuperWord packset at different stages") \ flags(SW_INFO, "Trace SuperWord info (equivalent to TraceSuperWord)") \ flags(SW_VERBOSE, "Trace SuperWord verbose (all SW tags enabled)") \ + flags(VTRANSFORM, "Trace VTransform Graph") \ + flags(OPTIMIZATION, "Trace VTransform::optimize") \ flags(ALIGN_VECTOR, "Trace AlignVector") \ flags(SPECULATIVE_ALIASING_ANALYSIS, "Trace Speculative Aliasing Analysis") \ flags(SPECULATIVE_RUNTIME_CHECKS, "Trace VTransform::apply_speculative_runtime_checks") \ - flags(VTRANSFORM, "Trace VTransform Graph") \ flags(ALL, "Trace everything (very verbose)") #define table_entry(name, description) name, diff --git a/src/hotspot/share/opto/vectorization.cpp b/src/hotspot/share/opto/vectorization.cpp index 8e0f0980ff7..5c4e15fdbb9 100644 --- a/src/hotspot/share/opto/vectorization.cpp +++ b/src/hotspot/share/opto/vectorization.cpp @@ -182,6 +182,11 @@ VStatus VLoopAnalyzer::setup_submodules_helper() { _reductions.mark_reductions(); } + VStatus body_status = _body.construct(); + if (!body_status.is_success()) { + return body_status; + } + _memory_slices.find_memory_slices(); // If there is no memory slice detected, it means there is no store. @@ -192,11 +197,6 @@ VStatus VLoopAnalyzer::setup_submodules_helper() { return VStatus::make_failure(VLoopAnalyzer::FAILURE_NO_REDUCTION_OR_STORE); } - VStatus body_status = _body.construct(); - if (!body_status.is_success()) { - return body_status; - } - _types.compute_vector_element_type(); _vpointers.compute_vpointers(); @@ -206,6 +206,64 @@ VStatus VLoopAnalyzer::setup_submodules_helper() { return VStatus::make_success(); } +// There are 2 kinds of slices: +// - No memory phi: only loads. All have the same input memory state from before the loop. +// - With memory phi. Chain of memory operations inside the loop. +void VLoopMemorySlices::find_memory_slices() { + Compile* C = _vloop.phase()->C; + // We iterate over the body, which is topologically sorted. Hence, if there is a phi + // in a slice, we will find it first, and the loads and stores afterwards. + for (int i = 0; i < _body.body().length(); i++) { + Node* n = _body.body().at(i); + if (n->is_memory_phi()) { + // Memory slice with stores (and maybe loads) + PhiNode* phi = n->as_Phi(); + int alias_idx = C->get_alias_index(phi->adr_type()); + assert(_inputs.at(alias_idx) == nullptr, "did not yet touch this slice"); + _inputs.at_put(alias_idx, phi->in(1)); + _heads.at_put(alias_idx, phi); + } else if (n->is_Load()) { + LoadNode* load = n->as_Load(); + int alias_idx = C->get_alias_index(load->adr_type()); + PhiNode* head = _heads.at(alias_idx); + if (head == nullptr) { + // We did not find a phi on this slice yet -> must be a slice with only loads. + assert(_inputs.at(alias_idx) == nullptr || _inputs.at(alias_idx) == load->in(1), + "not yet touched or the same input"); + _inputs.at_put(alias_idx, load->in(1)); + } // else: the load belongs to a slice with a phi that already set heads and inputs. +#ifdef ASSERT + } else if (n->is_Store()) { + // Found a store. Make sure it is in a slice with a Phi. + StoreNode* store = n->as_Store(); + int alias_idx = C->get_alias_index(store->adr_type()); + PhiNode* head = _heads.at(alias_idx); + assert(head != nullptr, "should have found a mem phi for this slice"); +#endif + } + } + NOT_PRODUCT( if (_vloop.is_trace_memory_slices()) { print(); } ) +} + +#ifndef PRODUCT +void VLoopMemorySlices::print() const { + tty->print_cr("\nVLoopMemorySlices::print: %s", + heads().length() > 0 ? "" : "NONE"); + for (int i = 0; i < _inputs.length(); i++) { + Node* input = _inputs.at(i); + PhiNode* head = _heads.at(i); + if (input != nullptr) { + tty->print("%3d input", i); input->dump(); + if (head == nullptr) { + tty->print_cr(" load only"); + } else { + tty->print(" head "); head->dump(); + } + } + } +} +#endif + void VLoopVPointers::compute_vpointers() { count_vpointers(); allocate_vpointers_array(); @@ -267,7 +325,6 @@ void VLoopVPointers::print() const { // the edge, i.e. spaw the order. void VLoopDependencyGraph::construct() { const GrowableArray& mem_slice_heads = _memory_slices.heads(); - const GrowableArray& mem_slice_tails = _memory_slices.tails(); ResourceMark rm; GrowableArray slice_nodes; @@ -277,7 +334,10 @@ void VLoopDependencyGraph::construct() { // For each memory slice, create the memory subgraph for (int i = 0; i < mem_slice_heads.length(); i++) { PhiNode* head = mem_slice_heads.at(i); - MemNode* tail = mem_slice_tails.at(i); + // If there is no head (memory-phi) for this slice, then we have either no memops + // in the loop, or only loads. We do not need to add any memory edges in that case. + if (head == nullptr) { continue; } + MemNode* tail = head->in(2)->as_Mem(); _memory_slices.get_slice_in_reverse_order(head, tail, slice_nodes); diff --git a/src/hotspot/share/opto/vectorization.hpp b/src/hotspot/share/opto/vectorization.hpp index b39e46cbf35..b1be52d531a 100644 --- a/src/hotspot/share/opto/vectorization.hpp +++ b/src/hotspot/share/opto/vectorization.hpp @@ -205,6 +205,10 @@ public: return _vtrace.is_trace(TraceAutoVectorizationTag::POINTERS); } + bool is_trace_optimization() const { + return _vtrace.is_trace(TraceAutoVectorizationTag::OPTIMIZATION); + } + bool is_trace_speculative_runtime_checks() const { return _vtrace.is_trace(TraceAutoVectorizationTag::SPECULATIVE_RUNTIME_CHECKS); } @@ -379,37 +383,6 @@ private: static Node* original_input(const Node* n, uint i); }; -// Submodule of VLoopAnalyzer. -// Find the memory slices in the loop. -class VLoopMemorySlices : public StackObj { -private: - const VLoop& _vloop; - - GrowableArray _heads; - GrowableArray _tails; - -public: - VLoopMemorySlices(Arena* arena, const VLoop& vloop) : - _vloop(vloop), - _heads(arena, 8, 0, nullptr), - _tails(arena, 8, 0, nullptr) {}; - NONCOPYABLE(VLoopMemorySlices); - - void find_memory_slices(); - - const GrowableArray& heads() const { return _heads; } - const GrowableArray& tails() const { return _tails; } - - // Get all memory nodes of a slice, in reverse order - void get_slice_in_reverse_order(PhiNode* head, MemNode* tail, GrowableArray& slice) const; - - bool same_memory_slice(MemNode* m1, MemNode* m2) const; - -#ifndef PRODUCT - void print() const; -#endif -}; - // Submodule of VLoopAnalyzer. // Finds all nodes in the body, and creates a mapping node->_idx to a body_idx. // This mapping is used so that subsequent datastructures sizes only grow with @@ -461,6 +434,73 @@ private: } }; +// Submodule of VLoopAnalyzer. +// Find the memory slices in the loop. There are 3 kinds of slices: +// 1. no use in loop: inputs(i) = nullptr, heads(i) = nullptr +// 2. stores in loop: inputs(i) = entry_mem, heads(i) = phi_mem +// +// = entry_mem +// | +// CountedLoop | +-----------------------+ +// | v v | +// phi_mem | +// | | +// | +// | | +// +---------------------------+ +// | +// +// +// Note: the mem uses after the loop are dependent on the last store in the loop. +// Once we vectorize, we may reorder the loads and stores, and replace +// scalar mem ops with vector mem ops. We will have to make sure that all +// uses after the loop use the new last store. +// See: VTransformApplyState::fix_memory_state_uses_after_loop +// +// 3. only loads but no stores in loop: inputs(i) = entry_mem, heads(i) = nullptr +// +// = entry_mem +// | | +// | CountedLoop | +// | | | +// | +// | +// +// +// Note: the mem uses after the loop are NOT dependent any mem ops in the loop, +// since there are no stores. +// +class VLoopMemorySlices : public StackObj { +private: + const VLoop& _vloop; + const VLoopBody& _body; + + GrowableArray _inputs; + GrowableArray _heads; + +public: + VLoopMemorySlices(Arena* arena, const VLoop& vloop, const VLoopBody& body) : + _vloop(vloop), + _body(body), + _inputs(arena, num_slices(), num_slices(), nullptr), + _heads(arena, num_slices(), num_slices(), nullptr) {}; + NONCOPYABLE(VLoopMemorySlices); + + const GrowableArray& inputs() const { return _inputs; } + const GrowableArray& heads() const { return _heads; } + + void find_memory_slices(); + void get_slice_in_reverse_order(PhiNode* head, MemNode* tail, GrowableArray& slice) const; + bool same_memory_slice(MemNode* m1, MemNode* m2) const; + +private: +#ifndef PRODUCT + void print() const; +#endif + + int num_slices() const { return _vloop.phase()->C->num_alias_types(); } +}; + // Submodule of VLoopAnalyzer. // Compute the vector element type for every node in the loop body. // We need to do this to be able to vectorize the narrower integer @@ -737,8 +777,8 @@ private: // Submodules VLoopReductions _reductions; - VLoopMemorySlices _memory_slices; VLoopBody _body; + VLoopMemorySlices _memory_slices; VLoopTypes _types; VLoopVPointers _vpointers; VLoopDependencyGraph _dependency_graph; @@ -749,8 +789,8 @@ public: _arena(mtCompiler, Arena::Tag::tag_superword), _success(false), _reductions (&_arena, vloop), - _memory_slices (&_arena, vloop), _body (&_arena, vloop, vshared), + _memory_slices (&_arena, vloop, _body), _types (&_arena, vloop, _body), _vpointers (&_arena, vloop, _body), _dependency_graph(&_arena, vloop, _body, _memory_slices, _vpointers) diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index d656bf3127b..6ae8bbe8aa0 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -292,121 +292,6 @@ int VectorNode::opcode(int sopc, BasicType bt) { } } -// Return the scalar opcode for the specified vector opcode -// and basic type. -int VectorNode::scalar_opcode(int sopc, BasicType bt) { - switch (sopc) { - case Op_AddReductionVI: - case Op_AddVI: - return Op_AddI; - case Op_AddReductionVL: - case Op_AddVL: - return Op_AddL; - case Op_MulReductionVI: - case Op_MulVI: - return Op_MulI; - case Op_MulReductionVL: - case Op_MulVL: - return Op_MulL; - case Op_AndReductionV: - case Op_AndV: - switch (bt) { - case T_BOOLEAN: - case T_CHAR: - case T_BYTE: - case T_SHORT: - case T_INT: - return Op_AndI; - case T_LONG: - return Op_AndL; - default: - assert(false, "basic type not handled"); - return 0; - } - case Op_OrReductionV: - case Op_OrV: - switch (bt) { - case T_BOOLEAN: - case T_CHAR: - case T_BYTE: - case T_SHORT: - case T_INT: - return Op_OrI; - case T_LONG: - return Op_OrL; - default: - assert(false, "basic type not handled"); - return 0; - } - case Op_XorReductionV: - case Op_XorV: - switch (bt) { - case T_BOOLEAN: - case T_CHAR: - case T_BYTE: - case T_SHORT: - case T_INT: - return Op_XorI; - case T_LONG: - return Op_XorL; - default: - assert(false, "basic type not handled"); - return 0; - } - case Op_MinReductionV: - case Op_MinV: - switch (bt) { - case T_BOOLEAN: - case T_CHAR: - assert(false, "boolean and char are signed, not implemented for Min"); - return 0; - case T_BYTE: - case T_SHORT: - case T_INT: - return Op_MinI; - case T_LONG: - return Op_MinL; - case T_FLOAT: - return Op_MinF; - case T_DOUBLE: - return Op_MinD; - default: - assert(false, "basic type not handled"); - return 0; - } - case Op_MaxReductionV: - case Op_MaxV: - switch (bt) { - case T_BOOLEAN: - case T_CHAR: - assert(false, "boolean and char are signed, not implemented for Max"); - return 0; - case T_BYTE: - case T_SHORT: - case T_INT: - return Op_MaxI; - case T_LONG: - return Op_MaxL; - case T_FLOAT: - return Op_MaxF; - case T_DOUBLE: - return Op_MaxD; - default: - assert(false, "basic type not handled"); - return 0; - } - case Op_MinVHF: - return Op_MinHF; - case Op_MaxVHF: - return Op_MaxHF; - default: - assert(false, - "Vector node %s is not handled in VectorNode::scalar_opcode", - NodeClassNames[sopc]); - return 0; // Unimplemented - } -} - // Limits on vector size (number of elements) for auto-vectorization. bool VectorNode::vector_size_supported_auto_vectorization(const BasicType bt, int size) { return Matcher::max_vector_size_auto_vectorization(bt) >= size && @@ -1727,6 +1612,34 @@ bool ReductionNode::implemented(int opc, uint vlen, BasicType bt) { return false; } +bool ReductionNode::auto_vectorization_requires_strict_order(int vopc) { + switch (vopc) { + case Op_AddReductionVI: + case Op_AddReductionVL: + case Op_MulReductionVI: + case Op_MulReductionVL: + case Op_MinReductionV: + case Op_MaxReductionV: + case Op_AndReductionV: + case Op_OrReductionV: + case Op_XorReductionV: + // These are cases that all have associative operations, which can + // thus be reordered, allowing non-strict order reductions. + return false; + case Op_AddReductionVF: + case Op_MulReductionVF: + case Op_AddReductionVD: + case Op_MulReductionVD: + // Floating-point addition and multiplication are non-associative, + // so AddReductionVF/D and MulReductionVF/D require strict ordering + // in auto-vectorization. + return true; + default: + assert(false, "not handled: %s", NodeClassNames[vopc]); + return true; + } +} + MacroLogicVNode* MacroLogicVNode::make(PhaseGVN& gvn, Node* in1, Node* in2, Node* in3, Node* mask, uint truth_table, const TypeVect* vt) { assert(truth_table <= 0xFF, "invalid"); diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp index 53778b61d0e..427aeff53fc 100644 --- a/src/hotspot/share/opto/vectornode.hpp +++ b/src/hotspot/share/opto/vectornode.hpp @@ -95,7 +95,6 @@ class VectorNode : public TypeNode { static bool is_rotate_opcode(int opc); static int opcode(int sopc, BasicType bt); // scalar_opc -> vector_opc - static int scalar_opcode(int vopc, BasicType bt); // vector_opc -> scalar_opc static int shift_count_opcode(int opc); @@ -283,6 +282,8 @@ class ReductionNode : public Node { return false; } + static bool auto_vectorization_requires_strict_order(int vopc); + #ifndef PRODUCT void dump_spec(outputStream* st) const { if (requires_strict_order()) { diff --git a/src/hotspot/share/opto/vtransform.cpp b/src/hotspot/share/opto/vtransform.cpp index 8c1210a5a09..46e8f43cb65 100644 --- a/src/hotspot/share/opto/vtransform.cpp +++ b/src/hotspot/share/opto/vtransform.cpp @@ -23,6 +23,7 @@ #include "opto/castnode.hpp" #include "opto/convertnode.hpp" +#include "opto/rootnode.hpp" #include "opto/vectorization.hpp" #include "opto/vectornode.hpp" #include "opto/vtransform.hpp" @@ -32,6 +33,45 @@ void VTransformGraph::add_vtnode(VTransformNode* vtnode) { _vtnodes.push(vtnode); } +#define TRACE_OPTIMIZE(code) \ + NOT_PRODUCT( \ + if (vtransform.vloop().is_trace_optimization()) { \ + code \ + } \ + ) + +// This is similar to IGVN optimization. But we are a bit lazy, and don't care about +// notification / worklist, since the list of nodes is rather small, and we don't +// expect optimizations that trickle over the whole graph. +void VTransformGraph::optimize(VTransform& vtransform) { + TRACE_OPTIMIZE( tty->print_cr("\nVTransformGraph::optimize"); ) + + bool progress = true; + DEBUG_ONLY(int pass_count = 0;) + while (progress) { + progress = false; + assert(++pass_count < 10, "ensure we do not have endless loops"); + for (int i = 0; i < _vtnodes.length(); i++) { + VTransformNode* vtn = _vtnodes.at(i); + if (!vtn->is_alive()) { continue; } + progress |= vtn->optimize(_vloop_analyzer, vtransform); + + // Nodes that have no use any more are dead. + if (vtn->out_strong_edges() == 0 && + // There are some exceptions: + // 1. Memory phi uses are not modeled, so they appear to have no use here, but must be kept alive. + // 2. Similarly, some stores may not have their memory uses modeled, but need to be kept alive. + // 3. Outer node with strong inputs: is a use after the loop that we must keep alive. + !(vtn->isa_LoopPhi() != nullptr || + vtn->is_load_or_store_in_loop() || + (vtn->isa_Outer() != nullptr && vtn->has_strong_in_edge()))) { + vtn->mark_dead(); + progress = true; + } + } + } +} + // Compute a linearization of the graph. We do this with a reverse-post-order of a DFS. // This only works if the graph is a directed acyclic graph (DAG). The C2 graph, and // the VLoopDependencyGraph are both DAGs, but after introduction of vectors/packs, the @@ -59,10 +99,11 @@ bool VTransformGraph::schedule() { VectorSet post_visited; collect_nodes_without_strong_in_edges(stack); + const int num_alive_nodes = count_alive_vtnodes(); // We create a reverse-post-visit order. This gives us a linearization, if there are // no cycles. Then, we simply reverse the order, and we have a schedule. - int rpo_idx = _vtnodes.length() - 1; + int rpo_idx = num_alive_nodes - 1; while (!stack.is_empty()) { VTransformNode* vtn = stack.top(); if (!pre_visited.test_set(vtn->_idx)) { @@ -78,6 +119,13 @@ bool VTransformGraph::schedule() { // runtime check, see VTransform::apply_speculative_aliasing_runtime_checks. for (uint i = 0; i < vtn->out_strong_edges(); i++) { VTransformNode* use = vtn->out_strong_edge(i); + + // Skip dead nodes + if (!use->is_alive()) { continue; } + + // Skip LoopPhi backedge. + if ((use->isa_LoopPhi() != nullptr || use->isa_CountedLoop() != nullptr) && use->in_req(2) == vtn) { continue; } + if (post_visited.test(use->_idx)) { continue; } if (pre_visited.test(use->_idx)) { // Cycle detected! @@ -117,12 +165,27 @@ bool VTransformGraph::schedule() { void VTransformGraph::collect_nodes_without_strong_in_edges(GrowableArray& stack) const { for (int i = 0; i < _vtnodes.length(); i++) { VTransformNode* vtn = _vtnodes.at(i); + if (!vtn->is_alive()) { continue; } if (!vtn->has_strong_in_edge()) { stack.push(vtn); } + // If an Outer node has both inputs and outputs, we will most likely have cycles in the final graph. + // This is not a correctness problem, but it just will prevent vectorization. If this ever happens + // try to find a way to avoid the cycle somehow. + assert(vtn->isa_Outer() == nullptr || (vtn->has_strong_in_edge() != (vtn->out_strong_edges() > 0)), + "Outer nodes should either be inputs or outputs, but not both, otherwise we may get cycles"); } } +int VTransformGraph::count_alive_vtnodes() const { + int count = 0; + for (int i = 0; i < _vtnodes.length(); i++) { + VTransformNode* vtn = _vtnodes.at(i); + if (vtn->is_alive()) { count++; } + } + return count; +} + #ifndef PRODUCT void VTransformGraph::trace_schedule_cycle(const GrowableArray& stack, const VectorSet& pre_visited, @@ -717,28 +780,118 @@ Node* VTransformApplyState::transformed_node(const VTransformNode* vtn) const { return n; } +void VTransformApplyState::init_memory_states_and_uses_after_loop() { + const GrowableArray& inputs = _vloop_analyzer.memory_slices().inputs(); + const GrowableArray& heads = _vloop_analyzer.memory_slices().heads(); + for (int i = 0; i < inputs.length(); i++) { + PhiNode* head = heads.at(i); + if (head != nullptr) { + // Slice with Phi (i.e. with stores) -> start with the phi (phi_mem) + _memory_states.at_put(i, head); + + // Remember uses outside the loop of the last memory state (store). + StoreNode* last_store = head->in(2)->as_Store(); + assert(vloop().in_bb(last_store), "backedge store should be in the loop"); + for (DUIterator_Fast jmax, j = last_store->fast_outs(jmax); j < jmax; j++) { + Node* use = last_store->fast_out(j); + if (!vloop().in_bb(use)) { + for (uint k = 0; k < use->req(); k++) { + if (use->in(k) == last_store) { + _memory_state_uses_after_loop.push(MemoryStateUseAfterLoop(use, k, i)); + } + } + } + } + } else { + // Slice without Phi (i.e. only loads) -> use the input state (entry_mem) + _memory_states.at_put(i, inputs.at(i)); + } + } +} + +// We may have reordered the scalar stores, or replaced them with vectors. Now +// the last memory state in the loop may have changed. Thus, we need to change +// the uses of the old last memory state the new last memory state. +void VTransformApplyState::fix_memory_state_uses_after_loop() { + for (int i = 0; i < _memory_state_uses_after_loop.length(); i++) { + MemoryStateUseAfterLoop& use = _memory_state_uses_after_loop.at(i); + Node* last_state = memory_state(use._alias_idx); + phase()->igvn().replace_input_of(use._use, use._in_idx, last_state); + } +} + +void VTransformNode::apply_vtn_inputs_to_node(Node* n, VTransformApplyState& apply_state) const { + PhaseIdealLoop* phase = apply_state.phase(); + for (uint i = 0; i < req(); i++) { + VTransformNode* vtn_def = in_req(i); + if (vtn_def != nullptr) { + Node* def = apply_state.transformed_node(vtn_def); + phase->igvn().replace_input_of(n, i, def); + } + } +} + VTransformApplyResult VTransformMemopScalarNode::apply(VTransformApplyState& apply_state) const { - // This was just wrapped. Now we simply unwrap without touching the inputs. + apply_vtn_inputs_to_node(_node, apply_state); + // The memory state has to be applied separately: the vtn does not hold it. This allows reordering. + Node* mem = apply_state.memory_state(_node->adr_type()); + apply_state.phase()->igvn().replace_input_of(_node, 1, mem); + if (_node->is_Store()) { + apply_state.set_memory_state(_node->adr_type(), _node); + } + return VTransformApplyResult::make_scalar(_node); } VTransformApplyResult VTransformDataScalarNode::apply(VTransformApplyState& apply_state) const { - // This was just wrapped. Now we simply unwrap without touching the inputs. + apply_vtn_inputs_to_node(_node, apply_state); return VTransformApplyResult::make_scalar(_node); } VTransformApplyResult VTransformLoopPhiNode::apply(VTransformApplyState& apply_state) const { - // This was just wrapped. Now we simply unwrap without touching the inputs. + PhaseIdealLoop* phase = apply_state.phase(); + Node* in0 = apply_state.transformed_node(in_req(0)); + Node* in1 = apply_state.transformed_node(in_req(1)); + phase->igvn().replace_input_of(_node, 0, in0); + phase->igvn().replace_input_of(_node, 1, in1); + // Note: the backedge is hooked up later. + + // The Phi's inputs may have been modified, and the types changes, + // e.g. from scalar to vector. + const Type* t = in1->bottom_type(); + _node->as_Type()->set_type(t); + phase->igvn().set_type(_node, t); + return VTransformApplyResult::make_scalar(_node); } +// Cleanup backedges. In the schedule, the backedges come after their phis. Hence, +// we only have the transformed backedges after the phis are already transformed. +// We hook the backedges into the phis now, during cleanup. +void VTransformLoopPhiNode::apply_backedge(VTransformApplyState& apply_state) const { + PhaseIdealLoop* phase = apply_state.phase(); + if (_node->is_memory_phi()) { + // Memory phi/backedge + // The last memory state of that slice is the backedge. + Node* last_state = apply_state.memory_state(_node->adr_type()); + phase->igvn().replace_input_of(_node, 2, last_state); + } else { + // Data phi/backedge + Node* in2 = apply_state.transformed_node(in_req(2)); + phase->igvn().replace_input_of(_node, 2, in2); + } +} + VTransformApplyResult VTransformCFGNode::apply(VTransformApplyState& apply_state) const { - // This was just wrapped. Now we simply unwrap without touching the inputs. + // We do not modify the inputs of the CountedLoop (and certainly not its backedge) + if (!_node->is_CountedLoop()) { + apply_vtn_inputs_to_node(_node, apply_state); + } return VTransformApplyResult::make_scalar(_node); } VTransformApplyResult VTransformOuterNode::apply(VTransformApplyState& apply_state) const { - // This was just wrapped. Now we simply unwrap without touching the inputs. + apply_vtn_inputs_to_node(_node, apply_state); return VTransformApplyResult::make_scalar(_node); } @@ -797,7 +950,7 @@ VTransformApplyResult VTransformElementWiseVectorNode::apply(VTransformApplyStat vn = VectorNode::make(_vector_opcode, in1, in2, in3, vt); // ternary } - register_new_node_from_vectorization_and_replace_scalar_nodes(apply_state, vn); + register_new_node_from_vectorization(apply_state, vn); return VTransformApplyResult::make_vector(vn); } @@ -812,7 +965,7 @@ VTransformApplyResult VTransformElementWiseLongOpWithCastToIntVectorNode::apply( register_new_node_from_vectorization(apply_state, long_vn); // Cast long -> int, to mimic the scalar long -> int operation. VectorNode* vn = VectorCastNode::make(Op_VectorCastL2X, long_vn, T_INT, vlen); - register_new_node_from_vectorization_and_replace_scalar_nodes(apply_state, vn); + register_new_node_from_vectorization(apply_state, vn); return VTransformApplyResult::make_vector(vn); } @@ -824,7 +977,7 @@ VTransformApplyResult VTransformReinterpretVectorNode::apply(VTransformApplyStat Node* in1 = apply_state.transformed_node(in_req(1)); VectorNode* vn = new VectorReinterpretNode(in1, src_vt, dst_vt); - register_new_node_from_vectorization_and_replace_scalar_nodes(apply_state, vn); + register_new_node_from_vectorization(apply_state, vn); return VTransformApplyResult::make_vector(vn); } @@ -843,16 +996,252 @@ VTransformApplyResult VTransformBoolVectorNode::apply(VTransformApplyState& appl PhaseIdealLoop* phase = apply_state.phase(); ConINode* mask_node = phase->intcon((int)mask); VectorNode* vn = new VectorMaskCmpNode(mask, cmp_in1, cmp_in2, mask_node, vt); - register_new_node_from_vectorization_and_replace_scalar_nodes(apply_state, vn); + register_new_node_from_vectorization(apply_state, vn); return VTransformApplyResult::make_vector(vn); } +bool VTransformReductionVectorNode::optimize(const VLoopAnalyzer& vloop_analyzer, VTransform& vtransform) { + return optimize_move_non_strict_order_reductions_out_of_loop(vloop_analyzer, vtransform); +} + +int VTransformReductionVectorNode::vector_reduction_opcode() const { + return ReductionNode::opcode(scalar_opcode(), element_basic_type()); +} + +bool VTransformReductionVectorNode::requires_strict_order() const { + int vopc = vector_reduction_opcode(); + return ReductionNode::auto_vectorization_requires_strict_order(vopc); +} + +// Having ReductionNodes in the loop is expensive. They need to recursively +// fold together the vector values, for every vectorized loop iteration. If +// we encounter the following pattern, we can vector accumulate the values +// inside the loop, and only have a single UnorderedReduction after the loop. +// +// Note: UnorderedReduction represents a ReductionNode which does not require +// calculating in strict order. +// +// CountedLoop init +// | | +// +------+ | +------------------------+ +// | | | | +// PhiNode (s) | +// | | +// | Vector | +// | | | +// UnorderedReduction (first_red) | +// | | +// ... Vector | +// | | | +// UnorderedReduction (last_red) | +// | | +// +----------------------+ +// +// We patch the graph to look like this: +// +// CountedLoop identity_vector +// | | +// +-------+ | +---------------+ +// | | | | +// PhiNode (v) | +// | | +// | Vector | +// | | | +// VectorAccumulator | +// | | +// ... Vector | +// | | | +// init VectorAccumulator | +// | | | | +// UnorderedReduction +-----------+ +// +// We turned the scalar (s) Phi into a vectorized one (v). In the loop, we +// use vector_accumulators, which do the same reductions, but only element +// wise. This is a single operation per vector_accumulator, rather than many +// for a UnorderedReduction. We can then reduce the last vector_accumulator +// after the loop, and also reduce the init value into it. +// +// We can not do this with all reductions. Some reductions do not allow the +// reordering of operations (for example float addition/multiplication require +// strict order). +// +// Note: we must perform this optimization already during auto vectorization, +// before we evaluate the cost-model. Without this optimization, we may +// still have expensive reduction nodes in the loop which can make +// vectorization unprofitable. Only with the optimization does vectorization +// become profitable, since the expensive reduction node is moved +// outside the loop, and instead cheaper element-wise vector accumulations +// are performed inside the loop. +bool VTransformReductionVectorNode::optimize_move_non_strict_order_reductions_out_of_loop_preconditions(VTransform& vtransform) { + // We have a phi with a single use. + VTransformLoopPhiNode* phi = in_req(1)->isa_LoopPhi(); + if (phi == nullptr) { + return false; + } + if (phi->out_strong_edges() != 1) { + TRACE_OPTIMIZE( + tty->print(" Cannot move out of loop, phi has multiple uses:"); + print(); + tty->print(" phi: "); + phi->print(); + ) + return false; + } + + if (requires_strict_order()) { + TRACE_OPTIMIZE( + tty->print(" Cannot move out of loop, strict order required: "); + print(); + ) + return false; + } + + const int sopc = scalar_opcode(); + const uint vlen = vector_length(); + const BasicType bt = element_basic_type(); + const int ropc = vector_reduction_opcode(); + const int vopc = VectorNode::opcode(sopc, bt); + if (!Matcher::match_rule_supported_vector(vopc, vlen, bt)) { + DEBUG_ONLY( this->print(); ) + assert(false, "do not have normal vector op for this reduction"); + return false; // not implemented + } + + // Traverse up the chain of non strict order reductions, checking that it loops + // back to the phi. Check that all non strict order reductions only have a single + // use, except for the last (last_red), which only has phi as a use in the loop, + // and all other uses are outside the loop. + VTransformReductionVectorNode* first_red = this; + VTransformReductionVectorNode* last_red = phi->in_req(2)->isa_ReductionVector(); + VTransformReductionVectorNode* current_red = last_red; + while (true) { + if (current_red == nullptr || + current_red->vector_reduction_opcode() != ropc || + current_red->element_basic_type() != bt || + current_red->vector_length() != vlen) { + TRACE_OPTIMIZE( + tty->print(" Cannot move out of loop, other reduction node does not match:"); + print(); + tty->print(" other: "); + current_red->print(); + ) + return false; // not compatible + } + + VTransformVectorNode* vector_input = current_red->in_req(2)->isa_Vector(); + if (vector_input == nullptr) { + assert(false, "reduction has a bad vector input"); + return false; + } + + // Expect single use of the non strict order reduction. Except for the last_red. + if (current_red == last_red) { + // All uses must be outside loop body, except for the phi. + for (uint i = 0; i < current_red->out_strong_edges(); i++) { + VTransformNode* use = current_red->out_strong_edge(i); + if (use->isa_LoopPhi() == nullptr && + use->isa_Outer() == nullptr) { + // Should not be allowed by SuperWord::mark_reductions + assert(false, "reduction has use inside loop"); + return false; + } + } + } else { + if (current_red->out_strong_edges() != 1) { + TRACE_OPTIMIZE( + tty->print(" Cannot move out of loop, other reduction node has use outside loop:"); + print(); + tty->print(" other: "); + current_red->print(); + ) + return false; // Only single use allowed + } + } + + // If the scalar input is a phi, we passed all checks. + VTransformNode* scalar_input = current_red->in_req(1); + if (scalar_input == phi) { + break; + } + + // We expect another non strict reduction, verify it in the next iteration. + current_red = scalar_input->isa_ReductionVector(); + } + return true; // success +} + +bool VTransformReductionVectorNode::optimize_move_non_strict_order_reductions_out_of_loop(const VLoopAnalyzer& vloop_analyzer, VTransform& vtransform) { + if (!optimize_move_non_strict_order_reductions_out_of_loop_preconditions(vtransform)) { + return false; + } + + // All checks were successful. Edit the vtransform graph now. + TRACE_OPTIMIZE( + tty->print_cr("VTransformReductionVectorNode::optimize_move_non_strict_order_reductions_out_of_loop"); + ) + + const int sopc = scalar_opcode(); + const uint vlen = vector_length(); + const BasicType bt = element_basic_type(); + const int vopc = VectorNode::opcode(sopc, bt); + PhaseIdealLoop* phase = vloop_analyzer.vloop().phase(); + + // Create a vector of identity values. + Node* identity = ReductionNode::make_identity_con_scalar(phase->igvn(), sopc, bt); + phase->set_root_as_ctrl(identity); + VTransformNode* vtn_identity = new (vtransform.arena()) VTransformOuterNode(vtransform, identity); + + VTransformNode* vtn_identity_vector = new (vtransform.arena()) VTransformReplicateNode(vtransform, vlen, bt); + vtn_identity_vector->init_req(1, vtn_identity); + + // Turn the scalar phi into a vector phi. + VTransformLoopPhiNode* phi = in_req(1)->isa_LoopPhi(); + VTransformNode* init = phi->in_req(1); + phi->set_req(1, vtn_identity_vector); + + // Traverse down the chain of reductions, and replace them with vector_accumulators. + VTransformReductionVectorNode* first_red = this; + VTransformReductionVectorNode* last_red = phi->in_req(2)->isa_ReductionVector(); + VTransformReductionVectorNode* current_red = first_red; + VTransformNode* current_vector_accumulator = phi; + while (true) { + VTransformNode* vector_input = current_red->in_req(2); + VTransformVectorNode* vector_accumulator = new (vtransform.arena()) VTransformElementWiseVectorNode(vtransform, 3, current_red->properties(), vopc); + vector_accumulator->init_req(1, current_vector_accumulator); + vector_accumulator->init_req(2, vector_input); + TRACE_OPTIMIZE( + tty->print(" replace "); + current_red->print(); + tty->print(" with "); + vector_accumulator->print(); + ) + current_vector_accumulator = vector_accumulator; + if (current_red == last_red) { break; } + current_red = current_red->unique_out_strong_edge()->isa_ReductionVector(); + } + + // Feed vector accumulator into the backedge. + phi->set_req(2, current_vector_accumulator); + + // Create post-loop reduction. last_red keeps all uses outside the loop. + last_red->set_req(1, init); + last_red->set_req(2, current_vector_accumulator); + + TRACE_OPTIMIZE( + tty->print(" phi "); + phi->print(); + tty->print(" after loop "); + last_red->print(); + ) + return true; // success +} + VTransformApplyResult VTransformReductionVectorNode::apply(VTransformApplyState& apply_state) const { Node* init = apply_state.transformed_node(in_req(1)); Node* vec = apply_state.transformed_node(in_req(2)); ReductionNode* vn = ReductionNode::make(scalar_opcode(), nullptr, init, vec, element_basic_type()); - register_new_node_from_vectorization_and_replace_scalar_nodes(apply_state, vn); + register_new_node_from_vectorization(apply_state, vn); return VTransformApplyResult::make_vector(vn, vn->vect_type()); } @@ -861,10 +1250,9 @@ VTransformApplyResult VTransformLoadVectorNode::apply(VTransformApplyState& appl uint vlen = vector_length(); BasicType bt = element_basic_type(); - LoadNode* first = nodes().at(0)->as_Load(); + // The memory state has to be applied separately: the vtn does not hold it. This allows reordering. Node* ctrl = apply_state.transformed_node(in_req(MemNode::Control)); - // first has the correct memory state, determined by VTransformGraph::apply_memops_reordering_with_schedule - Node* mem = first->in(MemNode::Memory); + Node* mem = apply_state.memory_state(_adr_type); Node* adr = apply_state.transformed_node(in_req(MemNode::Address)); // Set the memory dependency of the LoadVector as early as possible. @@ -880,10 +1268,9 @@ VTransformApplyResult VTransformLoadVectorNode::apply(VTransformApplyState& appl } } - LoadVectorNode* vn = LoadVectorNode::make(sopc, ctrl, mem, adr, _adr_type, vlen, bt, - control_dependency()); + LoadVectorNode* vn = LoadVectorNode::make(sopc, ctrl, mem, adr, _adr_type, vlen, bt, _control_dependency); DEBUG_ONLY( if (VerifyAlignVector) { vn->set_must_verify_alignment(); } ) - register_new_node_from_vectorization_and_replace_scalar_nodes(apply_state, vn); + register_new_node_from_vectorization(apply_state, vn); return VTransformApplyResult::make_vector(vn, vn->vect_type()); } @@ -891,27 +1278,17 @@ VTransformApplyResult VTransformStoreVectorNode::apply(VTransformApplyState& app int sopc = scalar_opcode(); uint vlen = vector_length(); - StoreNode* first = nodes().at(0)->as_Store(); + // The memory state has to be applied separately: the vtn does not hold it. This allows reordering. Node* ctrl = apply_state.transformed_node(in_req(MemNode::Control)); - // first has the correct memory state, determined by VTransformGraph::apply_memops_reordering_with_schedule - Node* mem = first->in(MemNode::Memory); + Node* mem = apply_state.memory_state(_adr_type); Node* adr = apply_state.transformed_node(in_req(MemNode::Address)); Node* value = apply_state.transformed_node(in_req(MemNode::ValueIn)); StoreVectorNode* vn = StoreVectorNode::make(sopc, ctrl, mem, adr, _adr_type, value, vlen); DEBUG_ONLY( if (VerifyAlignVector) { vn->set_must_verify_alignment(); } ) - register_new_node_from_vectorization_and_replace_scalar_nodes(apply_state, vn); - return VTransformApplyResult::make_vector(vn, vn->vect_type()); -} - -void VTransformVectorNode::register_new_node_from_vectorization_and_replace_scalar_nodes(VTransformApplyState& apply_state, Node* vn) const { - PhaseIdealLoop* phase = apply_state.phase(); register_new_node_from_vectorization(apply_state, vn); - - for (int i = 0; i < _nodes.length(); i++) { - Node* n = _nodes.at(i); - phase->igvn().replace_node(n, vn); - } + apply_state.set_memory_state(_adr_type, vn); + return VTransformApplyResult::make_vector(vn, vn->vect_type()); } void VTransformNode::register_new_node_from_vectorization(VTransformApplyState& apply_state, Node* vn) const { @@ -944,15 +1321,6 @@ void VTransformGraph::print_schedule() const { } } -void VTransformGraph::print_memops_schedule() const { - tty->print_cr("\nVTransformGraph::print_memops_schedule:"); - int i = 0; - for_each_memop_in_schedule([&] (MemNode* mem) { - tty->print(" %3d: ", i++); - mem->dump(); - }); -} - void VTransformNode::print() const { tty->print("%3d %s (", _idx, name()); for (uint i = 0; i < _req; i++) { @@ -970,7 +1338,7 @@ void VTransformNode::print() const { print_node_idx(_in.at(i)); } } - tty->print(") ["); + tty->print(") %s[", _is_alive ? "" : "dead "); for (uint i = 0; i < _out_end_strong_edges; i++) { print_node_idx(_out.at(i)); } diff --git a/src/hotspot/share/opto/vtransform.hpp b/src/hotspot/share/opto/vtransform.hpp index 9a4e4de01a2..7ad7b432e9b 100644 --- a/src/hotspot/share/opto/vtransform.hpp +++ b/src/hotspot/share/opto/vtransform.hpp @@ -39,9 +39,13 @@ // // This is the life-cycle of a VTransform: // - Construction: -// - From SuperWord, with the SuperWordVTransformBuilder. +// - From SuperWord PackSet, with the SuperWordVTransformBuilder. // -// - Future Plans: optimize, if-conversion, etc. +// - Optimize: +// - Move non-strict order reductions out of the loop. This means we have +// only element-wise operations inside the loop, rather than the much +// more expensive lane-crossing reductions. We need to do this before +// assessing profitability with the cost-model. // // - Schedule: // - Compute linearization of the VTransformGraph, into an order that respects @@ -49,22 +53,31 @@ // // - Apply: // - Changes to the C2 IR are only made once the "apply" method is called. +// - Align the main loop, by adjusting pre loop limit. +// - Add speculative runtime checks (alignment and aliasing). // - Each vtnode generates its corresponding scalar and vector C2 nodes, -// possibly replacing old scalar C2 nodes. +// possibly replacing old scalar C2 nodes. We apply each vtnode in order +// of the schedule, so that all input vtnodes are already applied, i.e. +// all input vtnodes have already generated the transformed C2 nodes. +// - We also build the new memory graph on the fly. The schedule may have +// reordered the memory operations, and so we cannot use the old memory +// graph, but must build it from the scheduled order. We keep track of +// the current memory state in VTransformApplyState. // // Future Plans with VTransform: // - Cost model: estimate if vectorization is profitable. -// - Optimizations: moving unordered reductions out of the loop, whih decreases cost. // - Pack/Unpack/Shuffle: introduce additional nodes not present in the scalar loop. // This is difficult to do with the SuperWord packset approach. // - If-conversion: convert predicated nodes into CFG. typedef int VTransformNodeIDX; +class VTransform; class VTransformNode; class VTransformMemopScalarNode; class VTransformDataScalarNode; class VTransformLoopPhiNode; class VTransformCFGNode; +class VTransformCountedLoopNode; class VTransformOuterNode; class VTransformVectorNode; class VTransformElementWiseVectorNode; @@ -174,9 +187,9 @@ public: const GrowableArray& vtnodes() const { return _vtnodes; } const GrowableArray& get_schedule() const { return _schedule; } + void optimize(VTransform& vtransform); bool schedule(); bool has_store_to_load_forwarding_failure(const VLoopAnalyzer& vloop_analyzer) const; - void apply_memops_reordering_with_schedule() const; void apply_vectorization_for_each_vtnode(uint& max_vector_length, uint& max_vector_width) const; private: @@ -186,14 +199,11 @@ private: bool in_bb(const Node* n) const { return _vloop.in_bb(n); } void collect_nodes_without_strong_in_edges(GrowableArray& stack) const; - - template - void for_each_memop_in_schedule(Callback callback) const; + int count_alive_vtnodes() const; #ifndef PRODUCT void print_vtnodes() const; void print_schedule() const; - void print_memops_schedule() const; void trace_schedule_cycle(const GrowableArray& stack, const VectorSet& pre_visited, const VectorSet& post_visited) const; @@ -215,14 +225,14 @@ private: VTransformGraph _graph; - // Memory reference, and the alignment width (aw) for which we align the main-loop, + // VPointer, and the alignment width (aw) for which we align the main-loop, // by adjusting the pre-loop limit. - MemNode const* _mem_ref_for_main_loop_alignment; + VPointer const* _vpointer_for_main_loop_alignment; int _aw_for_main_loop_alignment; public: VTransform(const VLoopAnalyzer& vloop_analyzer, - MemNode const* mem_ref_for_main_loop_alignment, + VPointer const* vpointer_for_main_loop_alignment, int aw_for_main_loop_alignment NOT_PRODUCT( COMMA const VTransformTrace trace) ) : @@ -231,14 +241,16 @@ public: NOT_PRODUCT(_trace(trace) COMMA) _arena(mtCompiler, Arena::Tag::tag_superword), _graph(_vloop_analyzer, _arena NOT_PRODUCT(COMMA _trace)), - _mem_ref_for_main_loop_alignment(mem_ref_for_main_loop_alignment), + _vpointer_for_main_loop_alignment(vpointer_for_main_loop_alignment), _aw_for_main_loop_alignment(aw_for_main_loop_alignment) {} const VLoopAnalyzer& vloop_analyzer() const { return _vloop_analyzer; } + const VLoop& vloop() const { return _vloop; } Arena* arena() { return &_arena; } DEBUG_ONLY( bool has_graph() const { return !_graph.is_empty(); } ) VTransformGraph& graph() { return _graph; } + void optimize() { return _graph.optimize(*this); } bool schedule() { return _graph.schedule(); } bool has_store_to_load_forwarding_failure() const { return _graph.has_store_to_load_forwarding_failure(_vloop_analyzer); } void apply(); @@ -257,7 +269,7 @@ private: } // Ensure that the main loop vectors are aligned by adjusting the pre loop limit. - void determine_mem_ref_and_aw_for_main_loop_alignment(); + void determine_vpointer_and_aw_for_main_loop_alignment(); void adjust_pre_loop_limit_to_align_main_loop_vectors(); void apply_speculative_alignment_runtime_checks(); @@ -271,7 +283,7 @@ private: }; // Keeps track of the state during "VTransform::apply" -// -> keep track of the already transformed nodes +// -> keep track of the already transformed nodes and the memory state. class VTransformApplyState : public StackObj { private: const VLoopAnalyzer& _vloop_analyzer; @@ -281,11 +293,35 @@ private: // generated def (input) nodes when we are generating the use nodes in "apply". GrowableArray _vtnode_idx_to_transformed_node; + // We keep track of the current memory state in each slice. If the slice has only + // loads (and no phi), then this is always the input memory state from before the + // loop. If there is a memory phi, this is initially the memory phi, and each time + // a store is processed, it is updated to that store. + GrowableArray _memory_states; + + // We need to keep track of the memory uses after the loop, for the slices that + // have a memory phi. + // use->in(in_idx) = + class MemoryStateUseAfterLoop : public StackObj { + public: + Node* _use; + int _in_idx; + int _alias_idx; + + MemoryStateUseAfterLoop(Node* use, int in_idx, int alias_idx) : + _use(use), _in_idx(in_idx), _alias_idx(alias_idx) {} + MemoryStateUseAfterLoop() : MemoryStateUseAfterLoop(nullptr, 0, 0) {} + }; + + GrowableArray _memory_state_uses_after_loop; + public: VTransformApplyState(const VLoopAnalyzer& vloop_analyzer, int num_vtnodes) : _vloop_analyzer(vloop_analyzer), - _vtnode_idx_to_transformed_node(num_vtnodes, num_vtnodes, nullptr) + _vtnode_idx_to_transformed_node(num_vtnodes, num_vtnodes, nullptr), + _memory_states(num_slices(), num_slices(), nullptr) { + init_memory_states_and_uses_after_loop(); } const VLoop& vloop() const { return _vloop_analyzer.vloop(); } @@ -294,6 +330,25 @@ public: void set_transformed_node(VTransformNode* vtn, Node* n); Node* transformed_node(const VTransformNode* vtn) const; + + Node* memory_state(int alias_idx) const { return _memory_states.at(alias_idx); } + void set_memory_state(int alias_idx, Node* n) { _memory_states.at_put(alias_idx, n); } + + Node* memory_state(const TypePtr* adr_type) const { + int alias_idx = phase()->C->get_alias_index(adr_type); + return memory_state(alias_idx); + } + + void set_memory_state(const TypePtr* adr_type, Node* n) { + int alias_idx = phase()->C->get_alias_index(adr_type); + return set_memory_state(alias_idx, n); + } + + void fix_memory_state_uses_after_loop(); + +private: + int num_slices() const { return _vloop_analyzer.memory_slices().heads().length(); } + void init_memory_states_and_uses_after_loop(); }; // The vtnodes (VTransformNode) resemble the C2 IR Nodes, and model a part of the @@ -325,6 +380,8 @@ public: const VTransformNodeIDX _idx; private: + bool _is_alive; + // We split _in into 3 sections: // - data edges (req): _in[0 .. _req-1] // - strong memory edges: _in[_req .. _in_end_strong_memory_edges-1] @@ -342,6 +399,7 @@ private: public: VTransformNode(VTransform& vtransform, const uint req) : _idx(vtransform.graph().new_idx()), + _is_alive(true), _req(req), _in_end_strong_memory_edges(req), _in(vtransform.arena(), req, req, nullptr), @@ -358,6 +416,14 @@ public: n->add_out_strong_edge(this); } + void set_req(uint i, VTransformNode* n) { + assert(i < _req, "must be a req"); + VTransformNode* old = _in.at(i); + if (old != nullptr) { old->del_out_strong_edge(this); } + _in.at_put(i, n); + if (n != nullptr) { n->add_out_strong_edge(this); } + } + void swap_req(uint i, uint j) { assert(i < _req, "must be a req"); assert(j < _req, "must be a req"); @@ -405,6 +471,23 @@ private: _out.push(n); } + void del_out_strong_edge(VTransformNode* n) { + int i = _out.find(n); + assert(0 <= i && i < (int)_out_end_strong_edges, "must be in strong edges"); + + // Replace n with the last strong edge. + VTransformNode* last_strong = _out.at(_out_end_strong_edges - 1); + _out.at_put(i, last_strong); + + if (_out_end_strong_edges < (uint)_out.length()) { + // Now replace where last_strong was with the last weak edge. + VTransformNode* last_weak = _out.top(); + _out.at_put(_out_end_strong_edges - 1, last_weak); + } + _out.pop(); + _out_end_strong_edges--; + } + public: uint req() const { return _req; } uint out_strong_edges() const { return _out_end_strong_edges; } @@ -432,7 +515,24 @@ public: return false; } + VTransformNode* unique_out_strong_edge() const { + assert(out_strong_edges() == 1, "must be unique"); + return _out.at(0); + } + + bool is_alive() const { return _is_alive; } + + void mark_dead() { + _is_alive = false; + // Remove all inputs + for (uint i = 0; i < req(); i++) { + set_req(i, nullptr); + } + } + virtual VTransformMemopScalarNode* isa_MemopScalar() { return nullptr; } + virtual VTransformLoopPhiNode* isa_LoopPhi() { return nullptr; } + virtual VTransformCountedLoopNode* isa_CountedLoop() { return nullptr; } virtual VTransformOuterNode* isa_Outer() { return nullptr; } virtual VTransformVectorNode* isa_Vector() { return nullptr; } virtual VTransformElementWiseVectorNode* isa_ElementWiseVector() { return nullptr; } @@ -447,10 +547,11 @@ public: virtual bool is_load_or_store_in_loop() const { return false; } virtual const VPointer& vpointer() const { ShouldNotReachHere(); } + virtual bool optimize(const VLoopAnalyzer& vloop_analyzer, VTransform& vtransform) { return false; } + virtual VTransformApplyResult apply(VTransformApplyState& apply_state) const = 0; - - Node* find_transformed_input(int i, const GrowableArray& vnode_idx_to_transformed_node) const; - + virtual void apply_backedge(VTransformApplyState& apply_state) const {}; + void apply_vtn_inputs_to_node(Node* n, VTransformApplyState& apply_state) const; void register_new_node_from_vectorization(VTransformApplyState& apply_state, Node* vn) const; NOT_PRODUCT(virtual const char* name() const = 0;) @@ -510,7 +611,9 @@ public: assert(_node->in(0)->is_Loop(), "phi ctrl must be Loop: %s", _node->in(0)->Name()); } + virtual VTransformLoopPhiNode* isa_LoopPhi() override { return this; } virtual VTransformApplyResult apply(VTransformApplyState& apply_state) const override; + virtual void apply_backedge(VTransformApplyState& apply_state) const override; NOT_PRODUCT(virtual const char* name() const override { return "LoopPhi"; };) NOT_PRODUCT(virtual void print_spec() const override;) }; @@ -531,6 +634,16 @@ public: NOT_PRODUCT(virtual void print_spec() const override;) }; +// Identity transform for CountedLoop, the only CFG node with a backedge. +class VTransformCountedLoopNode : public VTransformCFGNode { +public: + VTransformCountedLoopNode(VTransform& vtransform, CountedLoopNode* n) : + VTransformCFGNode(vtransform, n) {} + + virtual VTransformCountedLoopNode* isa_CountedLoop() override { return this; } + NOT_PRODUCT(virtual const char* name() const override { return "CountedLoop"; };) +}; + // Wrapper node for nodes outside the loop that are inputs to nodes in the loop. // Since we want the loop-internal nodes to be able to reference all inputs as vtnodes, // we must wrap the inputs that are outside the loop into special vtnodes, too. @@ -632,28 +745,16 @@ public: class VTransformVectorNode : public VTransformNode { private: const VTransformVectorNodeProperties _properties; -protected: - GrowableArray _nodes; public: VTransformVectorNode(VTransform& vtransform, const uint req, const VTransformVectorNodeProperties properties) : - VTransformNode(vtransform, req), - _properties(properties), - _nodes(vtransform.arena(), - properties.vector_length(), - properties.vector_length(), - nullptr) {} - - void set_nodes(const Node_List* pack) { - for (uint k = 0; k < pack->size(); k++) { - _nodes.at_put(k, pack->at(k)); - } - } + VTransformNode(vtransform, req), _properties(properties) {} virtual VTransformVectorNode* isa_Vector() override { return this; } void register_new_node_from_vectorization_and_replace_scalar_nodes(VTransformApplyState& apply_state, Node* vn) const; NOT_PRODUCT(virtual void print_spec() const override;) protected: + const VTransformVectorNodeProperties& properties() const { return _properties; } Node* approximate_origin() const { return _properties.approximate_origin(); } int scalar_opcode() const { return _properties.scalar_opcode(); } uint vector_length() const { return _properties.vector_length(); } @@ -733,8 +834,15 @@ public: VTransformReductionVectorNode(VTransform& vtransform, const VTransformVectorNodeProperties properties) : VTransformVectorNode(vtransform, 3, properties) {} virtual VTransformReductionVectorNode* isa_ReductionVector() override { return this; } + virtual bool optimize(const VLoopAnalyzer& vloop_analyzer, VTransform& vtransform) override; virtual VTransformApplyResult apply(VTransformApplyState& apply_state) const override; NOT_PRODUCT(virtual const char* name() const override { return "ReductionVector"; };) + +private: + int vector_reduction_opcode() const; + bool requires_strict_order() const; + bool optimize_move_non_strict_order_reductions_out_of_loop_preconditions(VTransform& vtransform); + bool optimize_move_non_strict_order_reductions_out_of_loop(const VLoopAnalyzer& vloop_analyzer, VTransform& vtransform); }; class VTransformMemVectorNode : public VTransformVectorNode { @@ -749,17 +857,23 @@ public: _vpointer(vpointer), _adr_type(adr_type) {} - const GrowableArray& nodes() const { return _nodes; } virtual VTransformMemVectorNode* isa_MemVector() override { return this; } virtual bool is_load_or_store_in_loop() const override { return true; } virtual const VPointer& vpointer() const override { return _vpointer; } }; class VTransformLoadVectorNode : public VTransformMemVectorNode { +private: + const LoadNode::ControlDependency _control_dependency; + public: // req = 3 -> [ctrl, mem, adr] - VTransformLoadVectorNode(VTransform& vtransform, const VTransformVectorNodeProperties properties, const VPointer& vpointer, const TypePtr* adr_type) : - VTransformMemVectorNode(vtransform, 3, properties, vpointer, adr_type) {} + VTransformLoadVectorNode(VTransform& vtransform, + const VTransformVectorNodeProperties properties, + const VPointer& vpointer, + const TypePtr* adr_type, + const LoadNode::ControlDependency control_dependency) : + VTransformMemVectorNode(vtransform, 3, properties, vpointer, adr_type), _control_dependency(control_dependency) {} LoadNode::ControlDependency control_dependency() const; virtual VTransformLoadVectorNode* isa_LoadVector() override { return this; } virtual bool is_load_in_loop() const override { return true; } @@ -777,30 +891,4 @@ public: virtual VTransformApplyResult apply(VTransformApplyState& apply_state) const override; NOT_PRODUCT(virtual const char* name() const override { return "StoreVector"; };) }; - -// Invoke callback on all memops, in the order of the schedule. -template -void VTransformGraph::for_each_memop_in_schedule(Callback callback) const { - assert(_schedule.length() == _vtnodes.length(), "schedule was computed"); - - for (int i = 0; i < _schedule.length(); i++) { - VTransformNode* vtn = _schedule.at(i); - - // We must ignore nodes outside the loop. - if (vtn->isa_Outer() != nullptr) { continue; } - - VTransformMemopScalarNode* scalar = vtn->isa_MemopScalar(); - if (scalar != nullptr) { - callback(scalar->node()); - } - - VTransformMemVectorNode* vector = vtn->isa_MemVector(); - if (vector != nullptr) { - for (int j = 0; j < vector->nodes().length(); j++) { - callback(vector->nodes().at(j)->as_Mem()); - } - } - } -} - #endif // SHARE_OPTO_VTRANSFORM_HPP diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp index 077b3fec505..fa6ede86cd9 100644 --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -1694,7 +1694,7 @@ bool JvmtiExport::has_frame_pops(JavaThread* thread) { if (!can_post_frame_pop()) { return false; } - JvmtiThreadState *state = get_jvmti_thread_state(thread); + JvmtiThreadState *state = thread->jvmti_thread_state(); if (state == nullptr) { return false; } @@ -1713,7 +1713,7 @@ void JvmtiExport::continuation_yield_cleanup(JavaThread* thread, jint continuati } assert(thread == JavaThread::current(), "must be"); - JvmtiThreadState *state = get_jvmti_thread_state(thread); + JvmtiThreadState *state = thread->jvmti_thread_state(); if (state == nullptr) { return; } diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index f77b648ba95..b4d100341e0 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -39,6 +39,7 @@ #include "classfile/systemDictionary.hpp" #include "classfile/vmSymbols.hpp" #include "code/codeCache.hpp" +#include "code/compiledIC.hpp" #include "compiler/compilationPolicy.hpp" #include "compiler/compilerOracle.hpp" #include "compiler/directivesParser.hpp" @@ -1548,19 +1549,23 @@ struct CodeBlobStub { name(os::strdup(blob->name())), size(blob->size()), blob_type(static_cast(WhiteBox::get_blob_type(blob))), - address((jlong) blob) { } + address((jlong) blob), + code_begin((jlong) blob->code_begin()), + is_nmethod((jboolean) blob->is_nmethod()) { } ~CodeBlobStub() { os::free((void*) name); } const char* const name; const jint size; const jint blob_type; const jlong address; + const jlong code_begin; + const jboolean is_nmethod; }; static jobjectArray codeBlob2objectArray(JavaThread* thread, JNIEnv* env, CodeBlobStub* cb) { ResourceMark rm; jclass clazz = env->FindClass(vmSymbols::java_lang_Object()->as_C_string()); CHECK_JNI_EXCEPTION_(env, nullptr); - jobjectArray result = env->NewObjectArray(4, clazz, nullptr); + jobjectArray result = env->NewObjectArray(6, clazz, nullptr); jstring name = env->NewStringUTF(cb->name); CHECK_JNI_EXCEPTION_(env, nullptr); @@ -1578,6 +1583,14 @@ static jobjectArray codeBlob2objectArray(JavaThread* thread, JNIEnv* env, CodeBl CHECK_JNI_EXCEPTION_(env, nullptr); env->SetObjectArrayElement(result, 3, obj); + obj = longBox(thread, env, cb->code_begin); + CHECK_JNI_EXCEPTION_(env, nullptr); + env->SetObjectArrayElement(result, 4, obj); + + obj = booleanBox(thread, env, cb->is_nmethod); + CHECK_JNI_EXCEPTION_(env, nullptr); + env->SetObjectArrayElement(result, 5, obj); + return result; } @@ -1627,6 +1640,44 @@ WB_ENTRY(jobjectArray, WB_GetNMethod(JNIEnv* env, jobject o, jobject method, jbo return result; WB_END +WB_ENTRY(void, WB_RelocateNMethodFromMethod(JNIEnv* env, jobject o, jobject method, jint blob_type)) + ResourceMark rm(THREAD); + jmethodID jmid = reflected_method_to_jmid(thread, env, method); + CHECK_JNI_EXCEPTION(env); + methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); + nmethod* code = mh->code(); + if (code != nullptr) { + MutexLocker ml_Compile_lock(Compile_lock); + CompiledICLocker ic_locker(code); + MutexLocker ml_CodeCache_lock(CodeCache_lock, Mutex::_no_safepoint_check_flag); + code->relocate(static_cast(blob_type)); + } +WB_END + +WB_ENTRY(void, WB_RelocateNMethodFromAddr(JNIEnv* env, jobject o, jlong addr, jint blob_type)) + ResourceMark rm(THREAD); + CHECK_JNI_EXCEPTION(env); + void* address = (void*) addr; + + if (address == nullptr) { + return; + } + + MutexLocker ml_Compile_lock(Compile_lock); + MutexLocker ml_CompiledIC_lock(CompiledIC_lock, Mutex::_no_safepoint_check_flag); + MutexLocker ml_CodeCache_lock(CodeCache_lock, Mutex::_no_safepoint_check_flag); + + // Verify that nmethod address is still valid + CodeBlob* blob = CodeCache::find_blob(address); + if (blob != nullptr && blob->is_nmethod()) { + nmethod* code = blob->as_nmethod(); + if (code->is_in_use()) { + CompiledICLocker ic_locker(code); + code->relocate(static_cast(blob_type)); + } + } +WB_END + CodeBlob* WhiteBox::allocate_code_blob(int size, CodeBlobType blob_type) { guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to be enabled"); BufferBlob* blob; @@ -2659,7 +2710,7 @@ WB_ENTRY(void, WB_WaitUnsafe(JNIEnv* env, jobject wb, jint time)) os::naked_short_sleep(time); WB_END -WB_ENTRY(void, WB_BusyWait(JNIEnv* env, jobject wb, jint time)) +WB_ENTRY(void, WB_BusyWaitCPUTime(JNIEnv* env, jobject wb, jint time)) ThreadToNativeFromVM ttn(thread); u8 start = os::current_thread_cpu_time(); u8 target_duration = time * (u8)1000000; @@ -2670,21 +2721,12 @@ WB_END WB_ENTRY(jboolean, WB_CPUSamplerSetOutOfStackWalking(JNIEnv* env, jobject wb, jboolean enable)) #if defined(ASSERT) && INCLUDE_JFR && defined(LINUX) - JfrCPUTimeThreadSampling::set_out_of_stack_walking_enabled(enable == JNI_TRUE); - return JNI_TRUE; + return JfrCPUTimeThreadSampling::set_out_of_stack_walking_enabled(enable == JNI_TRUE) ? JNI_TRUE : JNI_FALSE; #else return JNI_FALSE; #endif WB_END -WB_ENTRY(jlong, WB_CPUSamplerOutOfStackWalkingIterations(JNIEnv* env, jobject wb)) - #if defined(ASSERT) && INCLUDE_JFR && defined(LINUX) - return (jlong)JfrCPUTimeThreadSampling::out_of_stack_walking_iterations(); - #else - return 0; - #endif -WB_END - WB_ENTRY(jstring, WB_GetLibcName(JNIEnv* env, jobject o)) ThreadToNativeFromVM ttn(thread); jstring info_string = env->NewStringUTF(XSTR(LIBC)); @@ -2916,6 +2958,9 @@ static JNINativeMethod methods[] = { {CC"getCPUFeatures", CC"()Ljava/lang/String;", (void*)&WB_GetCPUFeatures }, {CC"getNMethod0", CC"(Ljava/lang/reflect/Executable;Z)[Ljava/lang/Object;", (void*)&WB_GetNMethod }, + {CC"relocateNMethodFromMethod0", CC"(Ljava/lang/reflect/Executable;I)V", + (void*)&WB_RelocateNMethodFromMethod }, + {CC"relocateNMethodFromAddr", CC"(JI)V", (void*)&WB_RelocateNMethodFromAddr }, {CC"allocateCodeBlob", CC"(II)J", (void*)&WB_AllocateCodeBlob }, {CC"freeCodeBlob", CC"(J)V", (void*)&WB_FreeCodeBlob }, {CC"getCodeHeapEntries", CC"(I)[Ljava/lang/Object;",(void*)&WB_GetCodeHeapEntries }, @@ -3038,9 +3083,8 @@ static JNINativeMethod methods[] = { {CC"isJVMTIIncluded", CC"()Z", (void*)&WB_IsJVMTIIncluded}, {CC"waitUnsafe", CC"(I)V", (void*)&WB_WaitUnsafe}, - {CC"busyWait", CC"(I)V", (void*)&WB_BusyWait}, + {CC"busyWaitCPUTime", CC"(I)V", (void*)&WB_BusyWaitCPUTime}, {CC"cpuSamplerSetOutOfStackWalking", CC"(Z)Z", (void*)&WB_CPUSamplerSetOutOfStackWalking}, - {CC"cpuSamplerOutOfStackWalkingIterations", CC"()J",(void*)&WB_CPUSamplerOutOfStackWalkingIterations}, {CC"getLibcName", CC"()Ljava/lang/String;", (void*)&WB_GetLibcName}, {CC"pinObject", CC"(Ljava/lang/Object;)V", (void*)&WB_PinObject}, diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index e43b18209bf..8b703cb442a 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -31,6 +31,7 @@ #include "classfile/stringTable.hpp" #include "classfile/symbolTable.hpp" #include "compiler/compilerDefinitions.hpp" +#include "gc/shared/gc_globals.hpp" #include "gc/shared/gcArguments.hpp" #include "gc/shared/gcConfig.hpp" #include "gc/shared/genArguments.hpp" @@ -1506,49 +1507,59 @@ size_t Arguments::limit_heap_by_allocatable_memory(size_t limit) { // Use static initialization to get the default before parsing static const size_t DefaultHeapBaseMinAddress = HeapBaseMinAddress; -void Arguments::set_heap_size() { - julong phys_mem; +static size_t clamp_by_size_t_max(uint64_t value) { + return (size_t)MIN2(value, (uint64_t)std::numeric_limits::max()); +} - // If the user specified one of these options, they - // want specific memory sizing so do not limit memory - // based on compressed oops addressability. - // Also, memory limits will be calculated based on - // available os physical memory, not our MaxRAM limit, - // unless MaxRAM is also specified. - bool override_coop_limit = (!FLAG_IS_DEFAULT(MaxRAMPercentage) || - !FLAG_IS_DEFAULT(MinRAMPercentage) || - !FLAG_IS_DEFAULT(InitialRAMPercentage) || - !FLAG_IS_DEFAULT(MaxRAM)); - if (override_coop_limit) { - if (FLAG_IS_DEFAULT(MaxRAM)) { - phys_mem = static_cast(os::physical_memory()); - FLAG_SET_ERGO(MaxRAM, (uint64_t)phys_mem); +void Arguments::set_heap_size() { + uint64_t physical_memory; + + // Check if the user has configured any limit on the amount of RAM we may use. + bool has_ram_limit = !FLAG_IS_DEFAULT(MaxRAMPercentage) || + !FLAG_IS_DEFAULT(MinRAMPercentage) || + !FLAG_IS_DEFAULT(InitialRAMPercentage) || + !FLAG_IS_DEFAULT(MaxRAM); + + if (has_ram_limit) { + if (!FLAG_IS_DEFAULT(MaxRAM)) { + // The user has configured MaxRAM, use that instead of physical memory + // reported by the OS. + physical_memory = MaxRAM; } else { - phys_mem = (julong)MaxRAM; + // The user has configured a limit, make sure MaxRAM reflects the physical + // memory limit that heap sizing takes into account. + physical_memory = os::physical_memory(); + FLAG_SET_ERGO(MaxRAM, physical_memory); } } else { - phys_mem = FLAG_IS_DEFAULT(MaxRAM) ? MIN2(static_cast(os::physical_memory()), (julong)MaxRAM) - : (julong)MaxRAM; + // If the user did not specify any limit, choose the lowest of the available + // physical memory and MaxRAM. MaxRAM is typically set to 128GB on 64-bit + // architecture. + physical_memory = MIN2(os::physical_memory(), MaxRAM); } - // If the maximum heap size has not been set with -Xmx, - // then set it as fraction of the size of physical memory, - // respecting the maximum and minimum sizes of the heap. + // If the maximum heap size has not been set with -Xmx, then set it as + // fraction of the size of physical memory, respecting the maximum and + // minimum sizes of the heap. if (FLAG_IS_DEFAULT(MaxHeapSize)) { - julong reasonable_max = (julong)(((double)phys_mem * MaxRAMPercentage) / 100); - const julong reasonable_min = (julong)(((double)phys_mem * MinRAMPercentage) / 100); + uint64_t min_memory = (uint64_t)(((double)physical_memory * MinRAMPercentage) / 100); + uint64_t max_memory = (uint64_t)(((double)physical_memory * MaxRAMPercentage) / 100); + + const size_t reasonable_min = clamp_by_size_t_max(min_memory); + size_t reasonable_max = clamp_by_size_t_max(max_memory); + if (reasonable_min < MaxHeapSize) { // Small physical memory, so use a minimum fraction of it for the heap reasonable_max = reasonable_min; } else { // Not-small physical memory, so require a heap at least // as large as MaxHeapSize - reasonable_max = MAX2(reasonable_max, (julong)MaxHeapSize); + reasonable_max = MAX2(reasonable_max, MaxHeapSize); } if (!FLAG_IS_DEFAULT(ErgoHeapSizeLimit) && ErgoHeapSizeLimit != 0) { // Limit the heap size to ErgoHeapSizeLimit - reasonable_max = MIN2(reasonable_max, (julong)ErgoHeapSizeLimit); + reasonable_max = MIN2(reasonable_max, ErgoHeapSizeLimit); } reasonable_max = limit_heap_by_allocatable_memory(reasonable_max); @@ -1558,9 +1569,9 @@ void Arguments::set_heap_size() { // so be sure that the maximum size is consistent. Done // after call to limit_heap_by_allocatable_memory because that // method might reduce the allocation size. - reasonable_max = MAX2(reasonable_max, (julong)InitialHeapSize); + reasonable_max = MAX2(reasonable_max, InitialHeapSize); } else if (!FLAG_IS_DEFAULT(MinHeapSize)) { - reasonable_max = MAX2(reasonable_max, (julong)MinHeapSize); + reasonable_max = MAX2(reasonable_max, MinHeapSize); } #ifdef _LP64 @@ -1569,8 +1580,8 @@ void Arguments::set_heap_size() { if (!FLAG_IS_DEFAULT(HeapBaseMinAddress)) { if (HeapBaseMinAddress < DefaultHeapBaseMinAddress) { // matches compressed oops printing flags - log_debug(gc, heap, coops)("HeapBaseMinAddress must be at least %zu" - " (%zuG) which is greater than value given %zu", + log_debug(gc, heap, coops)("HeapBaseMinAddress must be at least %zu " + "(%zuG) which is greater than value given %zu", DefaultHeapBaseMinAddress, DefaultHeapBaseMinAddress/G, HeapBaseMinAddress); @@ -1578,61 +1589,62 @@ void Arguments::set_heap_size() { } } } - if (UseCompressedOops) { - // Limit the heap size to the maximum possible when using compressed oops - julong max_coop_heap = (julong)max_heap_for_compressed_oops(); - if (HeapBaseMinAddress + MaxHeapSize < max_coop_heap) { - // Heap should be above HeapBaseMinAddress to get zero based compressed oops - // but it should be not less than default MaxHeapSize. + if (UseCompressedOops) { + size_t heap_end = HeapBaseMinAddress + MaxHeapSize; + size_t max_coop_heap = max_heap_for_compressed_oops(); + + // Limit the heap size to the maximum possible when using compressed oops + if (heap_end < max_coop_heap) { + // Heap should be above HeapBaseMinAddress to get zero based compressed + // oops but it should be not less than default MaxHeapSize. max_coop_heap -= HeapBaseMinAddress; } - // If user specified flags prioritizing os physical - // memory limits, then disable compressed oops if - // limits exceed max_coop_heap and UseCompressedOops - // was not specified. + // If the user has configured any limit on the amount of RAM we may use, + // then disable compressed oops if the calculated max exceeds max_coop_heap + // and UseCompressedOops was not specified. if (reasonable_max > max_coop_heap) { - if (FLAG_IS_ERGO(UseCompressedOops) && override_coop_limit) { - aot_log_info(aot)("UseCompressedOops disabled due to" - " max heap %zu > compressed oop heap %zu. " - "Please check the setting of MaxRAMPercentage %5.2f." - ,(size_t)reasonable_max, (size_t)max_coop_heap, MaxRAMPercentage); + if (FLAG_IS_ERGO(UseCompressedOops) && has_ram_limit) { + aot_log_info(aot)("UseCompressedOops disabled due to " + "max heap %zu > compressed oop heap %zu. " + "Please check the setting of MaxRAMPercentage %5.2f.", + reasonable_max, max_coop_heap, MaxRAMPercentage); FLAG_SET_ERGO(UseCompressedOops, false); } else { - reasonable_max = MIN2(reasonable_max, max_coop_heap); + reasonable_max = max_coop_heap; } } } #endif // _LP64 - log_trace(gc, heap)(" Maximum heap size %zu", (size_t) reasonable_max); - FLAG_SET_ERGO(MaxHeapSize, (size_t)reasonable_max); + log_trace(gc, heap)(" Maximum heap size %zu", reasonable_max); + FLAG_SET_ERGO(MaxHeapSize, reasonable_max); } // If the minimum or initial heap_size have not been set or requested to be set // ergonomically, set them accordingly. if (InitialHeapSize == 0 || MinHeapSize == 0) { - julong reasonable_minimum = (julong)(OldSize + NewSize); - - reasonable_minimum = MIN2(reasonable_minimum, (julong)MaxHeapSize); - + size_t reasonable_minimum = clamp_by_size_t_max((uint64_t)OldSize + (uint64_t)NewSize); + reasonable_minimum = MIN2(reasonable_minimum, MaxHeapSize); reasonable_minimum = limit_heap_by_allocatable_memory(reasonable_minimum); if (InitialHeapSize == 0) { - julong reasonable_initial = (julong)(((double)phys_mem * InitialRAMPercentage) / 100); + uint64_t initial_memory = (uint64_t)(((double)physical_memory * InitialRAMPercentage) / 100); + size_t reasonable_initial = clamp_by_size_t_max(initial_memory); reasonable_initial = limit_heap_by_allocatable_memory(reasonable_initial); - reasonable_initial = MAX3(reasonable_initial, reasonable_minimum, (julong)MinHeapSize); - reasonable_initial = MIN2(reasonable_initial, (julong)MaxHeapSize); + reasonable_initial = MAX3(reasonable_initial, reasonable_minimum, MinHeapSize); + reasonable_initial = MIN2(reasonable_initial, MaxHeapSize); FLAG_SET_ERGO(InitialHeapSize, (size_t)reasonable_initial); log_trace(gc, heap)(" Initial heap size %zu", InitialHeapSize); } + // If the minimum heap size has not been set (via -Xms or -XX:MinHeapSize), // synchronize with InitialHeapSize to avoid errors with the default value. if (MinHeapSize == 0) { - FLAG_SET_ERGO(MinHeapSize, MIN2((size_t)reasonable_minimum, InitialHeapSize)); + FLAG_SET_ERGO(MinHeapSize, MIN2(reasonable_minimum, InitialHeapSize)); log_trace(gc, heap)(" Minimum heap size %zu", MinHeapSize); } } diff --git a/src/hotspot/share/runtime/continuation.hpp b/src/hotspot/share/runtime/continuation.hpp index e678e0bd42b..0cfd484361d 100644 --- a/src/hotspot/share/runtime/continuation.hpp +++ b/src/hotspot/share/runtime/continuation.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -53,10 +53,9 @@ enum freeze_result { freeze_ok_bottom = 1, freeze_pinned_cs = 2, freeze_pinned_native = 3, - freeze_pinned_monitor = 4, - freeze_exception = 5, - freeze_not_mounted = 6, - freeze_unsupported = 7 + freeze_exception = 4, + freeze_not_mounted = 5, + freeze_unsupported = 6 }; class Continuation : AllStatic { diff --git a/src/hotspot/share/runtime/continuationEntry.cpp b/src/hotspot/share/runtime/continuationEntry.cpp index 4551bfa7cc8..69a20808798 100644 --- a/src/hotspot/share/runtime/continuationEntry.cpp +++ b/src/hotspot/share/runtime/continuationEntry.cpp @@ -107,7 +107,6 @@ void ContinuationEntry::describe(FrameValues& values, int frame_no) const { values.describe(frame_no, (intptr_t*)(usp + in_bytes(ContinuationEntry::argsize_offset())), "argsize"); values.describe(frame_no, (intptr_t*)(usp + in_bytes(ContinuationEntry::pin_count_offset())), "pin_count"); values.describe(frame_no, (intptr_t*)(usp + in_bytes(ContinuationEntry::parent_cont_fastpath_offset())), "parent fastpath"); - values.describe(frame_no, (intptr_t*)(usp + in_bytes(ContinuationEntry::parent_held_monitor_count_offset())), "parent held monitor count"); } #endif diff --git a/src/hotspot/share/runtime/continuationEntry.hpp b/src/hotspot/share/runtime/continuationEntry.hpp index 3c8532b9e87..8361f2f912b 100644 --- a/src/hotspot/share/runtime/continuationEntry.hpp +++ b/src/hotspot/share/runtime/continuationEntry.hpp @@ -79,11 +79,6 @@ class ContinuationEntry { // The caller (if there is one) is the still frozen top frame in the StackChunk. int _argsize; intptr_t* _parent_cont_fastpath; -#ifdef _LP64 - int64_t _parent_held_monitor_count; -#else - int32_t _parent_held_monitor_count; -#endif uint32_t _pin_count; public: @@ -94,7 +89,6 @@ class ContinuationEntry { static ByteSize argsize_offset() { return byte_offset_of(ContinuationEntry, _argsize); } static ByteSize pin_count_offset(){ return byte_offset_of(ContinuationEntry, _pin_count); } static ByteSize parent_cont_fastpath_offset() { return byte_offset_of(ContinuationEntry, _parent_cont_fastpath); } - static ByteSize parent_held_monitor_count_offset() { return byte_offset_of(ContinuationEntry, _parent_held_monitor_count); } static address return_pc() { return _return_pc; } static address return_pc_address() { return (address)&_return_pc; } @@ -103,7 +97,6 @@ class ContinuationEntry { static size_t size() { return align_up((int)sizeof(ContinuationEntry), 2*wordSize); } ContinuationEntry* parent() const { return _parent; } - int64_t parent_held_monitor_count() const { return (int64_t)_parent_held_monitor_count; } static address entry_pc() { return _return_pc; } intptr_t* entry_sp() const { return (intptr_t*)this; } diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp index 024b69c765f..3e509e71551 100644 --- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp +++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp @@ -1626,7 +1626,7 @@ static void invalidate_jvmti_stack(JavaThread* thread) { } static void jvmti_yield_cleanup(JavaThread* thread, ContinuationWrapper& cont) { - if (JvmtiExport::has_frame_pops(thread)) { + if (!cont.entry()->is_virtual_thread() && JvmtiExport::has_frame_pops(thread)) { int num_frames = num_java_frames(cont); ContinuationWrapper::SafepointOp so(Thread::current(), cont); @@ -1736,13 +1736,10 @@ static inline freeze_result freeze_internal(JavaThread* current, intptr_t* const assert(entry->is_virtual_thread() == (entry->scope(current) == java_lang_VirtualThread::vthread_scope()), ""); - assert((current->held_monitor_count() == 0 && current->jni_monitor_count() == 0), - "Held monitor count should not be used for lightweight locking: " INT64_FORMAT " JNI: " INT64_FORMAT, (int64_t)current->held_monitor_count(), (int64_t)current->jni_monitor_count()); - - if (entry->is_pinned() || current->held_monitor_count() > 0) { - log_develop_debug(continuations)("PINNED due to critical section/hold monitor"); + if (entry->is_pinned()) { + log_develop_debug(continuations)("PINNED due to critical section"); verify_continuation(cont.continuation()); - freeze_result res = entry->is_pinned() ? freeze_pinned_cs : freeze_pinned_monitor; + const freeze_result res = freeze_pinned_cs; if (!preempt) { JFR_ONLY(current->set_last_freeze_fail_result(res);) } @@ -1799,8 +1796,6 @@ static freeze_result is_pinned0(JavaThread* thread, oop cont_scope, bool safepoi } if (entry->is_pinned()) { return freeze_pinned_cs; - } else if (thread->held_monitor_count() > 0) { - return freeze_pinned_monitor; } RegisterMap map(thread, @@ -1836,15 +1831,12 @@ static freeze_result is_pinned0(JavaThread* thread, oop cont_scope, bool safepoi if (scope == cont_scope) { break; } - intx monitor_count = entry->parent_held_monitor_count(); entry = entry->parent(); if (entry == nullptr) { break; } if (entry->is_pinned()) { return freeze_pinned_cs; - } else if (monitor_count > 0) { - return freeze_pinned_monitor; } } } diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index a96c18a18aa..853c6554022 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -589,12 +589,7 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread // Verify we have the right vframeArray assert(cb->frame_size() >= 0, "Unexpected frame size"); intptr_t* unpack_sp = stub_frame.sp() + cb->frame_size(); - - // If the deopt call site is a MethodHandle invoke call site we have - // to adjust the unpack_sp. - nmethod* deoptee_nm = deoptee.cb()->as_nmethod_or_null(); - if (deoptee_nm != nullptr && deoptee_nm->is_method_handle_return(deoptee.pc())) - unpack_sp = deoptee.unextended_sp(); + assert(unpack_sp == deoptee.unextended_sp(), "must be"); #ifdef ASSERT assert(cb->is_deoptimization_stub() || diff --git a/src/hotspot/share/runtime/frame.cpp b/src/hotspot/share/runtime/frame.cpp index 75c6e388b0d..e578e614440 100644 --- a/src/hotspot/share/runtime/frame.cpp +++ b/src/hotspot/share/runtime/frame.cpp @@ -206,10 +206,7 @@ address frame::raw_pc() const { if (is_deoptimized_frame()) { nmethod* nm = cb()->as_nmethod_or_null(); assert(nm != nullptr, "only nmethod is expected here"); - if (nm->is_method_handle_return(pc())) - return nm->deopt_mh_handler_begin() - pc_return_offset; - else - return nm->deopt_handler_begin() - pc_return_offset; + return nm->deopt_handler_begin() - pc_return_offset; } else { return (pc() - pc_return_offset); } @@ -358,9 +355,7 @@ void frame::deoptimize(JavaThread* thread) { // If the call site is a MethodHandle call site use the MH deopt handler. nmethod* nm = _cb->as_nmethod(); - address deopt = nm->is_method_handle_return(pc()) ? - nm->deopt_mh_handler_begin() : - nm->deopt_handler_begin(); + address deopt = nm->deopt_handler_begin(); NativePostCallNop* inst = nativePostCallNop_at(pc()); diff --git a/src/hotspot/share/runtime/frame.inline.hpp b/src/hotspot/share/runtime/frame.inline.hpp index 449abddd443..cbf01dd5763 100644 --- a/src/hotspot/share/runtime/frame.inline.hpp +++ b/src/hotspot/share/runtime/frame.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -76,7 +76,10 @@ inline address frame::get_deopt_original_pc() const { nmethod* nm = _cb->as_nmethod_or_null(); if (nm != nullptr && nm->is_deopt_pc(_pc)) { - return nm->get_original_pc(this); + address original_pc = nm->get_original_pc(this); + assert(nm->insts_contains_inclusive(original_pc), + "original PC must be in the main code section of the compiled method (or must be immediately following it)"); + return original_pc; } return nullptr; } diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index dac01d018bf..513edaf6588 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -1565,6 +1565,9 @@ const int ObjectAlignmentInBytes = 8; "Start aggressive sweeping if less than X[%] of the total code cache is free.")\ range(0, 100) \ \ + product(bool, NMethodRelocation, false, EXPERIMENTAL, \ + "Enables use of experimental function nmethod::relocate()") \ + \ /* interpreter debugging */ \ develop(intx, BinarySwitchThreshold, 5, \ "Minimal number of lookupswitch entries for rewriting to binary " \ diff --git a/src/hotspot/share/runtime/handles.cpp b/src/hotspot/share/runtime/handles.cpp index cd3bd3eb3e0..cfd693aa3cc 100644 --- a/src/hotspot/share/runtime/handles.cpp +++ b/src/hotspot/share/runtime/handles.cpp @@ -97,31 +97,26 @@ DEF_METADATA_HANDLE_FN_NOINLINE(method, Method) DEF_METADATA_HANDLE_FN_NOINLINE(constantPool, ConstantPool) -static uintx chunk_oops_do(OopClosure* f, Chunk* chunk, char* chunk_top) { +static void chunk_oops_do(OopClosure* f, Chunk* chunk, char* chunk_top) { oop* bottom = (oop*) chunk->bottom(); oop* top = (oop*) chunk_top; - uintx handles_visited = top - bottom; assert(top >= bottom && top <= (oop*) chunk->top(), "just checking"); - // during GC phase 3, a handle may be a forward pointer that - // is not yet valid, so loosen the assertion + while (bottom < top) { f->do_oop(bottom++); } - return handles_visited; } void HandleArea::oops_do(OopClosure* f) { - uintx handles_visited = 0; // First handle the current chunk. It is filled to the high water mark. - handles_visited += chunk_oops_do(f, _chunk, _hwm); + chunk_oops_do(f, _chunk, _hwm); + // Then handle all previous chunks. They are completely filled. Chunk* k = _first; while(k != _chunk) { - handles_visited += chunk_oops_do(f, k, k->top()); + chunk_oops_do(f, k, k->top()); k = k->next(); } - - if (_prev != nullptr) _prev->oops_do(f); } void HandleMark::initialize(Thread* thread) { diff --git a/src/hotspot/share/runtime/handles.hpp b/src/hotspot/share/runtime/handles.hpp index d2020e34121..7b51b4aff1c 100644 --- a/src/hotspot/share/runtime/handles.hpp +++ b/src/hotspot/share/runtime/handles.hpp @@ -184,13 +184,11 @@ class HandleArea: public Arena { int _handle_mark_nesting; int _no_handle_mark_nesting; #endif - HandleArea* _prev; // link to outer (older) area public: // Constructor - HandleArea(MemTag mem_tag, HandleArea* prev) : Arena(mem_tag, Tag::tag_ha, Chunk::tiny_size) { + HandleArea(MemTag mem_tag) : Arena(mem_tag, Tag::tag_ha, Chunk::tiny_size) { DEBUG_ONLY(_handle_mark_nesting = 0); DEBUG_ONLY(_no_handle_mark_nesting = 0); - _prev = prev; } // Handle allocation diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index f5cd43b1769..8bb8095878f 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -476,7 +476,6 @@ JavaThread::JavaThread(MemTag mem_tag) : _exception_oop(oop()), _exception_pc(nullptr), _exception_handler_pc(nullptr), - _is_method_handle_return(0), _jni_active_critical(0), _pending_jni_exception_check_fn(nullptr), @@ -489,8 +488,6 @@ JavaThread::JavaThread(MemTag mem_tag) : _cont_entry(nullptr), _cont_fastpath(nullptr), _cont_fastpath_thread_state(1), - _held_monitor_count(0), - _jni_monitor_count(0), _unlocked_inflated_monitor(nullptr), _preempt_alternate_return(nullptr), @@ -928,27 +925,6 @@ void JavaThread::exit(bool destroy_vm, ExitType exit_type) { "should not have a Java frame when detaching or exiting"); ObjectSynchronizer::release_monitors_owned_by_thread(this); assert(!this->has_pending_exception(), "release_monitors should have cleared"); - // Check for monitor counts being out of sync. - assert(held_monitor_count() == jni_monitor_count(), - "held monitor count should be equal to jni: %zd != %zd", - held_monitor_count(), jni_monitor_count()); - // All in-use monitors, including JNI-locked ones, should have been released above. - assert(held_monitor_count() == 0, "Failed to unlock %zd object monitors", - held_monitor_count()); - } else { - // Check for monitor counts being out of sync. - assert(held_monitor_count() == jni_monitor_count(), - "held monitor count should be equal to jni: %zd != %zd", - held_monitor_count(), jni_monitor_count()); - // It is possible that a terminating thread failed to unlock monitors it locked - // via JNI so we don't assert the count is zero. - } - - if (CheckJNICalls && jni_monitor_count() > 0) { - // We would like a fatal here, but due to we never checked this before there - // is a lot of tests which breaks, even with an error log. - log_debug(jni)("JavaThread %s (tid: %zu) with Objects still locked by JNI MonitorEnter.", - exit_type == JavaThread::normal_exit ? "exiting" : "detaching", os::current_thread_id()); } // These things needs to be done while we are still a Java Thread. Make sure that thread @@ -1989,26 +1965,6 @@ void JavaThread::trace_stack() { #endif // PRODUCT -// Slow-path increment of the held monitor counts. JNI locking is always -// this slow-path. -void JavaThread::inc_held_monitor_count(intx i, bool jni) { -#ifdef SUPPORT_MONITOR_COUNT - // Nothing to do. Just do some sanity check. - assert(_held_monitor_count == 0, "counter should not be used"); - assert(_jni_monitor_count == 0, "counter should not be used"); -#endif // SUPPORT_MONITOR_COUNT -} - -// Slow-path decrement of the held monitor counts. JNI unlocking is always -// this slow-path. -void JavaThread::dec_held_monitor_count(intx i, bool jni) { -#ifdef SUPPORT_MONITOR_COUNT - // Nothing to do. Just do some sanity check. - assert(_held_monitor_count == 0, "counter should not be used"); - assert(_jni_monitor_count == 0, "counter should not be used"); -#endif // SUPPORT_MONITOR_COUNT -} - frame JavaThread::vthread_last_frame() { assert (is_vthread_mounted(), "Virtual thread not mounted"); return last_frame(); diff --git a/src/hotspot/share/runtime/javaThread.hpp b/src/hotspot/share/runtime/javaThread.hpp index 00bc5969196..c8be1594a69 100644 --- a/src/hotspot/share/runtime/javaThread.hpp +++ b/src/hotspot/share/runtime/javaThread.hpp @@ -450,7 +450,6 @@ class JavaThread: public Thread { volatile oop _exception_oop; // Exception thrown in compiled code volatile address _exception_pc; // PC where exception happened volatile address _exception_handler_pc; // PC for handler of exception - volatile int _is_method_handle_return; // true (== 1) if the current exception PC is a MethodHandle call site. private: // support for JNI critical regions @@ -477,9 +476,6 @@ class JavaThread: public Thread { // frame inside the continuation that we know about int _cont_fastpath_thread_state; // whether global thread state allows continuation fastpath (JVMTI) - // It's signed for error detection. - intx _held_monitor_count; // used by continuations for fast lock detection - intx _jni_monitor_count; ObjectMonitor* _unlocked_inflated_monitor; // This is the field we poke in the interpreter and native @@ -663,13 +659,6 @@ private: bool cont_fastpath() const { return _cont_fastpath == nullptr && _cont_fastpath_thread_state != 0; } bool cont_fastpath_thread_state() const { return _cont_fastpath_thread_state != 0; } - void inc_held_monitor_count(intx i = 1, bool jni = false); - void dec_held_monitor_count(intx i = 1, bool jni = false); - - intx held_monitor_count() { return _held_monitor_count; } - intx jni_monitor_count() { return _jni_monitor_count; } - void clear_jni_monitor_count() { _jni_monitor_count = 0; } - // Support for SharedRuntime::monitor_exit_helper() ObjectMonitor* unlocked_inflated_monitor() const { return _unlocked_inflated_monitor; } void clear_unlocked_inflated_monitor() { @@ -817,7 +806,6 @@ public: void set_exception_oop(oop o); void set_exception_pc(address a) { _exception_pc = a; } void set_exception_handler_pc(address a) { _exception_handler_pc = a; } - void set_is_method_handle_return(bool value) { _is_method_handle_return = value ? 1 : 0; } void clear_exception_oop_and_pc() { set_exception_oop(nullptr); @@ -866,7 +854,6 @@ public: static ByteSize exception_oop_offset() { return byte_offset_of(JavaThread, _exception_oop); } static ByteSize exception_pc_offset() { return byte_offset_of(JavaThread, _exception_pc); } static ByteSize exception_handler_pc_offset() { return byte_offset_of(JavaThread, _exception_handler_pc); } - static ByteSize is_method_handle_return_offset() { return byte_offset_of(JavaThread, _is_method_handle_return); } static ByteSize active_handles_offset() { return byte_offset_of(JavaThread, _active_handles); } @@ -900,8 +887,6 @@ public: static ByteSize cont_entry_offset() { return byte_offset_of(JavaThread, _cont_entry); } static ByteSize cont_fastpath_offset() { return byte_offset_of(JavaThread, _cont_fastpath); } - static ByteSize held_monitor_count_offset() { return byte_offset_of(JavaThread, _held_monitor_count); } - static ByteSize jni_monitor_count_offset() { return byte_offset_of(JavaThread, _jni_monitor_count); } static ByteSize preemption_cancelled_offset() { return byte_offset_of(JavaThread, _preemption_cancelled); } static ByteSize preempt_alternate_return_offset() { return byte_offset_of(JavaThread, _preempt_alternate_return); } static ByteSize unlocked_inflated_monitor_offset() { return byte_offset_of(JavaThread, _unlocked_inflated_monitor); } diff --git a/src/hotspot/share/runtime/nonJavaThread.cpp b/src/hotspot/share/runtime/nonJavaThread.cpp index 2fb4c2dce02..cb0c3f8910d 100644 --- a/src/hotspot/share/runtime/nonJavaThread.cpp +++ b/src/hotspot/share/runtime/nonJavaThread.cpp @@ -50,15 +50,19 @@ public: List() : _head(nullptr), _protect() {} }; -NonJavaThread::List NonJavaThread::_the_list; +DeferredStatic NonJavaThread::_the_list; + +void NonJavaThread::init() { + _the_list.initialize(); +} NonJavaThread::Iterator::Iterator() : - _protect_enter(_the_list._protect.enter()), - _current(AtomicAccess::load_acquire(&_the_list._head)) + _protect_enter(_the_list->_protect.enter()), + _current(AtomicAccess::load_acquire(&_the_list->_head)) {} NonJavaThread::Iterator::~Iterator() { - _the_list._protect.exit(_protect_enter); + _the_list->_protect.exit(_protect_enter); } void NonJavaThread::Iterator::step() { @@ -76,8 +80,8 @@ void NonJavaThread::add_to_the_list() { MutexLocker ml(NonJavaThreadsList_lock, Mutex::_no_safepoint_check_flag); // Initialize BarrierSet-related data before adding to list. BarrierSet::barrier_set()->on_thread_attach(this); - AtomicAccess::release_store(&_next, _the_list._head); - AtomicAccess::release_store(&_the_list._head, this); + AtomicAccess::release_store(&_next, _the_list->_head); + AtomicAccess::release_store(&_the_list->_head, this); } void NonJavaThread::remove_from_the_list() { @@ -85,7 +89,7 @@ void NonJavaThread::remove_from_the_list() { MutexLocker ml(NonJavaThreadsList_lock, Mutex::_no_safepoint_check_flag); // Cleanup BarrierSet-related data before removing from list. BarrierSet::barrier_set()->on_thread_detach(this); - NonJavaThread* volatile* p = &_the_list._head; + NonJavaThread* volatile* p = &_the_list->_head; for (NonJavaThread* t = *p; t != nullptr; p = &t->_next, t = *p) { if (t == this) { *p = _next; @@ -97,7 +101,7 @@ void NonJavaThread::remove_from_the_list() { // allowed, so do it while holding a dedicated lock. Outside and distinct // from NJTList_lock in case an iteration attempts to lock it. MutexLocker ml(NonJavaThreadsListSync_lock, Mutex::_no_safepoint_check_flag); - _the_list._protect.synchronize(); + _the_list->_protect.synchronize(); _next = nullptr; // Safe to drop the link now. } @@ -344,4 +348,3 @@ void WatcherThread::print_on(outputStream* st) const { Thread::print_on(st); st->cr(); } - diff --git a/src/hotspot/share/runtime/nonJavaThread.hpp b/src/hotspot/share/runtime/nonJavaThread.hpp index b2ac6a31737..aaf0dcdfa2d 100644 --- a/src/hotspot/share/runtime/nonJavaThread.hpp +++ b/src/hotspot/share/runtime/nonJavaThread.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,12 +26,18 @@ #define SHARE_RUNTIME_NONJAVATHREAD_HPP #include "runtime/thread.hpp" +#include "utilities/deferredStatic.hpp" class NonJavaThread: public Thread { - NonJavaThread* volatile _next; + friend class Threads; class List; - static List _the_list; + static DeferredStatic _the_list; + + // Deferred static initialization + static void init(); + + NonJavaThread* volatile _next; void add_to_the_list(); void remove_from_the_list(); diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index 8859f6e7f5f..142324fec7a 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -117,6 +117,8 @@ OopStorage* ObjectMonitor::_oop_storage = nullptr; OopHandle ObjectMonitor::_vthread_list_head; ParkEvent* ObjectMonitor::_vthread_unparker_ParkEvent = nullptr; +static const jlong MAX_RECHECK_INTERVAL = 1000; + // ----------------------------------------------------------------------------- // Theory of operations -- Monitors lists, thread residency, etc: // @@ -294,6 +296,7 @@ ObjectMonitor::ObjectMonitor(oop object) : _succ(NO_OWNER), _SpinDuration(ObjectMonitor::Knob_SpinLimit), _contentions(0), + _unmounted_vthreads(0), _wait_set(nullptr), _waiters(0), _wait_set_lock(0) @@ -983,19 +986,18 @@ void ObjectMonitor::enter_internal(JavaThread* current) { // to defer the state transitions until absolutely necessary, // and in doing so avoid some transitions ... - // For virtual threads that are pinned, do a timed-park instead to - // alleviate some deadlocks cases where the succesor is an unmounted - // virtual thread that cannot run. This can happen in particular when - // this virtual thread is currently loading/initializing a class, and - // all other carriers have a vthread pinned to it waiting for said class - // to be loaded/initialized. - static int MAX_RECHECK_INTERVAL = 1000; - int recheck_interval = 1; - bool do_timed_parked = false; - ContinuationEntry* ce = current->last_continuation(); - if (ce != nullptr && ce->is_virtual_thread()) { - do_timed_parked = true; - } + // If there are unmounted virtual threads ahead in the _entry_list we want + // to do a timed-park instead to alleviate some deadlock cases where one + // of them is picked as the successor but cannot run due to having run out + // of carriers. This can happen, for example, if this is a pinned virtual + // thread currently loading or initializining a class, and all other carriers + // have a pinned vthread waiting for said class to be loaded/initialized. + // Read counter *after* adding this thread to the _entry_list. Adding to + // _entry_list uses Atomic::cmpxchg() which already provides a fence that + // prevents this load from floating up previous store. + // Note that we can have false positives where timed-park is not necessary. + bool do_timed_parked = has_unmounted_vthreads(); + jlong recheck_interval = 1; for (;;) { @@ -1006,7 +1008,7 @@ void ObjectMonitor::enter_internal(JavaThread* current) { // park self if (do_timed_parked) { - current->_ParkEvent->park((jlong) recheck_interval); + current->_ParkEvent->park(recheck_interval); // Increase the recheck_interval, but clamp the value. recheck_interval *= 8; if (recheck_interval > MAX_RECHECK_INTERVAL) { @@ -1090,6 +1092,22 @@ void ObjectMonitor::reenter_internal(JavaThread* current, ObjectWaiter* currentN assert(_waiters > 0, "invariant"); assert_mark_word_consistency(); + // If there are unmounted virtual threads ahead in the _entry_list we want + // to do a timed-park instead to alleviate some deadlock cases where one + // of them is picked as the successor but cannot run due to having run out + // of carriers. This can happen, for example, if a mixed of unmounted and + // pinned vthreads taking up all the carriers are waiting for a class to be + // initialized, and the selected successor is one of the unmounted vthreads. + // Although this method is used for the "notification" case, it could be + // that this thread reached here without been added to the _entry_list yet. + // This can happen if it was interrupted or the wait timed-out at the same + // time. In that case we rely on currentNode->_do_timed_park, which will be + // read on the next loop iteration, after consuming the park permit set by + // the notifier in notify_internal. + // Note that we can have false positives where timed-park is not necessary. + bool do_timed_parked = has_unmounted_vthreads(); + jlong recheck_interval = 1; + for (;;) { ObjectWaiter::TStates v = currentNode->TState; guarantee(v == ObjectWaiter::TS_ENTER, "invariant"); @@ -1114,7 +1132,16 @@ void ObjectMonitor::reenter_internal(JavaThread* current, ObjectWaiter* currentN { ClearSuccOnSuspend csos(this); ThreadBlockInVMPreprocess tbivs(current, csos, true /* allow_suspend */); - current->_ParkEvent->park(); + if (do_timed_parked) { + current->_ParkEvent->park(recheck_interval); + // Increase the recheck_interval, but clamp the value. + recheck_interval *= 8; + if (recheck_interval > MAX_RECHECK_INTERVAL) { + recheck_interval = MAX_RECHECK_INTERVAL; + } + } else { + current->_ParkEvent->park(); + } } } @@ -1134,6 +1161,9 @@ void ObjectMonitor::reenter_internal(JavaThread* current, ObjectWaiter* currentN // Invariant: after clearing _succ a contending thread // *must* retry _owner before parking. OrderAccess::fence(); + + // See comment in notify_internal + do_timed_parked |= currentNode->_do_timed_park; } // Current has acquired the lock -- Unlink current from the _entry_list. @@ -1161,9 +1191,16 @@ bool ObjectMonitor::vthread_monitor_enter(JavaThread* current, ObjectWaiter* wai oop vthread = current->vthread(); ObjectWaiter* node = waiter != nullptr ? waiter : new ObjectWaiter(vthread, this); + + // Increment counter *before* adding the vthread to the _entry_list. + // Adding to _entry_list uses Atomic::cmpxchg() which already provides + // a fence that prevents reordering of the stores. + inc_unmounted_vthreads(); + if (try_lock_or_add_to_entry_list(current, node)) { // We got the lock. if (waiter == nullptr) delete node; // for Object.wait() don't delete yet + dec_unmounted_vthreads(); return true; } // This thread is now added to the entry_list. @@ -1175,6 +1212,7 @@ bool ObjectMonitor::vthread_monitor_enter(JavaThread* current, ObjectWaiter* wai unlink_after_acquire(current, node); if (has_successor(current)) clear_successor(); if (waiter == nullptr) delete node; // for Object.wait() don't delete yet + dec_unmounted_vthreads(); return true; } @@ -1230,6 +1268,7 @@ bool ObjectMonitor::resume_operation(JavaThread* current, ObjectWaiter* node, Co void ObjectMonitor::vthread_epilog(JavaThread* current, ObjectWaiter* node) { assert(has_owner(current), "invariant"); add_to_contentions(-1); + dec_unmounted_vthreads(); if (has_successor(current)) clear_successor(); @@ -1952,7 +1991,6 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { int relock_count = JvmtiDeferredUpdates::get_and_reset_relock_count_after_wait(current); _recursions = save // restore the old recursion count + relock_count; // increased by the deferred relock count - current->inc_held_monitor_count(relock_count); // Deopt never entered these counts. _waiters--; // decrement the number of waiters // Verify a few postconditions @@ -2004,6 +2042,10 @@ bool ObjectMonitor::notify_internal(JavaThread* current) { old_state == java_lang_VirtualThread::TIMED_WAIT) { java_lang_VirtualThread::cmpxchg_state(vthread, old_state, java_lang_VirtualThread::BLOCKED); } + // Increment counter *before* adding the vthread to the _entry_list. + // Adding to _entry_list uses Atomic::cmpxchg() which already provides + // a fence that prevents reordering of the stores. + inc_unmounted_vthreads(); } iterator->_notified = true; @@ -2021,6 +2063,24 @@ bool ObjectMonitor::notify_internal(JavaThread* current) { if (!iterator->is_vthread()) { iterator->wait_reenter_begin(this); + + // Read counter *after* adding the thread to the _entry_list. + // Adding to _entry_list uses Atomic::cmpxchg() which already provides + // a fence that prevents this load from floating up previous store. + if (has_unmounted_vthreads()) { + // Wake up the thread to alleviate some deadlock cases where the successor + // that will be picked up when this thread releases the monitor is an unmounted + // virtual thread that cannot run due to having run out of carriers. Upon waking + // up, the thread will call reenter_internal() which will use timed-park in case + // there is contention and there are still vthreads in the _entry_list. + // If the target was interrupted or the wait timed-out at the same time, it could + // have reached reenter_internal and read a false value of has_unmounted_vthreads() + // before we added it to the _entry_list above. To deal with that case, we set _do_timed_park + // which will be read by the target on the next loop iteration in reenter_internal. + iterator->_do_timed_park = true; + JavaThread* t = iterator->thread(); + t->_ParkEvent->unpark(); + } } } Thread::SpinRelease(&_wait_set_lock); @@ -2460,6 +2520,7 @@ ObjectWaiter::ObjectWaiter(JavaThread* current) { _is_wait = false; _at_reenter = false; _interrupted = false; + _do_timed_park = false; _active = false; } @@ -2632,6 +2693,7 @@ void ObjectMonitor::print_debug_style_on(outputStream* st) const { st->print_cr(" _succ = " INT64_FORMAT, successor()); st->print_cr(" _SpinDuration = %d", _SpinDuration); st->print_cr(" _contentions = %d", contentions()); + st->print_cr(" _unmounted_vthreads = " INT64_FORMAT, _unmounted_vthreads); st->print_cr(" _wait_set = " INTPTR_FORMAT, p2i(_wait_set)); st->print_cr(" _waiters = %d", _waiters); st->print_cr(" _wait_set_lock = %d", _wait_set_lock); diff --git a/src/hotspot/share/runtime/objectMonitor.hpp b/src/hotspot/share/runtime/objectMonitor.hpp index 50aec549045..058e0317ec1 100644 --- a/src/hotspot/share/runtime/objectMonitor.hpp +++ b/src/hotspot/share/runtime/objectMonitor.hpp @@ -56,6 +56,7 @@ class ObjectWaiter : public CHeapObj { bool _is_wait; bool _at_reenter; bool _interrupted; + bool _do_timed_park; bool _active; // Contention monitoring is enabled public: ObjectWaiter(JavaThread* current); @@ -199,6 +200,8 @@ class ObjectMonitor : public CHeapObj { // along with other fields to determine if an ObjectMonitor can be // deflated. It is also used by the async deflation protocol. See // ObjectMonitor::deflate_monitor(). + int64_t _unmounted_vthreads; // Number of nodes in the _entry_list associated with unmounted vthreads. + // It might be temporarily more than the actual number but never less. ObjectWaiter* volatile _wait_set; // LL of threads waiting on the monitor - wait() volatile int _waiters; // number of waiting threads @@ -325,6 +328,9 @@ class ObjectMonitor : public CHeapObj { intx recursions() const { return _recursions; } void set_recursions(size_t recursions); void increment_recursions(JavaThread* current); + void inc_unmounted_vthreads(); + void dec_unmounted_vthreads(); + bool has_unmounted_vthreads() const; // JVM/TI GetObjectMonitorUsage() needs this: int waiters() const; diff --git a/src/hotspot/share/runtime/objectMonitor.inline.hpp b/src/hotspot/share/runtime/objectMonitor.inline.hpp index 52ef904b404..eeb451235dc 100644 --- a/src/hotspot/share/runtime/objectMonitor.inline.hpp +++ b/src/hotspot/share/runtime/objectMonitor.inline.hpp @@ -143,6 +143,21 @@ inline void ObjectMonitor::increment_recursions(JavaThread* current) { _recursions++; } +inline void ObjectMonitor::inc_unmounted_vthreads() { + assert(_unmounted_vthreads >= 0, "invariant"); + AtomicAccess::inc(&_unmounted_vthreads, memory_order_relaxed); +} + +inline void ObjectMonitor::dec_unmounted_vthreads() { + assert(_unmounted_vthreads > 0, "invariant"); + AtomicAccess::dec(&_unmounted_vthreads, memory_order_relaxed); +} + +inline bool ObjectMonitor::has_unmounted_vthreads() const { + assert(_unmounted_vthreads >= 0, "invariant"); + return AtomicAccess::load(&_unmounted_vthreads) > 0; +} + // Clear _owner field; current value must match old_value. inline void ObjectMonitor::release_clear_owner(JavaThread* old_owner) { int64_t old_value = owner_id_from(old_owner); diff --git a/src/hotspot/share/runtime/safefetch.hpp b/src/hotspot/share/runtime/safefetch.hpp index 71a542e25e8..a1781962ec0 100644 --- a/src/hotspot/share/runtime/safefetch.hpp +++ b/src/hotspot/share/runtime/safefetch.hpp @@ -31,8 +31,8 @@ // Safefetch allows to load a value from a location that's not known // to be valid. If the load causes a fault, the error value is returned. -#ifdef _WIN32 - // Windows uses Structured Exception Handling +#if defined(_WIN32) && !defined(_M_ARM64) + // Windows x86_64 uses Structured Exception Handling #include "safefetch_windows.hpp" #elif defined(ZERO) || defined (_AIX) // These platforms implement safefetch via Posix sigsetjmp/longjmp. diff --git a/src/hotspot/share/runtime/safepoint.cpp b/src/hotspot/share/runtime/safepoint.cpp index a1922612bd6..b44a43bbfb1 100644 --- a/src/hotspot/share/runtime/safepoint.cpp +++ b/src/hotspot/share/runtime/safepoint.cpp @@ -30,7 +30,6 @@ #include "gc/shared/collectedHeap.hpp" #include "gc/shared/gcLocker.hpp" #include "gc/shared/oopStorage.hpp" -#include "gc/shared/strongRootsScope.hpp" #include "gc/shared/workerThread.hpp" #include "gc/shared/workerUtils.hpp" #include "interpreter/interpreter.hpp" @@ -162,11 +161,6 @@ bool SafepointSynchronize::thread_not_running(ThreadSafepointState *cur_state) { if (!cur_state->is_running()) { return true; } - LogTarget(Trace, safepoint) lt; - if (lt.is_enabled()) { - LogStream ls(lt); - cur_state->print_on(&ls); - } return false; } @@ -224,6 +218,8 @@ int SafepointSynchronize::synchronize_threads(jlong safepoint_limit_time, int no *initial_running = still_running; + log_trace(safepoint)("%d total threads, waiting for %d threads to block", nof_threads, still_running); + // If there is no thread still running, we are already done. if (still_running <= 0) { assert(tss_head == nullptr, "Must be empty"); @@ -234,6 +230,8 @@ int SafepointSynchronize::synchronize_threads(jlong safepoint_limit_time, int no int64_t start_time = os::javaTimeNanos(); do { + log_trace(safepoint)("Checking thread status"); + // Check if this has taken too long: if (SafepointTimeout && safepoint_limit_time < os::javaTimeNanos()) { print_safepoint_timeout(); @@ -244,12 +242,16 @@ int SafepointSynchronize::synchronize_threads(jlong safepoint_limit_time, int no while (cur_tss != nullptr) { assert(cur_tss->is_running(), "Illegal initial state"); if (thread_not_running(cur_tss)) { + log_trace(safepoint)("Thread " INTPTR_FORMAT " [%d] is now blocked", + p2i(cur_tss->thread()), cur_tss->thread()->osthread()->thread_id()); --still_running; *p_prev = nullptr; ThreadSafepointState *tmp = cur_tss; cur_tss = cur_tss->get_next(); tmp->set_next(nullptr); } else { + log_trace(safepoint)("Thread " INTPTR_FORMAT " [%d] is still running", + p2i(cur_tss->thread()), cur_tss->thread()->osthread()->thread_id()); *p_prev = cur_tss; p_prev = cur_tss->next_ptr(); cur_tss = cur_tss->get_next(); @@ -259,6 +261,7 @@ int SafepointSynchronize::synchronize_threads(jlong safepoint_limit_time, int no DEBUG_ONLY(assert_list_is_valid(tss_head, still_running);) if (still_running > 0) { + log_trace(safepoint)("Waiting for %d threads to block", still_running); back_off(start_time); } @@ -333,10 +336,12 @@ void SafepointSynchronize::begin() { EventSafepointBegin begin_event; SafepointTracing::begin(VMThread::vm_op_type()); + log_trace(safepoint)("Suspending GC threads"); Universe::heap()->safepoint_synchronize_begin(); // By getting the Threads_lock, we assure that no threads are about to start or // exit. It is released again in SafepointSynchronize::end(). + log_trace(safepoint)("Blocking threads from starting/exiting"); Threads_lock->lock(); assert( _state == _not_synchronized, "trying to safepoint synchronize with wrong state"); @@ -345,8 +350,6 @@ void SafepointSynchronize::begin() { _nof_threads_hit_polling_page = 0; - log_debug(safepoint)("Safepoint synchronization initiated using %s wait barrier. (%d threads)", _wait_barrier->description(), nof_threads); - // Reset the count of active JNI critical threads _current_jni_active_count = 0; @@ -365,6 +368,7 @@ void SafepointSynchronize::begin() { int initial_running = 0; // Arms the safepoint, _current_jni_active_count and _waiting_to_block must be set before. + log_trace(safepoint)("Arming safepoint using %s wait barrier", _wait_barrier->description()); arm_safepoint(); // Will spin until all threads are safe. @@ -472,8 +476,10 @@ void SafepointSynchronize::end() { EventSafepointEnd event; assert(Thread::current()->is_VM_thread(), "Only VM thread can execute a safepoint"); + log_trace(safepoint)("Disarming safepoint"); disarm_safepoint(); + log_trace(safepoint)("Resuming GC threads"); Universe::heap()->safepoint_synchronize_end(); SafepointTracing::end(); @@ -552,6 +558,9 @@ void SafepointSynchronize::block(JavaThread *thread) { // Threads shouldn't block if they are in the middle of printing, but... ttyLocker::break_tty_lock_for_safepoint(os::current_thread_id()); + log_trace(safepoint)("Blocking thread " INTPTR_FORMAT " [%d]", + p2i(thread), thread->osthread()->thread_id()); + // Only bail from the block() call if the thread is gone from the // thread list; starting to exit should still block. if (thread->is_terminated()) { @@ -595,6 +604,9 @@ void SafepointSynchronize::block(JavaThread *thread) { // cross_modify_fence is done by SafepointMechanism::process_if_requested // which is the only caller here. + + log_trace(safepoint)("Unblocking thread " INTPTR_FORMAT " [%d]", + p2i(thread), thread->osthread()->thread_id()); } // ------------------------------------------------------------------------------------------------------ @@ -956,6 +968,7 @@ void SafepointTracing::begin(VM_Operation::VMOp_Type type) { _last_safepoint_end_time_ns = 0; RuntimeService::record_safepoint_begin(_last_app_time_ns); + log_debug(safepoint)("Safepoint synchronization initiated"); } void SafepointTracing::synchronized(int nof_threads, int nof_running, int traps) { @@ -964,10 +977,12 @@ void SafepointTracing::synchronized(int nof_threads, int nof_running, int traps) _nof_running = nof_running; _page_trap = traps; RuntimeService::record_safepoint_synchronized(_last_safepoint_sync_time_ns - _last_safepoint_begin_time_ns); + log_debug(safepoint)("Safepoint synchronization complete"); } void SafepointTracing::leave() { _last_safepoint_leave_time_ns = os::javaTimeNanos(); + log_debug(safepoint)("Leaving safepoint"); } void SafepointTracing::end() { @@ -1002,4 +1017,5 @@ void SafepointTracing::end() { ); RuntimeService::record_safepoint_end(_last_safepoint_end_time_ns - _last_safepoint_sync_time_ns); + log_debug(safepoint)("Safepoint complete"); } diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index c3a6e0a4dc3..efc47dd11c6 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -67,6 +67,7 @@ #include "runtime/java.hpp" #include "runtime/javaCalls.hpp" #include "runtime/jniHandles.inline.hpp" +#include "runtime/osThread.hpp" #include "runtime/perfData.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/stackWatermarkSet.hpp" @@ -264,6 +265,46 @@ void SharedRuntime::print_ic_miss_histogram() { tty->print_cr("Total IC misses: %7d", tot_misses); } } + +#ifdef COMPILER2 +// Runtime methods for printf-style debug nodes (same printing format as fieldDescriptor::print_on_for) +void SharedRuntime::debug_print_value(jboolean x) { + tty->print_cr("boolean %d", x); +} + +void SharedRuntime::debug_print_value(jbyte x) { + tty->print_cr("byte %d", x); +} + +void SharedRuntime::debug_print_value(jshort x) { + tty->print_cr("short %d", x); +} + +void SharedRuntime::debug_print_value(jchar x) { + tty->print_cr("char %c %d", isprint(x) ? x : ' ', x); +} + +void SharedRuntime::debug_print_value(jint x) { + tty->print_cr("int %d", x); +} + +void SharedRuntime::debug_print_value(jlong x) { + tty->print_cr("long " JLONG_FORMAT, x); +} + +void SharedRuntime::debug_print_value(jfloat x) { + tty->print_cr("float %f", x); +} + +void SharedRuntime::debug_print_value(jdouble x) { + tty->print_cr("double %lf", x); +} + +void SharedRuntime::debug_print_value(oopDesc* x) { + x->print(); +} +#endif // COMPILER2 + #endif // PRODUCT @@ -525,9 +566,6 @@ address SharedRuntime::raw_exception_handler_for_return_address(JavaThread* curr assert(frame::verify_return_pc(return_address), "must be a return address: " INTPTR_FORMAT, p2i(return_address)); assert(current->frames_to_pop_failed_realloc() == 0 || Interpreter::contains(return_address), "missed frames to pop?"); - // Reset method handle flag. - current->set_is_method_handle_return(false); - #if INCLUDE_JVMCI // JVMCI's ExceptionHandlerStub expects the thread local exception PC to be clear // and other exception handler continuations do not read it @@ -542,8 +580,6 @@ address SharedRuntime::raw_exception_handler_for_return_address(JavaThread* curr CodeBlob* blob = CodeCache::find_blob(return_address); nmethod* nm = (blob != nullptr) ? blob->as_nmethod_or_null() : nullptr; if (nm != nullptr) { - // Set flag if return address is a method handle call site. - current->set_is_method_handle_return(nm->is_method_handle_return(return_address)); // native nmethods don't have exception handlers assert(!nm->is_native_method() || nm->method()->is_continuation_enter_intrinsic(), "no exception handler"); assert(nm->header_begin() != nm->exception_begin(), "no exception handler"); @@ -642,10 +678,13 @@ address SharedRuntime::get_poll_stub(address pc) { "polling page safepoint stub not created yet"); stub = SharedRuntime::polling_page_safepoint_handler_blob()->entry_point(); } - log_debug(safepoint)("... found polling page %s exception at pc = " - INTPTR_FORMAT ", stub =" INTPTR_FORMAT, + log_trace(safepoint)("Polling page exception: thread = " INTPTR_FORMAT " [%d], pc = " + INTPTR_FORMAT " (%s), stub = " INTPTR_FORMAT, + p2i(Thread::current()), + Thread::current()->osthread()->thread_id(), + p2i(pc), at_poll_return ? "return" : "loop", - (intptr_t)pc, (intptr_t)stub); + p2i(stub)); return stub; } @@ -1985,7 +2024,6 @@ void SharedRuntime::monitor_exit_helper(oopDesc* obj, BasicLock* lock, JavaThrea if (!m->try_enter(current, /*check_for_recursion*/ false)) { // Some other thread acquired the lock (or the monitor was // deflated). Either way we are done. - current->dec_held_monitor_count(); return; } } @@ -2007,20 +2045,6 @@ JRT_LEAF(void, SharedRuntime::complete_monitor_unlocking_C(oopDesc* obj, BasicLo SharedRuntime::monitor_exit_helper(obj, lock, current); JRT_END -// This is only called when CheckJNICalls is true, and only -// for virtual thread termination. -JRT_LEAF(void, SharedRuntime::log_jni_monitor_still_held()) - assert(CheckJNICalls, "Only call this when checking JNI usage"); - if (log_is_enabled(Debug, jni)) { - JavaThread* current = JavaThread::current(); - int64_t vthread_id = java_lang_Thread::thread_id(current->vthread()); - int64_t carrier_id = java_lang_Thread::thread_id(current->threadObj()); - log_debug(jni)("VirtualThread (tid: " INT64_FORMAT ", carrier id: " INT64_FORMAT - ") exiting with Objects still locked by JNI MonitorEnter.", - vthread_id, carrier_id); - } -JRT_END - #ifndef PRODUCT void SharedRuntime::print_statistics() { @@ -2520,6 +2544,7 @@ ArchivedAdapterTable AdapterHandlerLibrary::_aot_adapter_handler_table; #endif // INCLUDE_CDS static const int AdapterHandlerLibrary_size = 16*K; BufferBlob* AdapterHandlerLibrary::_buffer = nullptr; +volatile uint AdapterHandlerLibrary::_id_counter = 0; BufferBlob* AdapterHandlerLibrary::buffer_blob() { assert(_buffer != nullptr, "should be initialized"); @@ -2600,7 +2625,9 @@ void AdapterHandlerLibrary::initialize() { } AdapterHandlerEntry* AdapterHandlerLibrary::new_entry(AdapterFingerPrint* fingerprint) { - return AdapterHandlerEntry::allocate(fingerprint); + uint id = (uint)AtomicAccess::add((int*)&_id_counter, 1); + assert(id > 0, "we can never overflow because AOT cache cannot contain more than 2^32 methods"); + return AdapterHandlerEntry::allocate(id, fingerprint); } AdapterHandlerEntry* AdapterHandlerLibrary::get_simple_adapter(const methodHandle& method) { @@ -2754,8 +2781,8 @@ AdapterHandlerEntry* AdapterHandlerLibrary::get_adapter(const methodHandle& meth void AdapterHandlerLibrary::lookup_aot_cache(AdapterHandlerEntry* handler) { ResourceMark rm; - const char* name = AdapterHandlerLibrary::name(handler->fingerprint()); - const uint32_t id = AdapterHandlerLibrary::id(handler->fingerprint()); + const char* name = AdapterHandlerLibrary::name(handler); + const uint32_t id = AdapterHandlerLibrary::id(handler); CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::Adapter, id, name); if (blob != nullptr) { @@ -2850,8 +2877,8 @@ bool AdapterHandlerLibrary::generate_adapter_code(AdapterHandlerEntry* handler, handler->set_adapter_blob(adapter_blob); if (!is_transient && AOTCodeCache::is_dumping_adapter()) { // try to save generated code - const char* name = AdapterHandlerLibrary::name(handler->fingerprint()); - const uint32_t id = AdapterHandlerLibrary::id(handler->fingerprint()); + const char* name = AdapterHandlerLibrary::name(handler); + const uint32_t id = AdapterHandlerLibrary::id(handler); bool success = AOTCodeCache::store_code_blob(*adapter_blob, AOTCodeEntry::Adapter, id, name); assert(success || !AOTCodeCache::is_dumping_adapter(), "caching of adapter must be disabled"); } @@ -2991,11 +3018,22 @@ void AdapterHandlerEntry::link() { } void AdapterHandlerLibrary::link_aot_adapters() { + uint max_id = 0; assert(AOTCodeCache::is_using_adapter(), "AOT adapters code should be available"); - _aot_adapter_handler_table.iterate([](AdapterHandlerEntry* entry) { + /* It is possible that some adapters generated in assembly phase are not stored in the cache. + * That implies adapter ids of the adapters in the cache may not be contiguous. + * If the size of the _aot_adapter_handler_table is used to initialize _id_counter, then it may + * result in collision of adapter ids between AOT stored handlers and runtime generated handlers. + * To avoid such situation, initialize the _id_counter with the largest adapter id among the AOT stored handlers. + */ + _aot_adapter_handler_table.iterate([&](AdapterHandlerEntry* entry) { assert(!entry->is_linked(), "AdapterHandlerEntry is already linked!"); entry->link(); + max_id = MAX2(max_id, entry->id()); }); + // Set adapter id to the maximum id found in the AOTCache + assert(_id_counter == 0, "Did not expect new AdapterHandlerEntry to be created at this stage"); + _id_counter = max_id; } // This method is called during production run to lookup simple adapters @@ -3360,13 +3398,12 @@ bool AdapterHandlerLibrary::contains(const CodeBlob* b) { return found; } -const char* AdapterHandlerLibrary::name(AdapterFingerPrint* fingerprint) { - return fingerprint->as_basic_args_string(); +const char* AdapterHandlerLibrary::name(AdapterHandlerEntry* handler) { + return handler->fingerprint()->as_basic_args_string(); } -uint32_t AdapterHandlerLibrary::id(AdapterFingerPrint* fingerprint) { - unsigned int hash = fingerprint->compute_hash(); - return hash; +uint32_t AdapterHandlerLibrary::id(AdapterHandlerEntry* handler) { + return handler->id(); } void AdapterHandlerLibrary::print_handler_on(outputStream* st, const CodeBlob* b) { diff --git a/src/hotspot/share/runtime/sharedRuntime.hpp b/src/hotspot/share/runtime/sharedRuntime.hpp index 374985ad921..93cd92b3a32 100644 --- a/src/hotspot/share/runtime/sharedRuntime.hpp +++ b/src/hotspot/share/runtime/sharedRuntime.hpp @@ -32,6 +32,7 @@ #include "memory/allStatic.hpp" #include "memory/metaspaceClosure.hpp" #include "memory/resourceArea.hpp" +#include "runtime/safepointVerifiers.hpp" #include "runtime/stubInfo.hpp" #include "utilities/macros.hpp" @@ -404,9 +405,6 @@ class SharedRuntime: AllStatic { static void monitor_exit_helper(oopDesc* obj, BasicLock* lock, JavaThread* current); - // Issue UL warning for unlocked JNI monitor on virtual thread termination - static void log_jni_monitor_still_held(); - private: static Handle find_callee_info(Bytecodes::Code& bc, CallInfo& callinfo, TRAPS); static Handle find_callee_info_helper(vframeStream& vfst, Bytecodes::Code& bc, CallInfo& callinfo, TRAPS); @@ -638,6 +636,39 @@ class SharedRuntime: AllStatic { static void print_call_statistics(uint64_t comp_total); static void print_ic_miss_histogram(); +#ifdef COMPILER2 + // Runtime methods for printf-style debug nodes + static void debug_print_value(jboolean x); + static void debug_print_value(jbyte x); + static void debug_print_value(jshort x); + static void debug_print_value(jchar x); + static void debug_print_value(jint x); + static void debug_print_value(jlong x); + static void debug_print_value(jfloat x); + static void debug_print_value(jdouble x); + static void debug_print_value(oopDesc* x); + + template + static void debug_print_rec(T arg, Rest... args) { + debug_print_value(arg); + debug_print_rec(args...); + } + + static void debug_print_rec() {} + + // template is required here as we need to know the exact signature at compile-time + template + static void debug_print(const char *str, TT... args) { + // these three lines are the manual expansion of JRT_LEAF ... JRT_END, does not work well with templates + DEBUG_ONLY(NoHandleMark __hm;) + os::verify_stack_alignment(); + DEBUG_ONLY(NoSafepointVerifier __nsv;) + + tty->print_cr("%s", str); + debug_print_rec(args...); + } +#endif // COMPILER2 + #endif // PRODUCT static void print_statistics() PRODUCT_RETURN; @@ -686,6 +717,7 @@ class AdapterHandlerEntry : public MetaspaceObj { private: AdapterFingerPrint* _fingerprint; AdapterBlob* _adapter_blob; + uint _id; bool _linked; static const char *_entry_names[]; @@ -697,9 +729,10 @@ class AdapterHandlerEntry : public MetaspaceObj { int _saved_code_length; #endif - AdapterHandlerEntry(AdapterFingerPrint* fingerprint) : + AdapterHandlerEntry(int id, AdapterFingerPrint* fingerprint) : _fingerprint(fingerprint), _adapter_blob(nullptr), + _id(id), _linked(false) #ifdef ASSERT , _saved_code(nullptr), @@ -720,8 +753,8 @@ class AdapterHandlerEntry : public MetaspaceObj { } public: - static AdapterHandlerEntry* allocate(AdapterFingerPrint* fingerprint) { - return new(0) AdapterHandlerEntry(fingerprint); + static AdapterHandlerEntry* allocate(uint id, AdapterFingerPrint* fingerprint) { + return new(0) AdapterHandlerEntry(id, fingerprint); } static void deallocate(AdapterHandlerEntry *handler) { @@ -772,6 +805,7 @@ class AdapterHandlerEntry : public MetaspaceObj { AdapterBlob* adapter_blob() const { return _adapter_blob; } bool is_linked() const { return _linked; } + uint id() const { return _id; } AdapterFingerPrint* fingerprint() const { return _fingerprint; } #ifdef ASSERT @@ -798,6 +832,7 @@ class ArchivedAdapterTable; class AdapterHandlerLibrary: public AllStatic { friend class SharedRuntime; private: + static volatile uint _id_counter; // counter for generating unique adapter ids, range = [1,UINT_MAX] static BufferBlob* _buffer; // the temporary code buffer in CodeCache static AdapterHandlerEntry* _no_arg_handler; static AdapterHandlerEntry* _int_arg_handler; @@ -837,8 +872,8 @@ class AdapterHandlerLibrary: public AllStatic { static void print_handler(const CodeBlob* b) { print_handler_on(tty, b); } static void print_handler_on(outputStream* st, const CodeBlob* b); static bool contains(const CodeBlob* b); - static const char* name(AdapterFingerPrint* fingerprint); - static uint32_t id(AdapterFingerPrint* fingerprint); + static const char* name(AdapterHandlerEntry* handler); + static uint32_t id(AdapterHandlerEntry* handler); #ifndef PRODUCT static void print_statistics(); #endif // PRODUCT diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index ff4e09e741f..e513c57fe06 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -452,7 +452,6 @@ void ObjectSynchronizer::jni_enter(Handle obj, JavaThread* current) { while (true) { BasicLock lock; if (LightweightSynchronizer::inflate_and_enter(obj(), &lock, inflate_cause_jni_enter, current, current) != nullptr) { - current->inc_held_monitor_count(1, true); break; } } @@ -470,7 +469,6 @@ void ObjectSynchronizer::jni_exit(oop obj, TRAPS) { // monitor even if an exception was already pending. if (monitor->check_owner(THREAD)) { monitor->exit(current); - current->dec_held_monitor_count(1, true); } } @@ -1263,8 +1261,7 @@ class ReleaseJavaMonitorsClosure: public MonitorClosure { public: ReleaseJavaMonitorsClosure(JavaThread* thread) : _thread(thread) {} void do_monitor(ObjectMonitor* mid) { - intx rec = mid->complete_exit(_thread); - _thread->dec_held_monitor_count(rec + 1); + mid->complete_exit(_thread); } }; @@ -1290,9 +1287,6 @@ void ObjectSynchronizer::release_monitors_owned_by_thread(JavaThread* current) { ObjectSynchronizer::owned_monitors_iterate(&rjmc, current); assert(!current->has_pending_exception(), "Should not be possible"); current->clear_pending_exception(); - assert(current->held_monitor_count() == 0, "Should not be possible"); - // All monitors (including entered via JNI) have been unlocked above, so we need to clear jni count. - current->clear_jni_monitor_count(); } const char* ObjectSynchronizer::inflate_cause_name(const InflateCause cause) { diff --git a/src/hotspot/share/runtime/synchronizer.inline.hpp b/src/hotspot/share/runtime/synchronizer.inline.hpp index 6a850e5c8ca..cdbeb1daf5b 100644 --- a/src/hotspot/share/runtime/synchronizer.inline.hpp +++ b/src/hotspot/share/runtime/synchronizer.inline.hpp @@ -61,8 +61,6 @@ inline bool ObjectSynchronizer::quick_enter(oop obj, BasicLock* lock, JavaThread } inline void ObjectSynchronizer::exit(oop object, BasicLock* lock, JavaThread* current) { - current->dec_held_monitor_count(); - LightweightSynchronizer::exit(object, lock, current); } diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index 32c99320bdf..673f3b089a5 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -74,7 +74,7 @@ Thread::Thread(MemTag mem_tag) { set_osthread(nullptr); set_resource_area(new (mem_tag) ResourceArea(mem_tag)); DEBUG_ONLY(_current_resource_mark = nullptr;) - set_handle_area(new (mem_tag) HandleArea(mem_tag, nullptr)); + set_handle_area(new (mem_tag) HandleArea(mem_tag)); set_metadata_handles(new (mtClass) GrowableArray(30, mtClass)); set_last_handle_mark(nullptr); diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp index d098343c354..ffe1a86cda5 100644 --- a/src/hotspot/share/runtime/threads.cpp +++ b/src/hotspot/share/runtime/threads.cpp @@ -446,6 +446,9 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { // Check version if (!is_supported_jni_version(args->version)) return JNI_EVERSION; + // Deferred "static" initialization + NonJavaThread::init(); + // Initialize library-based TLS ThreadLocalStorage::init(); diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 5a3850feac8..dee0a5d4eb7 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -536,7 +536,6 @@ nonstatic_field(nmethod, _state, volatile signed char) \ nonstatic_field(nmethod, _exception_offset, int) \ nonstatic_field(nmethod, _deopt_handler_offset, int) \ - nonstatic_field(nmethod, _deopt_mh_handler_offset, int) \ nonstatic_field(nmethod, _orig_pc_offset, int) \ nonstatic_field(nmethod, _stub_offset, int) \ nonstatic_field(nmethod, _scopes_pcs_offset, int) \ @@ -608,7 +607,6 @@ volatile_nonstatic_field(JavaThread, _suspend_flags, uint32_t) \ volatile_nonstatic_field(JavaThread, _exception_oop, oop) \ volatile_nonstatic_field(JavaThread, _exception_pc, address) \ - volatile_nonstatic_field(JavaThread, _is_method_handle_return, int) \ nonstatic_field(JavaThread, _saved_exception_pc, address) \ volatile_nonstatic_field(JavaThread, _thread_state, JavaThreadState) \ nonstatic_field(JavaThread, _stack_base, address) \ @@ -1711,7 +1709,6 @@ /**********************/ \ \ declare_constant(PcDesc::PCDESC_reexecute) \ - declare_constant(PcDesc::PCDESC_is_method_handle_invoke) \ declare_constant(PcDesc::PCDESC_return_oop) \ \ /**********************/ \ diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 0d6e3bb2d76..51ea80a0150 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -1253,6 +1253,24 @@ JAVA_INTEGER_SHIFT_OP(>>, java_shift_right_unsigned, jlong, julong) #undef JAVA_INTEGER_SHIFT_OP +// Some convenient bit shift operations that accepts a BasicType as the last +// argument. These avoid potential mistakes with overloaded functions only +// distinguished by lhs argument type. +#define JAVA_INTEGER_SHIFT_BASIC_TYPE(FUNC) \ +inline jlong FUNC(jlong lhs, jint rhs, BasicType bt) { \ + if (bt == T_INT) { \ + return FUNC((jint) lhs, rhs); \ + } \ + assert(bt == T_LONG, "unsupported basic type"); \ + return FUNC(lhs, rhs); \ +} + +JAVA_INTEGER_SHIFT_BASIC_TYPE(java_shift_left) +JAVA_INTEGER_SHIFT_BASIC_TYPE(java_shift_right) +JAVA_INTEGER_SHIFT_BASIC_TYPE(java_shift_right_unsigned) + +#undef JAVA_INTERGER_SHIFT_BASIC_TYPE + //---------------------------------------------------------------------------------------------------- // The goal of this code is to provide saturating operations for int/uint. // Checks overflow conditions and saturates the result to min_jint/max_jint. diff --git a/src/hotspot/share/utilities/ticks.cpp b/src/hotspot/share/utilities/ticks.cpp index ef8b7362c9e..0d948cf1a0e 100644 --- a/src/hotspot/share/utilities/ticks.cpp +++ b/src/hotspot/share/utilities/ticks.cpp @@ -61,7 +61,7 @@ uint64_t ElapsedCounterSource::nanoseconds(Type value) { uint64_t FastUnorderedElapsedCounterSource::frequency() { #if defined(X86) && !defined(ZERO) - static bool valid_rdtsc = Rdtsc::initialize(); + static bool valid_rdtsc = Rdtsc::enabled(); if (valid_rdtsc) { static const uint64_t freq = (uint64_t)Rdtsc::frequency(); return freq; @@ -73,7 +73,7 @@ uint64_t FastUnorderedElapsedCounterSource::frequency() { FastUnorderedElapsedCounterSource::Type FastUnorderedElapsedCounterSource::now() { #if defined(X86) && !defined(ZERO) - static bool valid_rdtsc = Rdtsc::initialize(); + static bool valid_rdtsc = Rdtsc::enabled(); if (valid_rdtsc) { return Rdtsc::elapsed_counter(); } @@ -105,12 +105,7 @@ CompositeElapsedCounterSource::Type CompositeElapsedCounterSource::now() { CompositeTime ct; ct.val1 = ElapsedCounterSource::now(); #if defined(X86) && !defined(ZERO) - static bool initialized = false; - static bool valid_rdtsc = false; - if (!initialized) { - valid_rdtsc = Rdtsc::initialize(); - initialized = true; - } + static bool valid_rdtsc = Rdtsc::enabled(); if (valid_rdtsc) { ct.val2 = Rdtsc::elapsed_counter(); } diff --git a/src/java.base/macosx/classes/jdk/internal/ffi/generated/errno/errno_h$shared.java b/src/java.base/macosx/classes/jdk/internal/ffi/generated/errno/errno_h$shared.java new file mode 100644 index 00000000000..dbb8f91ad92 --- /dev/null +++ b/src/java.base/macosx/classes/jdk/internal/ffi/generated/errno/errno_h$shared.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// Generated by jextract + +package jdk.internal.ffi.generated.errno; + +import java.lang.foreign.*; +import java.lang.invoke.*; +import java.util.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; + +@SuppressWarnings("restricted") +public class errno_h$shared { + + errno_h$shared() { + // Should not be called directly + } + + public static final OfBoolean C_BOOL = (OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool"); + public static final OfByte C_CHAR =(OfByte)Linker.nativeLinker().canonicalLayouts().get("char"); + public static final OfShort C_SHORT = (OfShort) Linker.nativeLinker().canonicalLayouts().get("short"); + public static final OfInt C_INT = (OfInt) Linker.nativeLinker().canonicalLayouts().get("int"); + public static final OfLong C_LONG_LONG = (OfLong) Linker.nativeLinker().canonicalLayouts().get("long long"); + public static final OfFloat C_FLOAT = (OfFloat) Linker.nativeLinker().canonicalLayouts().get("float"); + public static final OfDouble C_DOUBLE = (OfDouble) Linker.nativeLinker().canonicalLayouts().get("double"); + public static final AddressLayout C_POINTER = ((AddressLayout) Linker.nativeLinker().canonicalLayouts().get("void*")) + .withTargetLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, C_CHAR)); + public static final OfLong C_LONG = (OfLong) Linker.nativeLinker().canonicalLayouts().get("long"); + + static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + + static void traceDowncall(String name, Object... args) { + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("%s(%s)\n", name, traceArgs); + } + + static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { + try { + return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + static MemoryLayout align(MemoryLayout layout, long align) { + return switch (layout) { + case PaddingLayout p -> p; + case ValueLayout v -> v.withByteAlignment(align); + case GroupLayout g -> { + MemoryLayout[] alignedMembers = g.memberLayouts().stream() + .map(m -> align(m, align)).toArray(MemoryLayout[]::new); + yield g instanceof StructLayout ? + MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); + } + case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); + }; + } +} diff --git a/src/java.base/macosx/classes/jdk/internal/ffi/generated/errno/errno_h.java b/src/java.base/macosx/classes/jdk/internal/ffi/generated/errno/errno_h.java new file mode 100644 index 00000000000..7fdc152e913 --- /dev/null +++ b/src/java.base/macosx/classes/jdk/internal/ffi/generated/errno/errno_h.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// Generated by jextract + +package jdk.internal.ffi.generated.errno; + +import java.lang.foreign.*; +import java.lang.invoke.*; + +@SuppressWarnings("restricted") +public class errno_h extends errno_h$shared { + + errno_h() { + // Should not be called directly + } + + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + + static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup() + .or(Linker.nativeLinker().defaultLookup()); + + private static final int EINTR = (int)4L; + /** + * {@snippet lang=c : + * #define EINTR 4 + * } + */ + public static int EINTR() { + return EINTR; + } + + private static class strerror_r { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + C_INT, + C_INT, + C_POINTER, + C_LONG + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("strerror_r"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * int strerror_r(int __errnum, char *__strerrbuf, size_t __buflen) + * } + */ + public static FunctionDescriptor strerror_r$descriptor() { + return strerror_r.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * int strerror_r(int __errnum, char *__strerrbuf, size_t __buflen) + * } + */ + public static MethodHandle strerror_r$handle() { + return strerror_r.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * int strerror_r(int __errnum, char *__strerrbuf, size_t __buflen) + * } + */ + public static MemorySegment strerror_r$address() { + return strerror_r.ADDR; + } + + /** + * {@snippet lang=c : + * int strerror_r(int __errnum, char *__strerrbuf, size_t __buflen) + * } + */ + public static int strerror_r(int __errnum, MemorySegment __strerrbuf, long __buflen) { + var mh$ = strerror_r.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("strerror_r", __errnum, __strerrbuf, __buflen); + } + return (int)mh$.invokeExact(__errnum, __strerrbuf, __buflen); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } +} diff --git a/src/hotspot/share/gc/shared/strongRootsScope.cpp b/src/java.base/macosx/classes/jdk/internal/ffi/generated/errno/package-info.java similarity index 52% rename from src/hotspot/share/gc/shared/strongRootsScope.cpp rename to src/java.base/macosx/classes/jdk/internal/ffi/generated/errno/package-info.java index 1316df68e5f..2a29f198b2a 100644 --- a/src/hotspot/share/gc/shared/strongRootsScope.cpp +++ b/src/java.base/macosx/classes/jdk/internal/ffi/generated/errno/package-info.java @@ -1,10 +1,12 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or @@ -19,33 +21,23 @@ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. + */ + +/** + * Defines native structures for errno APIs. + * Generated with the following jextract command: + * {@snippet lang = "Shell Script": + * + * HEADER_NAME=errno.h + * echo "#include " > $HEADER_NAME + * echo "#include " >> $HEADER_NAME + * + * jextract --target-package jdk.internal.ffi.generated.errno \ + * --include-constant EINTR \ + * --include-function strerror_r \ + * $HEADER_NAME + * } * */ -#include "classfile/stringTable.hpp" -#include "code/nmethod.hpp" -#include "gc/shared/strongRootsScope.hpp" -#include "runtime/threads.hpp" - -MarkScope::MarkScope() { - nmethod::oops_do_marking_prologue(); -} - -MarkScope::~MarkScope() { - nmethod::oops_do_marking_epilogue(); -} - -StrongRootsScope::StrongRootsScope(uint n_threads) : _n_threads(n_threads) { - // No need for thread claim for statically-known sequential case (_n_threads == 0) - // For positive values, clients of this class often unify sequential/parallel - // cases, so they expect the thread claim token to be updated. - if (_n_threads != 0) { - Threads::change_thread_claim_token(); - } -} - -StrongRootsScope::~StrongRootsScope() { - if (_n_threads != 0) { - Threads::assert_all_threads_claimed(); - } -} +package jdk.internal.ffi.generated.errno; diff --git a/src/java.base/macosx/classes/jdk/internal/ffi/generated/kqueue/kevent.java b/src/java.base/macosx/classes/jdk/internal/ffi/generated/kqueue/kevent.java new file mode 100644 index 00000000000..22bbf57bcfa --- /dev/null +++ b/src/java.base/macosx/classes/jdk/internal/ffi/generated/kqueue/kevent.java @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// Generated by jextract + +package jdk.internal.ffi.generated.kqueue; + +import java.lang.foreign.*; +import java.util.function.*; + +import static java.lang.foreign.MemoryLayout.PathElement.*; +import static java.lang.foreign.ValueLayout.*; + +/** + * {@snippet lang=c : + * struct kevent { + * uintptr_t ident; + * int16_t filter; + * uint16_t flags; + * uint32_t fflags; + * intptr_t data; + * void *udata; + * } + * } + */ +@SuppressWarnings("restricted") +public class kevent { + + kevent() { + // Should not be called directly + } + + private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( + kqueue_h.align(kqueue_h.C_LONG, 4).withName("ident"), + kqueue_h.C_SHORT.withName("filter"), + kqueue_h.C_SHORT.withName("flags"), + kqueue_h.C_INT.withName("fflags"), + kqueue_h.align(kqueue_h.C_LONG, 4).withName("data"), + kqueue_h.align(kqueue_h.C_POINTER, 4).withName("udata") + ).withName("kevent"); + + /** + * The layout of this struct + */ + public static final GroupLayout layout() { + return $LAYOUT; + } + + private static final OfLong ident$LAYOUT = (OfLong)$LAYOUT.select(groupElement("ident")); + + /** + * Layout for field: + * {@snippet lang=c : + * uintptr_t ident + * } + */ + public static final OfLong ident$layout() { + return ident$LAYOUT; + } + + private static final long ident$OFFSET = $LAYOUT.byteOffset(groupElement("ident")); + + /** + * Offset for field: + * {@snippet lang=c : + * uintptr_t ident + * } + */ + public static final long ident$offset() { + return ident$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * uintptr_t ident + * } + */ + public static long ident(MemorySegment struct) { + return struct.get(ident$LAYOUT, ident$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * uintptr_t ident + * } + */ + public static void ident(MemorySegment struct, long fieldValue) { + struct.set(ident$LAYOUT, ident$OFFSET, fieldValue); + } + + private static final OfShort filter$LAYOUT = (OfShort)$LAYOUT.select(groupElement("filter")); + + /** + * Layout for field: + * {@snippet lang=c : + * int16_t filter + * } + */ + public static final OfShort filter$layout() { + return filter$LAYOUT; + } + + private static final long filter$OFFSET = $LAYOUT.byteOffset(groupElement("filter")); + + /** + * Offset for field: + * {@snippet lang=c : + * int16_t filter + * } + */ + public static final long filter$offset() { + return filter$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * int16_t filter + * } + */ + public static short filter(MemorySegment struct) { + return struct.get(filter$LAYOUT, filter$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * int16_t filter + * } + */ + public static void filter(MemorySegment struct, short fieldValue) { + struct.set(filter$LAYOUT, filter$OFFSET, fieldValue); + } + + private static final OfShort flags$LAYOUT = (OfShort)$LAYOUT.select(groupElement("flags")); + + /** + * Layout for field: + * {@snippet lang=c : + * uint16_t flags + * } + */ + public static final OfShort flags$layout() { + return flags$LAYOUT; + } + + private static final long flags$OFFSET = $LAYOUT.byteOffset(groupElement("flags")); + + /** + * Offset for field: + * {@snippet lang=c : + * uint16_t flags + * } + */ + public static final long flags$offset() { + return flags$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * uint16_t flags + * } + */ + public static short flags(MemorySegment struct) { + return struct.get(flags$LAYOUT, flags$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * uint16_t flags + * } + */ + public static void flags(MemorySegment struct, short fieldValue) { + struct.set(flags$LAYOUT, flags$OFFSET, fieldValue); + } + + private static final OfInt fflags$LAYOUT = (OfInt)$LAYOUT.select(groupElement("fflags")); + + /** + * Layout for field: + * {@snippet lang=c : + * uint32_t fflags + * } + */ + public static final OfInt fflags$layout() { + return fflags$LAYOUT; + } + + private static final long fflags$OFFSET = $LAYOUT.byteOffset(groupElement("fflags")); + + /** + * Offset for field: + * {@snippet lang=c : + * uint32_t fflags + * } + */ + public static final long fflags$offset() { + return fflags$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * uint32_t fflags + * } + */ + public static int fflags(MemorySegment struct) { + return struct.get(fflags$LAYOUT, fflags$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * uint32_t fflags + * } + */ + public static void fflags(MemorySegment struct, int fieldValue) { + struct.set(fflags$LAYOUT, fflags$OFFSET, fieldValue); + } + + private static final OfLong data$LAYOUT = (OfLong)$LAYOUT.select(groupElement("data")); + + /** + * Layout for field: + * {@snippet lang=c : + * intptr_t data + * } + */ + public static final OfLong data$layout() { + return data$LAYOUT; + } + + private static final long data$OFFSET = $LAYOUT.byteOffset(groupElement("data")); + + /** + * Offset for field: + * {@snippet lang=c : + * intptr_t data + * } + */ + public static final long data$offset() { + return data$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * intptr_t data + * } + */ + public static long data(MemorySegment struct) { + return struct.get(data$LAYOUT, data$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * intptr_t data + * } + */ + public static void data(MemorySegment struct, long fieldValue) { + struct.set(data$LAYOUT, data$OFFSET, fieldValue); + } + + private static final AddressLayout udata$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("udata")); + + /** + * Layout for field: + * {@snippet lang=c : + * void *udata + * } + */ + public static final AddressLayout udata$layout() { + return udata$LAYOUT; + } + + private static final long udata$OFFSET = $LAYOUT.byteOffset(groupElement("udata")); + + /** + * Offset for field: + * {@snippet lang=c : + * void *udata + * } + */ + public static final long udata$offset() { + return udata$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * void *udata + * } + */ + public static MemorySegment udata(MemorySegment struct) { + return struct.get(udata$LAYOUT, udata$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * void *udata + * } + */ + public static void udata(MemorySegment struct, MemorySegment fieldValue) { + struct.set(udata$LAYOUT, udata$OFFSET, fieldValue); + } + + /** + * Obtains a slice of {@code arrayParam} which selects the array element at {@code index}. + * The returned segment has address {@code arrayParam.address() + index * layout().byteSize()} + */ + public static MemorySegment asSlice(MemorySegment array, long index) { + return array.asSlice(layout().byteSize() * index); + } + + /** + * The size (in bytes) of this struct + */ + public static long sizeof() { return layout().byteSize(); } + + /** + * Allocate a segment of size {@code layout().byteSize()} using {@code allocator} + */ + public static MemorySegment allocate(SegmentAllocator allocator) { + return allocator.allocate(layout()); + } + + /** + * Allocate an array of size {@code elementCount} using {@code allocator}. + * The returned segment has size {@code elementCount * layout().byteSize()}. + */ + public static MemorySegment allocateArray(long elementCount, SegmentAllocator allocator) { + return allocator.allocate(MemoryLayout.sequenceLayout(elementCount, layout())); + } + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, Arena arena, Consumer cleanup) { + return reinterpret(addr, 1, arena, cleanup); + } + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code elementCount * layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, long elementCount, Arena arena, Consumer cleanup) { + return addr.reinterpret(layout().byteSize() * elementCount, arena, cleanup); + } +} diff --git a/src/java.base/macosx/classes/jdk/internal/ffi/generated/kqueue/kqueue_h$shared.java b/src/java.base/macosx/classes/jdk/internal/ffi/generated/kqueue/kqueue_h$shared.java new file mode 100644 index 00000000000..662916d4e8d --- /dev/null +++ b/src/java.base/macosx/classes/jdk/internal/ffi/generated/kqueue/kqueue_h$shared.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// Generated by jextract + +package jdk.internal.ffi.generated.kqueue; + +import java.lang.foreign.*; +import java.lang.invoke.*; +import java.util.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; + +@SuppressWarnings("restricted") +public class kqueue_h$shared { + + kqueue_h$shared() { + // Should not be called directly + } + + public static final OfBoolean C_BOOL = (OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool"); + public static final OfByte C_CHAR =(OfByte)Linker.nativeLinker().canonicalLayouts().get("char"); + public static final OfShort C_SHORT = (OfShort) Linker.nativeLinker().canonicalLayouts().get("short"); + public static final OfInt C_INT = (OfInt) Linker.nativeLinker().canonicalLayouts().get("int"); + public static final OfLong C_LONG_LONG = (OfLong) Linker.nativeLinker().canonicalLayouts().get("long long"); + public static final OfFloat C_FLOAT = (OfFloat) Linker.nativeLinker().canonicalLayouts().get("float"); + public static final OfDouble C_DOUBLE = (OfDouble) Linker.nativeLinker().canonicalLayouts().get("double"); + public static final AddressLayout C_POINTER = ((AddressLayout) Linker.nativeLinker().canonicalLayouts().get("void*")) + .withTargetLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, C_CHAR)); + public static final OfLong C_LONG = (OfLong) Linker.nativeLinker().canonicalLayouts().get("long"); + + static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + + static void traceDowncall(String name, Object... args) { + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("%s(%s)\n", name, traceArgs); + } + + static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { + try { + return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + static MemoryLayout align(MemoryLayout layout, long align) { + return switch (layout) { + case PaddingLayout p -> p; + case ValueLayout v -> v.withByteAlignment(align); + case GroupLayout g -> { + MemoryLayout[] alignedMembers = g.memberLayouts().stream() + .map(m -> align(m, align)).toArray(MemoryLayout[]::new); + yield g instanceof StructLayout ? + MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); + } + case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); + }; + } +} diff --git a/src/java.base/macosx/classes/jdk/internal/ffi/generated/kqueue/kqueue_h.java b/src/java.base/macosx/classes/jdk/internal/ffi/generated/kqueue/kqueue_h.java new file mode 100644 index 00000000000..2ba04f53ea8 --- /dev/null +++ b/src/java.base/macosx/classes/jdk/internal/ffi/generated/kqueue/kqueue_h.java @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// Generated by jextract + +package jdk.internal.ffi.generated.kqueue; + +import java.lang.foreign.*; +import java.lang.invoke.*; + +@SuppressWarnings("restricted") +public class kqueue_h extends kqueue_h$shared { + + kqueue_h() { + // Should not be called directly + } + + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + + static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup() + .or(Linker.nativeLinker().defaultLookup()); + + private static final int EV_ADD = (int)1L; + /** + * {@snippet lang=c : + * #define EV_ADD 1 + * } + */ + public static int EV_ADD() { + return EV_ADD; + } + private static final int EV_DELETE = (int)2L; + /** + * {@snippet lang=c : + * #define EV_DELETE 2 + * } + */ + public static int EV_DELETE() { + return EV_DELETE; + } + private static final int EV_ONESHOT = (int)16L; + /** + * {@snippet lang=c : + * #define EV_ONESHOT 16 + * } + */ + public static int EV_ONESHOT() { + return EV_ONESHOT; + } + private static final int EV_CLEAR = (int)32L; + /** + * {@snippet lang=c : + * #define EV_CLEAR 32 + * } + */ + public static int EV_CLEAR() { + return EV_CLEAR; + } + + private static class kqueue { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + kqueue_h.C_INT ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("kqueue"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * int kqueue() + * } + */ + public static FunctionDescriptor kqueue$descriptor() { + return kqueue.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * int kqueue() + * } + */ + public static MethodHandle kqueue$handle() { + return kqueue.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * int kqueue() + * } + */ + public static MemorySegment kqueue$address() { + return kqueue.ADDR; + } + + /** + * {@snippet lang=c : + * int kqueue() + * } + */ + public static int kqueue() { + var mh$ = kqueue.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("kqueue"); + } + return (int)mh$.invokeExact(); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + private static class kevent { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + kqueue_h.C_INT, + kqueue_h.C_INT, + kqueue_h.C_POINTER, + kqueue_h.C_INT, + kqueue_h.C_POINTER, + kqueue_h.C_INT, + kqueue_h.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("kevent"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * int kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout) + * } + */ + public static FunctionDescriptor kevent$descriptor() { + return kevent.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * int kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout) + * } + */ + public static MethodHandle kevent$handle() { + return kevent.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * int kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout) + * } + */ + public static MemorySegment kevent$address() { + return kevent.ADDR; + } + + /** + * {@snippet lang=c : + * int kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout) + * } + */ + public static int kevent(int kq, MemorySegment changelist, int nchanges, MemorySegment eventlist, int nevents, MemorySegment timeout) { + var mh$ = kevent.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("kevent", kq, changelist, nchanges, eventlist, nevents, timeout); + } + return (int)mh$.invokeExact(kq, changelist, nchanges, eventlist, nevents, timeout); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + private static final int EVFILT_READ = (int)-1L; + /** + * {@snippet lang=c : + * #define EVFILT_READ -1 + * } + */ + public static int EVFILT_READ() { + return EVFILT_READ; + } + private static final int EVFILT_WRITE = (int)-2L; + /** + * {@snippet lang=c : + * #define EVFILT_WRITE -2 + * } + */ + public static int EVFILT_WRITE() { + return EVFILT_WRITE; + } +} diff --git a/src/java.base/macosx/classes/jdk/internal/ffi/generated/kqueue/package-info.java b/src/java.base/macosx/classes/jdk/internal/ffi/generated/kqueue/package-info.java new file mode 100644 index 00000000000..ce372c87b74 --- /dev/null +++ b/src/java.base/macosx/classes/jdk/internal/ffi/generated/kqueue/package-info.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Defines native structures for Kqueue socket APIs. + * Generated with the following jextract command: + * {@snippet lang = "Shell Script": + * + * HEADER_NAME=kqueue.h + * echo "#include " > $HEADER_NAME + * echo "#include " >> $HEADER_NAME + * + * + * jextract --target-package jdk.internal.ffi.generated.kqueue \ + * --include-constant EV_CLEAR \ + * --include-constant EV_ONESHOT \ + * --include-constant EV_DELETE \ + * --include-constant EV_ADD \ + * --include-constant EVFILT_WRITE \ + * --include-constant EVFILT_READ \ + * --include-function kevent \ + * --include-function kqueue \ + * --include-struct kevent \ + * $HEADER_NAME + * } + * + */ + +package jdk.internal.ffi.generated.kqueue; diff --git a/test/jdk/sun/tools/jrunscript/CheckEngine.java b/src/java.base/macosx/classes/jdk/internal/ffi/generated/timespec/package-info.java similarity index 59% rename from test/jdk/sun/tools/jrunscript/CheckEngine.java rename to src/java.base/macosx/classes/jdk/internal/ffi/generated/timespec/package-info.java index dd8f4cd81ae..850991e61fc 100644 --- a/test/jdk/sun/tools/jrunscript/CheckEngine.java +++ b/src/java.base/macosx/classes/jdk/internal/ffi/generated/timespec/package-info.java @@ -1,10 +1,12 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or @@ -21,16 +23,19 @@ * questions. */ -import javax.script.*; - -/* - * If the JDK being tested is not a Sun product JDK and a js - * engine is not present, return an exit code of 2 to indicate that - * the jrunscript tests which assume a js engine can be vacuously - * passed. +/** + * Defines native structures for timespec APIs. + * Generated with the following jextract command: + * {@snippet lang = "Shell Script": + * + * HEADER_NAME=timespec.h + * echo "#include " > $HEADER_NAME + * + * jextract --target-package jdk.internal.ffi.generated.timespec \ + * --include-struct timespec \ + * $HEADER_NAME + * } + * */ -public class CheckEngine { - public static void main(String... args) { - System.exit(2); - } -} + +package jdk.internal.ffi.generated.timespec; diff --git a/src/java.base/macosx/classes/jdk/internal/ffi/generated/timespec/timespec.java b/src/java.base/macosx/classes/jdk/internal/ffi/generated/timespec/timespec.java new file mode 100644 index 00000000000..bee0caf26fc --- /dev/null +++ b/src/java.base/macosx/classes/jdk/internal/ffi/generated/timespec/timespec.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// Generated by jextract + +package jdk.internal.ffi.generated.timespec; + +import java.lang.foreign.*; +import java.util.function.*; + +import static java.lang.foreign.MemoryLayout.PathElement.*; +import static java.lang.foreign.ValueLayout.*; + +/** + * {@snippet lang=c : + * struct timespec { + * __darwin_time_t tv_sec; + * long tv_nsec; + * } + * } + */ +@SuppressWarnings("restricted") +public class timespec { + + timespec() { + // Should not be called directly + } + + private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( + timespec_h.C_LONG.withName("tv_sec"), + timespec_h.C_LONG.withName("tv_nsec") + ).withName("timespec"); + + /** + * The layout of this struct + */ + public static final GroupLayout layout() { + return $LAYOUT; + } + + private static final OfLong tv_sec$LAYOUT = (OfLong)$LAYOUT.select(groupElement("tv_sec")); + + /** + * Layout for field: + * {@snippet lang=c : + * __darwin_time_t tv_sec + * } + */ + public static final OfLong tv_sec$layout() { + return tv_sec$LAYOUT; + } + + private static final long tv_sec$OFFSET = $LAYOUT.byteOffset(groupElement("tv_sec")); + + /** + * Offset for field: + * {@snippet lang=c : + * __darwin_time_t tv_sec + * } + */ + public static final long tv_sec$offset() { + return tv_sec$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * __darwin_time_t tv_sec + * } + */ + public static long tv_sec(MemorySegment struct) { + return struct.get(tv_sec$LAYOUT, tv_sec$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * __darwin_time_t tv_sec + * } + */ + public static void tv_sec(MemorySegment struct, long fieldValue) { + struct.set(tv_sec$LAYOUT, tv_sec$OFFSET, fieldValue); + } + + private static final OfLong tv_nsec$LAYOUT = (OfLong)$LAYOUT.select(groupElement("tv_nsec")); + + /** + * Layout for field: + * {@snippet lang=c : + * long tv_nsec + * } + */ + public static final OfLong tv_nsec$layout() { + return tv_nsec$LAYOUT; + } + + private static final long tv_nsec$OFFSET = $LAYOUT.byteOffset(groupElement("tv_nsec")); + + /** + * Offset for field: + * {@snippet lang=c : + * long tv_nsec + * } + */ + public static final long tv_nsec$offset() { + return tv_nsec$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * long tv_nsec + * } + */ + public static long tv_nsec(MemorySegment struct) { + return struct.get(tv_nsec$LAYOUT, tv_nsec$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * long tv_nsec + * } + */ + public static void tv_nsec(MemorySegment struct, long fieldValue) { + struct.set(tv_nsec$LAYOUT, tv_nsec$OFFSET, fieldValue); + } + + /** + * Obtains a slice of {@code arrayParam} which selects the array element at {@code index}. + * The returned segment has address {@code arrayParam.address() + index * layout().byteSize()} + */ + public static MemorySegment asSlice(MemorySegment array, long index) { + return array.asSlice(layout().byteSize() * index); + } + + /** + * The size (in bytes) of this struct + */ + public static long sizeof() { return layout().byteSize(); } + + /** + * Allocate a segment of size {@code layout().byteSize()} using {@code allocator} + */ + public static MemorySegment allocate(SegmentAllocator allocator) { + return allocator.allocate(layout()); + } + + /** + * Allocate an array of size {@code elementCount} using {@code allocator}. + * The returned segment has size {@code elementCount * layout().byteSize()}. + */ + public static MemorySegment allocateArray(long elementCount, SegmentAllocator allocator) { + return allocator.allocate(MemoryLayout.sequenceLayout(elementCount, layout())); + } + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, Arena arena, Consumer cleanup) { + return reinterpret(addr, 1, arena, cleanup); + } + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code elementCount * layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, long elementCount, Arena arena, Consumer cleanup) { + return addr.reinterpret(layout().byteSize() * elementCount, arena, cleanup); + } +} diff --git a/src/java.base/macosx/classes/jdk/internal/ffi/generated/timespec/timespec_h$shared.java b/src/java.base/macosx/classes/jdk/internal/ffi/generated/timespec/timespec_h$shared.java new file mode 100644 index 00000000000..6af5bb12e0c --- /dev/null +++ b/src/java.base/macosx/classes/jdk/internal/ffi/generated/timespec/timespec_h$shared.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// Generated by jextract + +package jdk.internal.ffi.generated.timespec; + +import java.lang.foreign.*; +import java.lang.invoke.*; +import java.util.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; + +@SuppressWarnings("restricted") +public class timespec_h$shared { + + timespec_h$shared() { + // Should not be called directly + } + + public static final OfBoolean C_BOOL = (OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool"); + public static final OfByte C_CHAR =(OfByte)Linker.nativeLinker().canonicalLayouts().get("char"); + public static final OfShort C_SHORT = (OfShort) Linker.nativeLinker().canonicalLayouts().get("short"); + public static final OfInt C_INT = (OfInt) Linker.nativeLinker().canonicalLayouts().get("int"); + public static final OfLong C_LONG_LONG = (OfLong) Linker.nativeLinker().canonicalLayouts().get("long long"); + public static final OfFloat C_FLOAT = (OfFloat) Linker.nativeLinker().canonicalLayouts().get("float"); + public static final OfDouble C_DOUBLE = (OfDouble) Linker.nativeLinker().canonicalLayouts().get("double"); + public static final AddressLayout C_POINTER = ((AddressLayout) Linker.nativeLinker().canonicalLayouts().get("void*")) + .withTargetLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, C_CHAR)); + public static final OfLong C_LONG = (OfLong) Linker.nativeLinker().canonicalLayouts().get("long"); + + static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + + static void traceDowncall(String name, Object... args) { + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("%s(%s)\n", name, traceArgs); + } + + static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { + try { + return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + static MemoryLayout align(MemoryLayout layout, long align) { + return switch (layout) { + case PaddingLayout p -> p; + case ValueLayout v -> v.withByteAlignment(align); + case GroupLayout g -> { + MemoryLayout[] alignedMembers = g.memberLayouts().stream() + .map(m -> align(m, align)).toArray(MemoryLayout[]::new); + yield g instanceof StructLayout ? + MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); + } + case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); + }; + } +} diff --git a/test/jdk/sun/tools/jrunscript/Hello.java b/src/java.base/macosx/classes/jdk/internal/ffi/generated/timespec/timespec_h.java similarity index 57% rename from test/jdk/sun/tools/jrunscript/Hello.java rename to src/java.base/macosx/classes/jdk/internal/ffi/generated/timespec/timespec_h.java index 48b6c9298ab..e1470fcf43f 100644 --- a/test/jdk/sun/tools/jrunscript/Hello.java +++ b/src/java.base/macosx/classes/jdk/internal/ffi/generated/timespec/timespec_h.java @@ -1,10 +1,12 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or @@ -21,14 +23,21 @@ * questions. */ -/** - * This is a test program used in the test jrunscript-cp.sh - * - * - */ -public class Hello { - public Hello() {} - public String getString() { - return "hello"; - } +// Generated by jextract + +package jdk.internal.ffi.generated.timespec; + +import java.lang.foreign.*; + +public class timespec_h extends timespec_h$shared { + + timespec_h() { + // Should not be called directly + } + + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + + static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup() + .or(Linker.nativeLinker().defaultLookup()); + } diff --git a/src/java.base/share/classes/java/lang/ClassLoader.java b/src/java.base/share/classes/java/lang/ClassLoader.java index 47f624cb0f8..fdb5a80e2a2 100644 --- a/src/java.base/share/classes/java/lang/ClassLoader.java +++ b/src/java.base/share/classes/java/lang/ClassLoader.java @@ -30,9 +30,11 @@ import java.io.InputStream; import java.io.IOException; import java.io.UncheckedIOException; import java.io.File; +import java.lang.foreign.Arena; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.net.URL; +import java.nio.ByteBuffer; import java.security.CodeSource; import java.security.ProtectionDomain; import java.security.cert.Certificate; @@ -1037,7 +1039,7 @@ public abstract class ClassLoader { * * @since 1.5 */ - protected final Class defineClass(String name, java.nio.ByteBuffer b, + protected final Class defineClass(String name, ByteBuffer b, ProtectionDomain protectionDomain) throws ClassFormatError { @@ -1057,13 +1059,26 @@ public abstract class ClassLoader { } } - protectionDomain = preDefineClass(name, protectionDomain); - String source = defineClassSourceLocation(protectionDomain); + boolean trusted = this instanceof BuiltinClassLoader; + if (trusted) { + return defineClass(name, b, len, protectionDomain); + } else { + // make copy from input byte buffer + try (var arena = Arena.ofConfined()) { + ByteBuffer bb = arena.allocate(len).asByteBuffer(); + bb.put(0, b, b.position(), len); + return defineClass(name, bb, len, protectionDomain); + } + } + } + private Class defineClass(String name, ByteBuffer b, int len, ProtectionDomain pd) { + pd = preDefineClass(name, pd); + String source = defineClassSourceLocation(pd); SharedSecrets.getJavaNioAccess().acquireSession(b); try { - Class c = defineClass2(this, name, b, b.position(), len, protectionDomain, source); - postDefineClass(c, protectionDomain); + Class c = defineClass2(this, name, b, b.position(), len, pd, source); + postDefineClass(c, pd); return c; } finally { SharedSecrets.getJavaNioAccess().releaseSession(b); diff --git a/src/java.base/share/classes/java/lang/Float.java b/src/java.base/share/classes/java/lang/Float.java index ac5ab9ba055..db694571567 100644 --- a/src/java.base/share/classes/java/lang/Float.java +++ b/src/java.base/share/classes/java/lang/Float.java @@ -420,10 +420,12 @@ public final class Float extends Number // replace subnormal double exponent with subnormal float // exponent String s = Double.toHexString(Math.scalb((double)f, - /* -1022+126 */ - Double.MIN_EXPONENT- + // -1022 + 126 + Double.MIN_EXPONENT - Float.MIN_EXPONENT)); - return s.replaceFirst("p-1022$", "p-126"); + // The char sequence "-1022" can only appear in the + // representation of the exponent, not in the (hex) significand. + return s.replace("-1022", "-126"); } else // double string will be the same as float string return Double.toHexString(f); diff --git a/src/java.base/share/classes/java/lang/StringLatin1.java b/src/java.base/share/classes/java/lang/StringLatin1.java index da3acbe5f0a..61c62d049bc 100644 --- a/src/java.base/share/classes/java/lang/StringLatin1.java +++ b/src/java.base/share/classes/java/lang/StringLatin1.java @@ -41,61 +41,49 @@ import static java.lang.String.checkIndex; import static java.lang.String.checkOffset; final class StringLatin1 { - public static char charAt(byte[] value, int index) { + static char charAt(byte[] value, int index) { checkIndex(index, value.length); return (char)(value[index] & 0xff); } - public static boolean canEncode(char cp) { + static boolean canEncode(char cp) { return cp <= 0xff; } - public static boolean canEncode(int cp) { + static boolean canEncode(int cp) { return cp >=0 && cp <= 0xff; } - public static byte coderFromChar(char cp) { + static byte coderFromChar(char cp) { return (byte)((0xff - cp) >>> (Integer.SIZE - 1)); } - public static int length(byte[] value) { + static int length(byte[] value) { return value.length; } - public static int codePointAt(byte[] value, int index, int end) { - return value[index] & 0xff; - } - - public static int codePointBefore(byte[] value, int index) { - return value[index - 1] & 0xff; - } - - public static int codePointCount(byte[] value, int beginIndex, int endIndex) { - return endIndex - beginIndex; - } - - public static char[] toChars(byte[] value) { + static char[] toChars(byte[] value) { char[] dst = new char[value.length]; inflate(value, 0, dst, 0, value.length); return dst; } - public static byte[] inflate(byte[] value, int off, int len) { + static byte[] inflate(byte[] value, int off, int len) { byte[] ret = StringUTF16.newBytesFor(len); inflate(value, off, ret, 0, len); return ret; } - public static void getChars(byte[] value, int srcBegin, int srcEnd, char[] dst, int dstBegin) { + static void getChars(byte[] value, int srcBegin, int srcEnd, char[] dst, int dstBegin) { inflate(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); } - public static void getBytes(byte[] value, int srcBegin, int srcEnd, byte[] dst, int dstBegin) { + static void getBytes(byte[] value, int srcBegin, int srcEnd, byte[] dst, int dstBegin) { System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); } @IntrinsicCandidate - public static boolean equals(byte[] value, byte[] other) { + static boolean equals(byte[] value, byte[] other) { if (value.length == other.length) { for (int i = 0; i < value.length; i++) { if (value[i] != other[i]) { @@ -108,20 +96,20 @@ final class StringLatin1 { } @IntrinsicCandidate - public static int compareTo(byte[] value, byte[] other) { + static int compareTo(byte[] value, byte[] other) { int len1 = value.length; int len2 = other.length; return compareTo(value, other, len1, len2); } - public static int compareTo(byte[] value, byte[] other, int len1, int len2) { + static int compareTo(byte[] value, byte[] other, int len1, int len2) { int lim = Math.min(len1, len2); int k = ArraysSupport.mismatch(value, other, lim); return (k < 0) ? len1 - len2 : getChar(value, k) - getChar(other, k); } @IntrinsicCandidate - public static int compareToUTF16(byte[] value, byte[] other) { + static int compareToUTF16(byte[] value, byte[] other) { int len1 = length(value); int len2 = StringUTF16.length(other); return compareToUTF16Values(value, other, len1, len2); @@ -130,7 +118,7 @@ final class StringLatin1 { /* * Checks the boundary and then compares the byte arrays. */ - public static int compareToUTF16(byte[] value, byte[] other, int len1, int len2) { + static int compareToUTF16(byte[] value, byte[] other, int len1, int len2) { checkOffset(len1, length(value)); checkOffset(len2, StringUTF16.length(other)); @@ -149,7 +137,7 @@ final class StringLatin1 { return len1 - len2; } - public static int compareToCI(byte[] value, byte[] other) { + static int compareToCI(byte[] value, byte[] other) { int len1 = value.length; int len2 = other.length; int lim = Math.min(len1, len2); @@ -169,7 +157,7 @@ final class StringLatin1 { return len1 - len2; } - public static int compareToCI_UTF16(byte[] value, byte[] other) { + static int compareToCI_UTF16(byte[] value, byte[] other) { int len1 = length(value); int len2 = StringUTF16.length(other); int lim = Math.min(len1, len2); @@ -191,12 +179,12 @@ final class StringLatin1 { return len1 - len2; } - public static int hashCode(byte[] value) { + static int hashCode(byte[] value) { return ArraysSupport.hashCodeOfUnsigned(value, 0, value.length, 0); } // Caller must ensure that from- and toIndex are within bounds - public static int indexOf(byte[] value, int ch, int fromIndex, int toIndex) { + static int indexOf(byte[] value, int ch, int fromIndex, int toIndex) { if (!canEncode(ch)) { return -1; } @@ -215,7 +203,7 @@ final class StringLatin1 { } @IntrinsicCandidate - public static int indexOf(byte[] value, byte[] str) { + static int indexOf(byte[] value, byte[] str) { if (str.length == 0) { return 0; } @@ -226,7 +214,7 @@ final class StringLatin1 { } @IntrinsicCandidate - public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) { + static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) { byte first = str[0]; int max = (valueCount - strCount); for (int i = fromIndex; i <= max; i++) { @@ -248,8 +236,8 @@ final class StringLatin1 { return -1; } - public static int lastIndexOf(byte[] src, int srcCount, - byte[] tgt, int tgtCount, int fromIndex) { + static int lastIndexOf(byte[] src, int srcCount, + byte[] tgt, int tgtCount, int fromIndex) { int min = tgtCount - 1; int i = min + fromIndex; int strLastIndex = tgtCount - 1; @@ -276,7 +264,7 @@ final class StringLatin1 { } } - public static int lastIndexOf(final byte[] value, int ch, int fromIndex) { + static int lastIndexOf(final byte[] value, int ch, int fromIndex) { if (!canEncode(ch)) { return -1; } @@ -289,7 +277,7 @@ final class StringLatin1 { return -1; } - public static String replace(byte[] value, char oldChar, char newChar) { + static String replace(byte[] value, char oldChar, char newChar) { if (canEncode(oldChar)) { int len = value.length; int i = -1; @@ -326,8 +314,8 @@ final class StringLatin1 { return null; // for string to return this; } - public static String replace(byte[] value, int valLen, byte[] targ, - int targLen, byte[] repl, int replLen) + static String replace(byte[] value, int valLen, byte[] targ, + int targLen, byte[] repl, int replLen) { assert targLen > 0; int i, j, p = 0; @@ -377,8 +365,8 @@ final class StringLatin1 { } // case insensitive - public static boolean regionMatchesCI(byte[] value, int toffset, - byte[] other, int ooffset, int len) { + static boolean regionMatchesCI(byte[] value, int toffset, + byte[] other, int ooffset, int len) { int last = toffset + len; while (toffset < last) { byte b1 = value[toffset++]; @@ -391,8 +379,8 @@ final class StringLatin1 { return true; } - public static boolean regionMatchesCI_UTF16(byte[] value, int toffset, - byte[] other, int ooffset, int len) { + static boolean regionMatchesCI_UTF16(byte[] value, int toffset, + byte[] other, int ooffset, int len) { int last = toffset + len; while (toffset < last) { char c1 = (char)(value[toffset++] & 0xff); @@ -413,7 +401,7 @@ final class StringLatin1 { return true; } - public static String toLowerCase(String str, byte[] value, Locale locale) { + static String toLowerCase(String str, byte[] value, Locale locale) { if (locale == null) { throw new NullPointerException(); } @@ -480,7 +468,7 @@ final class StringLatin1 { return StringUTF16.newString(result, 0, resultOffset); } - public static String toUpperCase(String str, byte[] value, Locale locale) { + static String toUpperCase(String str, byte[] value, Locale locale) { if (locale == null) { throw new NullPointerException(); } @@ -560,7 +548,7 @@ final class StringLatin1 { return StringUTF16.newString(result, 0, resultOffset); } - public static String trim(byte[] value) { + static String trim(byte[] value) { int len = value.length; int st = 0; while ((st < len) && ((value[st] & 0xff) <= ' ')) { @@ -573,7 +561,7 @@ final class StringLatin1 { newString(value, st, len - st) : null; } - public static int indexOfNonWhitespace(byte[] value) { + static int indexOfNonWhitespace(byte[] value) { int length = value.length; int left = 0; while (left < length) { @@ -586,9 +574,8 @@ final class StringLatin1 { return left; } - public static int lastIndexOfNonWhitespace(byte[] value) { - int length = value.length; - int right = length; + static int lastIndexOfNonWhitespace(byte[] value) { + int right = value.length; while (0 < right) { char ch = getChar(value, right - 1); if (ch != ' ' && ch != '\t' && !CharacterDataLatin1.instance.isWhitespace(ch)) { @@ -599,7 +586,7 @@ final class StringLatin1 { return right; } - public static String strip(byte[] value) { + static String strip(byte[] value) { int left = indexOfNonWhitespace(value); if (left == value.length) { return ""; @@ -609,12 +596,12 @@ final class StringLatin1 { return ifChanged ? newString(value, left, right - left) : null; } - public static String stripLeading(byte[] value) { + static String stripLeading(byte[] value) { int left = indexOfNonWhitespace(value); return (left != 0) ? newString(value, left, value.length - left) : null; } - public static String stripTrailing(byte[] value) { + static String stripTrailing(byte[] value) { int right = lastIndexOfNonWhitespace(value); return (right != value.length) ? newString(value, 0, right) : null; } @@ -713,14 +700,14 @@ final class StringLatin1 { return StreamSupport.stream(LinesSpliterator.spliterator(value), false); } - public static void putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4) { + static void putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4) { value[i] = (byte)c1; value[i + 1] = (byte)c2; value[i + 2] = (byte)c3; value[i + 3] = (byte)c4; } - public static void putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4, char c5) { + static void putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4, char c5) { value[i] = (byte)c1; value[i + 1] = (byte)c2; value[i + 2] = (byte)c3; @@ -728,32 +715,15 @@ final class StringLatin1 { value[i + 4] = (byte)c5; } - public static void putChar(byte[] val, int index, int c) { - //assert (canEncode(c)); - val[index] = (byte)(c); - } - - public static char getChar(byte[] val, int index) { + static char getChar(byte[] val, int index) { return (char)(val[index] & 0xff); } - public static byte[] toBytes(int[] val, int off, int len) { - byte[] ret = new byte[len]; - for (int i = 0; i < len; i++) { - int cp = val[off++]; - if (!canEncode(cp)) { - return null; - } - ret[i] = (byte)cp; - } - return ret; - } - - public static byte[] toBytes(char c) { + static byte[] toBytes(char c) { return new byte[] { (byte)c }; } - public static String newString(byte[] val, int index, int len) { + static String newString(byte[] val, int index, int len) { if (len == 0) { return ""; } @@ -763,7 +733,7 @@ final class StringLatin1 { // inflatedCopy byte[] -> char[] @IntrinsicCandidate - public static void inflate(byte[] src, int srcOff, char[] dst, int dstOff, int len) { + static void inflate(byte[] src, int srcOff, char[] dst, int dstOff, int len) { for (int i = 0; i < len; i++) { dst[dstOff++] = (char)(src[srcOff++] & 0xff); } @@ -771,7 +741,7 @@ final class StringLatin1 { // inflatedCopy byte[] -> byte[] @IntrinsicCandidate - public static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) { + static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) { StringUTF16.inflate(src, srcOff, dst, dstOff, len); } @@ -824,7 +794,7 @@ final class StringLatin1 { } @Override - public long estimateSize() { return (long)(fence - index); } + public long estimateSize() { return fence - index; } @Override public int characteristics() { diff --git a/src/java.base/share/classes/java/lang/StringUTF16.java b/src/java.base/share/classes/java/lang/StringUTF16.java index 08b4072fc35..4e31c9728e9 100644 --- a/src/java.base/share/classes/java/lang/StringUTF16.java +++ b/src/java.base/share/classes/java/lang/StringUTF16.java @@ -54,13 +54,13 @@ final class StringUTF16 { // Return a new byte array for a UTF16-coded string for len chars // Throw an exception if out of range - public static byte[] newBytesFor(int len) { + static byte[] newBytesFor(int len) { return new byte[newBytesLength(len)]; } // Check the size of a UTF16-coded string // Throw an exception if out of range - public static int newBytesLength(int len) { + static int newBytesLength(int len) { if (len < 0) { throw new NegativeArraySizeException(); } @@ -89,7 +89,7 @@ final class StringUTF16 { ((val[index] & 0xff) << LO_BYTE_SHIFT)); } - public static int length(byte[] value) { + static int length(byte[] value) { return value.length >> 1; } @@ -111,7 +111,7 @@ final class StringUTF16 { return c1; } - public static int codePointAt(byte[] value, int index, int end) { + static int codePointAt(byte[] value, int index, int end) { return codePointAt(value, index, end, false /* unchecked */); } @@ -134,7 +134,7 @@ final class StringUTF16 { return c2; } - public static int codePointBefore(byte[] value, int index) { + static int codePointBefore(byte[] value, int index) { return codePointBefore(value, index, false /* unchecked */); } @@ -155,11 +155,11 @@ final class StringUTF16 { return count; } - public static int codePointCount(byte[] value, int beginIndex, int endIndex) { + static int codePointCount(byte[] value, int beginIndex, int endIndex) { return codePointCount(value, beginIndex, endIndex, false /* unchecked */); } - public static char[] toChars(byte[] value) { + static char[] toChars(byte[] value) { char[] dst = new char[value.length >> 1]; getChars(value, 0, dst.length, dst, 0); return dst; @@ -173,7 +173,7 @@ final class StringUTF16 { * @param len a length */ @IntrinsicCandidate - public static byte[] toBytes(char[] value, int off, int len) { + static byte[] toBytes(char[] value, int off, int len) { byte[] val = newBytesFor(len); for (int i = 0; i < len; i++) { putChar(val, i, value[off]); @@ -218,7 +218,7 @@ final class StringUTF16 { * @param count count of chars to be compressed, {@code count} > 0 */ @ForceInline - public static byte[] compress(final char[] val, final int off, final int count) { + static byte[] compress(final char[] val, final int off, final int count) { byte[] latin1 = new byte[count]; int ndx = compress(val, off, latin1, 0, count); if (ndx != count) { @@ -245,7 +245,7 @@ final class StringUTF16 { * @param off starting offset * @param count count of chars to be compressed, {@code count} > 0 */ - public static byte[] compress(final byte[] val, final int off, final int count) { + static byte[] compress(final byte[] val, final int off, final int count) { byte[] latin1 = new byte[count]; int ndx = compress(val, off, latin1, 0, count); if (ndx != count) {// Switch to UTF16 @@ -279,7 +279,7 @@ final class StringUTF16 { * @param off starting offset * @param count length of code points to be compressed, length > 0 */ - public static byte[] compress(final int[] val, int off, final int count) { + static byte[] compress(final int[] val, int off, final int count) { // Optimistically copy all latin1 code points to the destination byte[] latin1 = new byte[count]; final int end = off + count; @@ -389,7 +389,7 @@ final class StringUTF16 { // compressedCopy char[] -> byte[] @IntrinsicCandidate - public static int compress(char[] src, int srcOff, byte[] dst, int dstOff, int len) { + static int compress(char[] src, int srcOff, byte[] dst, int dstOff, int len) { for (int i = 0; i < len; i++) { char c = src[srcOff]; if (c > 0xff) { @@ -404,7 +404,7 @@ final class StringUTF16 { // compressedCopy byte[] -> byte[] @IntrinsicCandidate - public static int compress(byte[] src, int srcOff, byte[] dst, int dstOff, int len) { + static int compress(byte[] src, int srcOff, byte[] dst, int dstOff, int len) { // We need a range check here because 'getChar' has no checks checkBoundsOffCount(srcOff, len, src); for (int i = 0; i < len; i++) { @@ -420,7 +420,7 @@ final class StringUTF16 { } // Create the UTF16 buffer for !COMPACT_STRINGS - public static byte[] toBytes(int[] val, int index, int len) { + static byte[] toBytes(int[] val, int index, int len) { final int end = index + len; int n = computeCodePointSize(val, index, end); @@ -428,7 +428,7 @@ final class StringUTF16 { return extractCodepoints(val, index, end, buf, 0); } - public static byte[] toBytes(char c) { + static byte[] toBytes(char c) { byte[] result = new byte[2]; putChar(result, 0, c); return result; @@ -442,7 +442,7 @@ final class StringUTF16 { } @IntrinsicCandidate - public static void getChars(byte[] value, int srcBegin, int srcEnd, char[] dst, int dstBegin) { + static void getChars(byte[] value, int srcBegin, int srcEnd, char[] dst, int dstBegin) { // We need a range check here because 'getChar' has no checks if (srcBegin < srcEnd) { checkBoundsOffCount(srcBegin, srcEnd - srcBegin, value); @@ -453,7 +453,7 @@ final class StringUTF16 { } /* @see java.lang.String.getBytes(int, int, byte[], int) */ - public static void getBytes(byte[] value, int srcBegin, int srcEnd, byte[] dst, int dstBegin) { + static void getBytes(byte[] value, int srcBegin, int srcEnd, byte[] dst, int dstBegin) { srcBegin <<= 1; srcEnd <<= 1; for (int i = srcBegin + (1 >> LO_BYTE_SHIFT); i < srcEnd; i += 2) { @@ -462,7 +462,7 @@ final class StringUTF16 { } @IntrinsicCandidate - public static int compareTo(byte[] value, byte[] other) { + static int compareTo(byte[] value, byte[] other) { int len1 = length(value); int len2 = length(other); return compareValues(value, other, len1, len2); @@ -471,7 +471,7 @@ final class StringUTF16 { /* * Checks the boundary and then compares the byte arrays. */ - public static int compareTo(byte[] value, byte[] other, int len1, int len2) { + static int compareTo(byte[] value, byte[] other, int len1, int len2) { checkOffset(len1, value); checkOffset(len2, other); @@ -491,15 +491,15 @@ final class StringUTF16 { } @IntrinsicCandidate - public static int compareToLatin1(byte[] value, byte[] other) { + static int compareToLatin1(byte[] value, byte[] other) { return -StringLatin1.compareToUTF16(other, value); } - public static int compareToLatin1(byte[] value, byte[] other, int len1, int len2) { + static int compareToLatin1(byte[] value, byte[] other, int len1, int len2) { return -StringLatin1.compareToUTF16(other, value, len2, len1); } - public static int compareToCI(byte[] value, byte[] other) { + static int compareToCI(byte[] value, byte[] other) { return compareToCIImpl(value, 0, length(value), other, 0, length(other)); } @@ -512,8 +512,8 @@ final class StringUTF16 { assert olast <= length(other); for (int k1 = toffset, k2 = ooffset; k1 < tlast && k2 < olast; k1++, k2++) { - int cp1 = (int)getChar(value, k1); - int cp2 = (int)getChar(other, k2); + int cp1 = getChar(value, k1); + int cp2 = getChar(other, k2); if (cp1 == cp2 || compareCodePointCI(cp1, cp2) == 0) { continue; @@ -588,16 +588,16 @@ final class StringUTF16 { return cp; } - public static int compareToCI_Latin1(byte[] value, byte[] other) { + static int compareToCI_Latin1(byte[] value, byte[] other) { return -StringLatin1.compareToCI_UTF16(other, value); } - public static int hashCode(byte[] value) { + static int hashCode(byte[] value) { return ArraysSupport.hashCodeOfUTF16(value, 0, value.length >> 1, 0); } // Caller must ensure that from- and toIndex are within bounds - public static int indexOf(byte[] value, int ch, int fromIndex, int toIndex) { + static int indexOf(byte[] value, int ch, int fromIndex, int toIndex) { if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) { // handle most cases here (ch is a BMP code point or a // negative value (invalid code point)) @@ -608,7 +608,7 @@ final class StringUTF16 { } @IntrinsicCandidate - public static int indexOf(byte[] value, byte[] str) { + static int indexOf(byte[] value, byte[] str) { if (str.length == 0) { return 0; } @@ -619,7 +619,7 @@ final class StringUTF16 { } @IntrinsicCandidate - public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) { + static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) { checkBoundsBeginEnd(fromIndex, valueCount, value); checkBoundsBeginEnd(0, strCount, str); return indexOfUnsafe(value, valueCount, str, strCount, fromIndex); @@ -657,7 +657,7 @@ final class StringUTF16 { * Handles indexOf Latin1 substring in UTF16 string. */ @IntrinsicCandidate - public static int indexOfLatin1(byte[] value, byte[] str) { + static int indexOfLatin1(byte[] value, byte[] str) { if (str.length == 0) { return 0; } @@ -668,13 +668,13 @@ final class StringUTF16 { } @IntrinsicCandidate - public static int indexOfLatin1(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) { + static int indexOfLatin1(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) { checkBoundsBeginEnd(fromIndex, srcCount, src); String.checkBoundsBeginEnd(0, tgtCount, tgt.length); return indexOfLatin1Unsafe(src, srcCount, tgt, tgtCount, fromIndex); } - public static int indexOfLatin1Unsafe(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) { + static int indexOfLatin1Unsafe(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) { assert fromIndex >= 0; assert tgtCount > 0; assert tgtCount <= tgt.length; @@ -730,8 +730,8 @@ final class StringUTF16 { } // srcCoder == UTF16 && tgtCoder == UTF16 - public static int lastIndexOf(byte[] src, int srcCount, - byte[] tgt, int tgtCount, int fromIndex) { + static int lastIndexOf(byte[] src, int srcCount, + byte[] tgt, int tgtCount, int fromIndex) { assert fromIndex >= 0; assert tgtCount > 0; assert tgtCount <= length(tgt); @@ -765,7 +765,7 @@ final class StringUTF16 { } } - public static int lastIndexOf(byte[] value, int ch, int fromIndex) { + static int lastIndexOf(byte[] value, int ch, int fromIndex) { if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) { // handle most cases here (ch is a BMP code point or a // negative value (invalid code point)) @@ -798,7 +798,7 @@ final class StringUTF16 { return -1; } - public static String replace(byte[] value, char oldChar, char newChar) { + static String replace(byte[] value, char oldChar, char newChar) { int len = value.length >> 1; int i = -1; while (++i < len) { @@ -829,9 +829,9 @@ final class StringUTF16 { return null; } - public static String replace(byte[] value, int valLen, boolean valLat1, - byte[] targ, int targLen, boolean targLat1, - byte[] repl, int replLen, boolean replLat1) + static String replace(byte[] value, int valLen, boolean valLat1, + byte[] targ, int targLen, boolean targLat1, + byte[] repl, int replLen, boolean replLat1) { assert targLen > 0; assert !valLat1 || !targLat1 || !replLat1; @@ -944,18 +944,18 @@ final class StringUTF16 { return new String(result, UTF16); } - public static boolean regionMatchesCI(byte[] value, int toffset, - byte[] other, int ooffset, int len) { + static boolean regionMatchesCI(byte[] value, int toffset, + byte[] other, int ooffset, int len) { return compareToCIImpl(value, toffset, len, other, ooffset, len) == 0; } - public static boolean regionMatchesCI_Latin1(byte[] value, int toffset, - byte[] other, int ooffset, - int len) { + static boolean regionMatchesCI_Latin1(byte[] value, int toffset, + byte[] other, int ooffset, + int len) { return StringLatin1.regionMatchesCI_UTF16(other, ooffset, value, toffset, len); } - public static String toLowerCase(String str, byte[] value, Locale locale) { + static String toLowerCase(String str, byte[] value, Locale locale) { if (locale == null) { throw new NullPointerException(); } @@ -965,7 +965,7 @@ final class StringUTF16 { // Now check if there are any characters that need to be changed, or are surrogate for (first = 0 ; first < len; first++) { - int cp = (int)getChar(value, first); + int cp = getChar(value, first); if (Character.isSurrogate((char)cp)) { hasSurr = true; break; @@ -988,7 +988,7 @@ final class StringUTF16 { } int bits = 0; for (int i = first; i < len; i++) { - int cp = (int)getChar(value, i); + int cp = getChar(value, i); if (cp == '\u03A3' || // GREEK CAPITAL LETTER SIGMA Character.isSurrogate((char)cp)) { return toLowerCaseEx(str, value, result, i, locale, false); @@ -1003,7 +1003,7 @@ final class StringUTF16 { bits |= cp; putChar(result, i, cp); } - if (bits < 0 || bits > 0xff) { + if (bits > 0xff) { return new String(result, UTF16); } else { return newString(result, 0, len); @@ -1059,7 +1059,7 @@ final class StringUTF16 { return newString(result, 0, resultOffset); } - public static String toUpperCase(String str, byte[] value, Locale locale) { + static String toUpperCase(String str, byte[] value, Locale locale) { if (locale == null) { throw new NullPointerException(); } @@ -1069,7 +1069,7 @@ final class StringUTF16 { // Now check if there are any characters that need to be changed, or are surrogate for (first = 0 ; first < len; first++) { - int cp = (int)getChar(value, first); + int cp = getChar(value, first); if (Character.isSurrogate((char)cp)) { hasSurr = true; break; @@ -1093,7 +1093,7 @@ final class StringUTF16 { } int bits = 0; for (int i = first; i < len; i++) { - int cp = (int)getChar(value, i); + int cp = getChar(value, i); if (Character.isSurrogate((char)cp)) { return toUpperCaseEx(str, value, result, i, locale, false); } @@ -1104,7 +1104,7 @@ final class StringUTF16 { bits |= cp; putChar(result, i, cp); } - if (bits < 0 || bits > 0xff) { + if (bits > 0xff) { return new String(result, UTF16); } else { return newString(result, 0, len); @@ -1164,7 +1164,7 @@ final class StringUTF16 { return newString(result, 0, resultOffset); } - public static String trim(byte[] value) { + static String trim(byte[] value) { int length = value.length >> 1; int len = length; int st = 0; @@ -1179,7 +1179,7 @@ final class StringUTF16 { null; } - public static int indexOfNonWhitespace(byte[] value) { + static int indexOfNonWhitespace(byte[] value) { int length = value.length >> 1; int left = 0; while (left < length) { @@ -1192,9 +1192,8 @@ final class StringUTF16 { return left; } - public static int lastIndexOfNonWhitespace(byte[] value) { - int length = value.length >>> 1; - int right = length; + static int lastIndexOfNonWhitespace(byte[] value) { + int right = value.length >>> 1; while (0 < right) { int codepoint = codePointBefore(value, right); if (codepoint != ' ' && codepoint != '\t' && !Character.isWhitespace(codepoint)) { @@ -1205,7 +1204,7 @@ final class StringUTF16 { return right; } - public static String strip(byte[] value) { + static String strip(byte[] value) { int length = value.length >>> 1; int left = indexOfNonWhitespace(value); if (left == length) { @@ -1216,13 +1215,13 @@ final class StringUTF16 { return ifChanged ? newString(value, left, right - left) : null; } - public static String stripLeading(byte[] value) { + static String stripLeading(byte[] value) { int length = value.length >>> 1; int left = indexOfNonWhitespace(value); return (left != 0) ? newString(value, left, length - left) : null; } - public static String stripTrailing(byte[] value) { + static String stripTrailing(byte[] value) { int length = value.length >>> 1; int right = lastIndexOfNonWhitespace(value); return (right != length) ? newString(value, 0, right) : null; @@ -1322,7 +1321,7 @@ final class StringUTF16 { return StreamSupport.stream(LinesSpliterator.spliterator(value), false); } - public static String newString(byte[] val, int index, int len) { + static String newString(byte[] val, int index, int len) { if (len == 0) { return ""; } @@ -1388,7 +1387,7 @@ final class StringUTF16 { } @Override - public long estimateSize() { return (long)(fence - index); } + public long estimateSize() { return fence - index; } @Override public int characteristics() { @@ -1473,7 +1472,7 @@ final class StringUTF16 { } @Override - public long estimateSize() { return (long)(fence - index); } + public long estimateSize() { return fence - index; } @Override public int characteristics() { @@ -1483,12 +1482,12 @@ final class StringUTF16 { //////////////////////////////////////////////////////////////// - public static void putCharSB(byte[] val, int index, int c) { + static void putCharSB(byte[] val, int index, int c) { checkIndex(index, val); putChar(val, index, c); } - public static void putCharsSB(byte[] val, int index, char[] ca, int off, int end) { + static void putCharsSB(byte[] val, int index, char[] ca, int off, int end) { checkBoundsBeginEnd(index, index + end - off, val); String.checkBoundsBeginEnd(off, end, ca.length); Unsafe.getUnsafe().copyMemory( @@ -1499,26 +1498,26 @@ final class StringUTF16 { (long) (end - off) << 1); } - public static void putCharsSB(byte[] val, int index, CharSequence s, int off, int end) { + static void putCharsSB(byte[] val, int index, CharSequence s, int off, int end) { checkBoundsBeginEnd(index, index + end - off, val); for (int i = off; i < end; i++) { putChar(val, index++, s.charAt(i)); } } - public static int codePointAtSB(byte[] val, int index, int end) { + static int codePointAtSB(byte[] val, int index, int end) { return codePointAt(val, index, end, true /* checked */); } - public static int codePointBeforeSB(byte[] val, int index) { + static int codePointBeforeSB(byte[] val, int index) { return codePointBefore(val, index, true /* checked */); } - public static int codePointCountSB(byte[] val, int beginIndex, int endIndex) { + static int codePointCountSB(byte[] val, int beginIndex, int endIndex) { return codePointCount(val, beginIndex, endIndex, true /* checked */); } - public static boolean contentEquals(byte[] v1, byte[] v2, int len) { + static boolean contentEquals(byte[] v1, byte[] v2, int len) { checkBoundsOffCount(0, len, v2); for (int i = 0; i < len; i++) { if ((char)(v1[i] & 0xff) != getChar(v2, i)) { @@ -1528,7 +1527,7 @@ final class StringUTF16 { return true; } - public static boolean contentEquals(byte[] value, CharSequence cs, int len) { + static boolean contentEquals(byte[] value, CharSequence cs, int len) { checkOffset(len, value); for (int i = 0; i < len; i++) { if (getChar(value, i) != cs.charAt(i)) { @@ -1538,7 +1537,7 @@ final class StringUTF16 { return true; } - public static void putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4) { + static void putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4) { int end = i + 4; checkBoundsBeginEnd(i, end, value); putChar(value, i, c1); @@ -1547,7 +1546,7 @@ final class StringUTF16 { putChar(value, i + 3, c4); } - public static void putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4, char c5) { + static void putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4, char c5) { int end = i + 5; checkBoundsBeginEnd(i, end, value); putChar(value, i, c1); @@ -1557,12 +1556,12 @@ final class StringUTF16 { putChar(value, i + 4, c5); } - public static char charAt(byte[] value, int index) { + static char charAt(byte[] value, int index) { checkIndex(index, value); return getChar(value, index); } - public static void reverse(byte[] val, int count) { + static void reverse(byte[] val, int count) { checkOffset(count, val); int n = count - 1; boolean hasSurrogates = false; @@ -1597,7 +1596,7 @@ final class StringUTF16 { } // inflatedCopy byte[] -> byte[] - public static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) { + static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) { // We need a range check here because 'putChar' has no checks checkBoundsOffCount(dstOff, len, dst); for (int i = 0; i < len; i++) { @@ -1606,7 +1605,7 @@ final class StringUTF16 { } // srcCoder == UTF16 && tgtCoder == LATIN1 - public static int lastIndexOfLatin1(byte[] src, int srcCount, + static int lastIndexOfLatin1(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) { assert fromIndex >= 0; assert tgtCount > 0; @@ -1659,19 +1658,19 @@ final class StringUTF16 { static final int MAX_LENGTH = Integer.MAX_VALUE >> 1; - public static void checkIndex(int off, byte[] val) { + static void checkIndex(int off, byte[] val) { String.checkIndex(off, length(val)); } - public static void checkOffset(int off, byte[] val) { + static void checkOffset(int off, byte[] val) { String.checkOffset(off, length(val)); } - public static void checkBoundsBeginEnd(int begin, int end, byte[] val) { + static void checkBoundsBeginEnd(int begin, int end, byte[] val) { String.checkBoundsBeginEnd(begin, end, length(val)); } - public static void checkBoundsOffCount(int offset, int count, byte[] val) { + static void checkBoundsOffCount(int offset, int count, byte[] val) { String.checkBoundsOffCount(offset, count, length(val)); } diff --git a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java index f4d82595842..99716baf439 100644 --- a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java +++ b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java @@ -165,22 +165,22 @@ public final class SwitchBootstraps { * @param lookup Represents a lookup context with the accessibility * privileges of the caller. When used with {@code invokedynamic}, * this is stacked automatically by the VM. - * @param invocationName unused + * @param invocationName unused, {@code null} is permitted * @param invocationType The invocation type of the {@code CallSite} with two parameters, * a reference type, an {@code int}, and {@code int} as a return type. * @param labels case labels - {@code String} and {@code Integer} constants * and {@code Class} and {@code EnumDesc} instances, in any combination * @return a {@code CallSite} returning the first matching element as described above * - * @throws NullPointerException if any argument is {@code null} + * @throws NullPointerException if any argument is {@code null}, unless noted otherwise * @throws IllegalArgumentException if any element in the labels array is null * @throws IllegalArgumentException if the invocation type is not a method type of first parameter of a reference type, - * second parameter of type {@code int} and with {@code int} as its return type, + * second parameter of type {@code int} and with {@code int} as its return type * @throws IllegalArgumentException if {@code labels} contains an element that is not of type {@code String}, * {@code Integer}, {@code Long}, {@code Float}, {@code Double}, {@code Boolean}, - * {@code Class} or {@code EnumDesc}. + * {@code Class} or {@code EnumDesc} * @throws IllegalArgumentException if {@code labels} contains an element that is not of type {@code Boolean} - * when {@code target} is a {@code Boolean.class}. + * when {@code target} is a {@code Boolean.class} * @jvms 4.4.6 The CONSTANT_NameAndType_info Structure * @jvms 4.4.10 The CONSTANT_Dynamic_info and CONSTANT_InvokeDynamic_info Structures */ @@ -255,29 +255,36 @@ public final class SwitchBootstraps { * enum constant's {@link Enum#name()}. * *

- * If no element in the {@code labels} array matches the target, then - * the method of the call site return the length of the {@code labels} array. + * If for a given {@code target} there is no element in the {@code labels} + * fulfilling one of the above conditions, then the method of the call + * site returns the length of the {@code labels} array. *

* The value of the {@code restart} index must be between {@code 0} (inclusive) and * the length of the {@code labels} array (inclusive), - * both or an {@link IndexOutOfBoundsException} is thrown. + * or an {@link IndexOutOfBoundsException} is thrown. + * + * @apiNote It is permissible for the {@code labels} array to contain {@code String} + * values that do not represent any enum constants at runtime. * * @param lookup Represents a lookup context with the accessibility * privileges of the caller. When used with {@code invokedynamic}, * this is stacked automatically by the VM. - * @param invocationName unused + * @param invocationName unused, {@code null} is permitted * @param invocationType The invocation type of the {@code CallSite} with two parameters, * an enum type, an {@code int}, and {@code int} as a return type. * @param labels case labels - {@code String} constants and {@code Class} instances, * in any combination * @return a {@code CallSite} returning the first matching element as described above * - * @throws NullPointerException if any argument is {@code null} - * @throws IllegalArgumentException if any element in the labels array is null, if the - * invocation type is not a method type whose first parameter type is an enum type, - * second parameter of type {@code int} and whose return type is {@code int}, - * or if {@code labels} contains an element that is not of type {@code String} or - * {@code Class} of the target enum type. + * @throws NullPointerException if any argument is {@code null}, unless noted otherwise + * @throws IllegalArgumentException if any element in the labels array is null + * @throws IllegalArgumentException if any element in the labels array is an empty {@code String} + * @throws IllegalArgumentException if the invocation type is not a method type + * whose first parameter type is an enum type, + * second parameter of type {@code int} and + * whose return type is {@code int} + * @throws IllegalArgumentException if {@code labels} contains an element that is not of type {@code String} or + * {@code Class} equal to the target enum type * @jvms 4.4.6 The CONSTANT_NameAndType_info Structure * @jvms 4.4.10 The CONSTANT_Dynamic_info and CONSTANT_InvokeDynamic_info Structures */ diff --git a/src/java.base/share/classes/java/net/URL.java b/src/java.base/share/classes/java/net/URL.java index 1435d851f41..c82236b5b85 100644 --- a/src/java.base/share/classes/java/net/URL.java +++ b/src/java.base/share/classes/java/net/URL.java @@ -41,7 +41,6 @@ import java.util.ServiceLoader; import jdk.internal.access.JavaNetURLAccess; import jdk.internal.access.SharedSecrets; -import jdk.internal.misc.ThreadTracker; import jdk.internal.misc.VM; import jdk.internal.vm.annotation.AOTRuntimeSetup; import jdk.internal.vm.annotation.AOTSafeClassInitializer; @@ -1394,24 +1393,13 @@ public final class URL implements java.io.Serializable { return handler; } - private static class ThreadTrackHolder { - static final ThreadTracker TRACKER = new ThreadTracker(); - } - - private static Object tryBeginLookup() { - return ThreadTrackHolder.TRACKER.tryBegin(); - } - - private static void endLookup(Object key) { - ThreadTrackHolder.TRACKER.end(key); - } + private static final ScopedValue IN_LOOKUP = ScopedValue.newInstance(); private static URLStreamHandler lookupViaProviders(final String protocol) { - Object key = tryBeginLookup(); - if (key == null) { + if (IN_LOOKUP.isBound()) { throw new Error("Circular loading of URL stream handler providers detected"); } - try { + return ScopedValue.where(IN_LOOKUP, true).call(() -> { final ClassLoader cl = ClassLoader.getSystemClassLoader(); final ServiceLoader sl = ServiceLoader.load(URLStreamHandlerProvider.class, cl); @@ -1423,9 +1411,7 @@ public final class URL implements java.io.Serializable { return h; } return null; - } finally { - endLookup(key); - } + }); } /** diff --git a/src/java.base/share/classes/java/text/DigitList.java b/src/java.base/share/classes/java/text/DigitList.java index ce098bd8800..9ad1203fb62 100644 --- a/src/java.base/share/classes/java/text/DigitList.java +++ b/src/java.base/share/classes/java/text/DigitList.java @@ -356,6 +356,11 @@ final class DigitList implements Cloneable { } decimalAt = count + exp; + // Eliminate trailing zeros. + while (count > 1 && digits[count - 1] == '0') { + --count; + } + if (fixedPoint) { // The negative of the exponent represents the number of leading // zeros between the decimal and the first non-zero digit, for @@ -382,11 +387,6 @@ final class DigitList implements Cloneable { // else fall through } - // Eliminate trailing zeros. - while (count > 1 && digits[count - 1] == '0') { - --count; - } - // Eliminate digits beyond maximum digits to be displayed. // Round up if appropriate. round(fixedPoint ? (maximumDigits + decimalAt) : maximumDigits, diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index eb69d1dfc2f..a55ddee648e 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -2726,9 +2726,13 @@ public final class Locale implements Cloneable, Serializable { * @see Locale#forLanguageTag(String) */ public Builder setLanguageTag(String languageTag) { - LanguageTag tag = LanguageTag.parse( - languageTag, new ParsePosition(0), false); - localeBuilder.setLanguageTag(tag); + if (LocaleUtils.isEmpty(languageTag)) { + localeBuilder.clear(); + } else { + LanguageTag tag = LanguageTag.parse( + languageTag, new ParsePosition(0), false); + localeBuilder.setLanguageTag(tag); + } return this; } diff --git a/src/java.base/share/classes/java/util/SimpleTimeZone.java b/src/java.base/share/classes/java/util/SimpleTimeZone.java index 58d43692ee0..f047ce26e58 100644 --- a/src/java.base/share/classes/java/util/SimpleTimeZone.java +++ b/src/java.base/share/classes/java/util/SimpleTimeZone.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -112,15 +112,15 @@ import sun.util.calendar.Gregorian; *


  *      // Base GMT offset: -8:00
  *      // DST starts:      at 2:00am in standard time
- *      //                  on the first Sunday in April
+ *      //                  on the second Sunday in March
  *      // DST ends:        at 2:00am in daylight time
- *      //                  on the last Sunday in October
+ *      //                  on the first Sunday in November
  *      // Save:            1 hour
  *      SimpleTimeZone(-28800000,
  *                     "America/Los_Angeles",
- *                     Calendar.APRIL, 1, -Calendar.SUNDAY,
+ *                     Calendar.MARCH, 8, -Calendar.SUNDAY,
  *                     7200000,
- *                     Calendar.OCTOBER, -1, Calendar.SUNDAY,
+ *                     Calendar.NOVEMBER, 1, -Calendar.SUNDAY,
  *                     7200000,
  *                     3600000)
  *
@@ -863,13 +863,24 @@ public class SimpleTimeZone extends TimeZone {
     }
 
     /**
-     * Generates the hash code for the SimpleDateFormat object.
+     * Generates the hash code for the SimpleTimeZone object.
      * @return the hash code for this object
      */
     public int hashCode()
     {
-        return startMonth ^ startDay ^ startDayOfWeek ^ startTime ^
-            endMonth ^ endDay ^ endDayOfWeek ^ endTime ^ rawOffset;
+        int hash = 31 * getID().hashCode() + rawOffset;
+        hash = 31 * hash + Boolean.hashCode(useDaylight);
+        if (useDaylight) {
+            hash = 31 * hash + startMonth;
+            hash = 31 * hash + startDay;
+            hash = 31 * hash + startDayOfWeek;
+            hash = 31 * hash + startTime;
+            hash = 31 * hash + endMonth;
+            hash = 31 * hash + endDay;
+            hash = 31 * hash + endDayOfWeek;
+            hash = 31 * hash + endTime;
+        }
+        return hash;
     }
 
     /**
diff --git a/src/java.base/share/classes/java/util/concurrent/Executors.java b/src/java.base/share/classes/java/util/concurrent/Executors.java
index ef3d6348010..8b339cf83ef 100644
--- a/src/java.base/share/classes/java/util/concurrent/Executors.java
+++ b/src/java.base/share/classes/java/util/concurrent/Executors.java
@@ -755,6 +755,13 @@ public final class Executors {
             super.shutdown();
             cleanable.clean();  // unregisters the cleanable
         }
+
+        @Override
+        public List shutdownNow() {
+            List unexecuted = super.shutdownNow();
+            cleanable.clean();  // unregisters the cleanable
+            return unexecuted;
+        }
     }
 
     /**
diff --git a/src/java.base/share/classes/javax/crypto/Cipher.java b/src/java.base/share/classes/javax/crypto/Cipher.java
index b22b8cf5483..f95917b5c86 100644
--- a/src/java.base/share/classes/javax/crypto/Cipher.java
+++ b/src/java.base/share/classes/javax/crypto/Cipher.java
@@ -286,7 +286,32 @@ public class Cipher {
         this.lock = new Object();
     }
 
-    private static final String SHA512TRUNCATED = "SHA512/2";
+    // for special handling SHA-512/224, SHA-512/256, SHA512/224, SHA512/256
+    static int indexOfRealSlash(String s, int fromIndex) {
+        while (true) {
+            int pos = s.indexOf('/', fromIndex);
+            // 512/2
+            if (pos > 3 && pos + 1 < s.length()
+                    && s.charAt(pos - 3) == '5'
+                    && s.charAt(pos - 2) == '1'
+                    && s.charAt(pos - 1) == '2'
+                    && s.charAt(pos + 1) == '2') {
+                fromIndex = pos + 1;
+                // see 512/2, find next
+            } else {
+                return pos;
+            }
+        }
+    }
+
+    static String reqNonEmpty(String in, String msg)
+            throws NoSuchAlgorithmException {
+        in = in.trim();
+        if (in.isEmpty()) {
+            throw new NoSuchAlgorithmException(msg);
+        }
+        return in;
+    }
 
     // Parse the specified cipher transformation for algorithm and the
     // optional mode and padding. If the transformation contains only
@@ -305,42 +330,34 @@ public class Cipher {
          * 2) feedback component (e.g., CFB) - optional
          * 3) padding component (e.g., PKCS5Padding) - optional
          */
-
-        // check if the transformation contains algorithms with "/" in their
-        // name which can cause the parsing logic to go wrong
-        int sha512Idx = transformation.toUpperCase(Locale.ENGLISH)
-                .indexOf(SHA512TRUNCATED);
-        int startIdx = (sha512Idx == -1 ? 0 :
-                sha512Idx + SHA512TRUNCATED.length());
-        int endIdx = transformation.indexOf('/', startIdx);
-
-        boolean algorithmOnly = (endIdx == -1);
-        String algo = (algorithmOnly ? transformation.trim() :
-                transformation.substring(0, endIdx).trim());
-        if (algo.isEmpty()) {
-            throw new NoSuchAlgorithmException("Invalid transformation: " +
-                                   "algorithm not specified-"
-                                   + transformation);
+        int endIdx = indexOfRealSlash(transformation, 0);
+        if (endIdx == -1) { // algo only, done
+            return new String[] { reqNonEmpty(transformation,
+                        "Invalid transformation: algorithm not specified")
+            };
         }
-        if (algorithmOnly) { // done
-            return new String[] { algo };
+        // must be algo/mode/padding
+        String algo = reqNonEmpty(transformation.substring(0, endIdx),
+                    "Invalid transformation: algorithm not specified");
+
+        int startIdx = endIdx + 1;
+        endIdx = indexOfRealSlash(transformation, startIdx);
+        if (endIdx == -1) {
+            throw new NoSuchAlgorithmException(
+                    "Invalid transformation format: " + transformation);
+        }
+        String mode = reqNonEmpty(transformation.substring(startIdx,
+                endIdx), "Invalid transformation: missing mode");
+
+        startIdx = endIdx + 1;
+        endIdx = indexOfRealSlash(transformation, startIdx);
+        if (endIdx == -1) {
+            return new String[] { algo, mode,
+                    reqNonEmpty(transformation.substring(startIdx),
+                            "Invalid transformation: missing padding") };
         } else {
-            // continue parsing mode and padding
-            startIdx = endIdx+1;
-            endIdx = transformation.indexOf('/', startIdx);
-            if (endIdx == -1) {
-                throw new NoSuchAlgorithmException("Invalid transformation"
-                            + " format:" + transformation);
-            }
-            String mode = transformation.substring(startIdx, endIdx).trim();
-            String padding = transformation.substring(endIdx+1).trim();
-            // ensure mode and padding are specified
-            if (mode.isEmpty() || padding.isEmpty()) {
-                throw new NoSuchAlgorithmException("Invalid transformation: " +
-                                   "missing mode and/or padding-"
-                                   + transformation);
-            }
-            return new String[] { algo, mode, padding };
+            throw new NoSuchAlgorithmException(
+                    "Invalid transformation format: " + transformation);
         }
     }
 
diff --git a/src/java.base/share/classes/jdk/internal/vm/Continuation.java b/src/java.base/share/classes/jdk/internal/vm/Continuation.java
index a97f9ac9ea4..a7eb3ea6a9f 100644
--- a/src/java.base/share/classes/jdk/internal/vm/Continuation.java
+++ b/src/java.base/share/classes/jdk/internal/vm/Continuation.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -57,7 +57,6 @@ public class Continuation {
     /** Reason for pinning */
     public enum Pinned {
         /** Native frame on stack */ NATIVE,
-        /** Monitor held */          MONITOR,
         /** In critical section */   CRITICAL_SECTION,
         /** Exception (OOME/SOE) */  EXCEPTION
     }
@@ -69,8 +68,7 @@ public class Continuation {
         /** Permanent failure: continuation already yielding */             PERM_FAIL_YIELDING(null),
         /** Permanent failure: continuation not mounted on the thread */    PERM_FAIL_NOT_MOUNTED(null),
         /** Transient failure: continuation pinned due to a held CS */      TRANSIENT_FAIL_PINNED_CRITICAL_SECTION(Pinned.CRITICAL_SECTION),
-        /** Transient failure: continuation pinned due to native frame */   TRANSIENT_FAIL_PINNED_NATIVE(Pinned.NATIVE),
-        /** Transient failure: continuation pinned due to a held monitor */ TRANSIENT_FAIL_PINNED_MONITOR(Pinned.MONITOR);
+        /** Transient failure: continuation pinned due to native frame */   TRANSIENT_FAIL_PINNED_NATIVE(Pinned.NATIVE);
 
         final Pinned pinned;
         private PreemptStatus(Pinned reason) { this.pinned = reason; }
@@ -85,8 +83,7 @@ public class Continuation {
         return switch (reason) {
             case 2 -> Pinned.CRITICAL_SECTION;
             case 3 -> Pinned.NATIVE;
-            case 4 -> Pinned.MONITOR;
-            case 5 -> Pinned.EXCEPTION;
+            case 4 -> Pinned.EXCEPTION;
             default -> throw new AssertionError("Unknown pinned reason: " + reason);
         };
     }
diff --git a/src/java.base/share/classes/sun/launcher/SecuritySettings.java b/src/java.base/share/classes/sun/launcher/SecuritySettings.java
index afd5ead4914..e7ec70afcc3 100644
--- a/src/java.base/share/classes/sun/launcher/SecuritySettings.java
+++ b/src/java.base/share/classes/sun/launcher/SecuritySettings.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -136,7 +136,18 @@ public final class SecuritySettings {
             for (String s : ssls.getEnabledCipherSuites()) {
                 ostream.println(THREEINDENT + s);
             }
+
+            ostream.println("\n" + TWOINDENT + "Enabled Named Groups:");
+            String [] groups = ssls.getSSLParameters().getNamedGroups();
+            if (groups == null) {
+                ostream.println(THREEINDENT + "");
+            } else {
+                for (String s : groups) {
+                    ostream.println(THREEINDENT + s);
+                }
+            }
         }
+
         ostream.println();
     }
 
diff --git a/src/java.base/share/classes/sun/security/ssl/ChangeCipherSpec.java b/src/java.base/share/classes/sun/security/ssl/ChangeCipherSpec.java
index 6da6f745691..4ea61161c1d 100644
--- a/src/java.base/share/classes/sun/security/ssl/ChangeCipherSpec.java
+++ b/src/java.base/share/classes/sun/security/ssl/ChangeCipherSpec.java
@@ -232,7 +232,8 @@ final class ChangeCipherSpec {
             tc.consumers.remove(ContentType.CHANGE_CIPHER_SPEC.id);
 
             // parse
-            if (message.remaining() != 1 || message.get() != 1) {
+            if (message.remaining() != 1 || message.get() != 1
+                    || tc.isNegotiated) {
                 throw tc.fatal(Alert.UNEXPECTED_MESSAGE,
                         "Malformed or unexpected ChangeCipherSpec message");
             }
diff --git a/src/java.base/share/classes/sun/util/locale/LanguageTag.java b/src/java.base/share/classes/sun/util/locale/LanguageTag.java
index 6036c1dd04f..0b2fee7f2cd 100644
--- a/src/java.base/share/classes/sun/util/locale/LanguageTag.java
+++ b/src/java.base/share/classes/sun/util/locale/LanguageTag.java
@@ -34,17 +34,21 @@ package sun.util.locale;
 import java.text.ParsePosition;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.IllformedLocaleException;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.StringJoiner;
 
 // List fields are unmodifiable
-public record LanguageTag(String language, String script, String region, String privateuse,
-                          List extlangs, List variants, List extensions) {
+public record LanguageTag(String language,
+                          String script,
+                          String region,
+                          String privateuse,
+                          List extlangs,
+                          List variants,
+                          List extensions) {
 
     public static final String SEP = "-";
     public static final String PRIVATEUSE = "x";
@@ -53,78 +57,6 @@ public record LanguageTag(String language, String script, String region, String
     private static final String EMPTY_SUBTAG = "";
     private static final List EMPTY_SUBTAGS = List.of();
 
-    // Map contains legacy language tags and its preferred mappings from
-    // http://www.ietf.org/rfc/rfc5646.txt
-    // Keys are lower-case strings.
-    private static final Map LEGACY;
-
-    static {
-        // grandfathered = irregular           ; non-redundant tags registered
-        //               / regular             ; during the RFC 3066 era
-        //
-        // irregular     = "en-GB-oed"         ; irregular tags do not match
-        //               / "i-ami"             ; the 'langtag' production and
-        //               / "i-bnn"             ; would not otherwise be
-        //               / "i-default"         ; considered 'well-formed'
-        //               / "i-enochian"        ; These tags are all valid,
-        //               / "i-hak"             ; but most are deprecated
-        //               / "i-klingon"         ; in favor of more modern
-        //               / "i-lux"             ; subtags or subtag
-        //               / "i-mingo"           ; combination
-        //               / "i-navajo"
-        //               / "i-pwn"
-        //               / "i-tao"
-        //               / "i-tay"
-        //               / "i-tsu"
-        //               / "sgn-BE-FR"
-        //               / "sgn-BE-NL"
-        //               / "sgn-CH-DE"
-        //
-        // regular       = "art-lojban"        ; these tags match the 'langtag'
-        //               / "cel-gaulish"       ; production, but their subtags
-        //               / "no-bok"            ; are not extended language
-        //               / "no-nyn"            ; or variant subtags: their meaning
-        //               / "zh-guoyu"          ; is defined by their registration
-        //               / "zh-hakka"          ; and all of these are deprecated
-        //               / "zh-min"            ; in favor of a more modern
-        //               / "zh-min-nan"        ; subtag or sequence of subtags
-        //               / "zh-xiang"
-
-        final String[][] entries = {
-          //{"tag",         "preferred"},
-            {"art-lojban",  "jbo"},
-            {"cel-gaulish", "xtg-x-cel-gaulish"},   // fallback
-            {"en-GB-oed",   "en-GB-x-oed"},         // fallback
-            {"i-ami",       "ami"},
-            {"i-bnn",       "bnn"},
-            {"i-default",   "en-x-i-default"},      // fallback
-            {"i-enochian",  "und-x-i-enochian"},    // fallback
-            {"i-hak",       "hak"},
-            {"i-klingon",   "tlh"},
-            {"i-lux",       "lb"},
-            {"i-mingo",     "see-x-i-mingo"},       // fallback
-            {"i-navajo",    "nv"},
-            {"i-pwn",       "pwn"},
-            {"i-tao",       "tao"},
-            {"i-tay",       "tay"},
-            {"i-tsu",       "tsu"},
-            {"no-bok",      "nb"},
-            {"no-nyn",      "nn"},
-            {"sgn-BE-FR",   "sfb"},
-            {"sgn-BE-NL",   "vgt"},
-            {"sgn-CH-DE",   "sgg"},
-            {"zh-guoyu",    "cmn"},
-            {"zh-hakka",    "hak"},
-            {"zh-min",      "nan-x-zh-min"},        // fallback
-            {"zh-min-nan",  "nan"},
-            {"zh-xiang",    "hsn"},
-        };
-        LEGACY = HashMap.newHashMap(entries.length);
-        for (String[] e : entries) {
-            LEGACY.put(LocaleUtils.toLowerString(e[0]), e);
-        }
-    }
-
     /*
      * BNF in RFC5646
      *
@@ -175,14 +107,10 @@ public record LanguageTag(String language, String script, String region, String
         StringTokenIterator itr;
         var errorMsg = new StringBuilder();
 
-        // Check if the tag is a legacy language tag
-        String[] gfmap = LEGACY.get(LocaleUtils.toLowerString(languageTag));
-        if (gfmap != null) {
-            // use preferred mapping
-            itr = new StringTokenIterator(gfmap[1], SEP);
-        } else {
-            itr = new StringTokenIterator(languageTag, SEP);
-        }
+        // Check if the tag is a legacy tag
+        var pref = legacyToPreferred(LocaleUtils.toLowerString(languageTag));
+        // If legacy use preferred mapping, otherwise use the tag as is
+        itr = new StringTokenIterator(Objects.requireNonNullElse(pref, languageTag), SEP);
 
         String language = parseLanguage(itr, pp);
         List extlangs;
@@ -400,15 +328,24 @@ public record LanguageTag(String language, String script, String region, String
 
     public static String caseFoldTag(String tag) {
         parse(tag, new ParsePosition(0), false);
+        StringBuilder bldr = new StringBuilder(tag.length());
+        String[] subtags = tag.split(SEP);
 
         // Legacy tags
-        String potentialLegacy = tag.toLowerCase(Locale.ROOT);
-        if (LEGACY.containsKey(potentialLegacy)) {
-            return LEGACY.get(potentialLegacy)[0];
+        if (legacyToPreferred(tag.toLowerCase(Locale.ROOT)) != null) {
+            // Fold the legacy tag
+            for (int i = 0; i < subtags.length ; i++) {
+                // 2 ALPHA Region subtag(s) are upper, all other subtags are lower
+                if (i > 0 && subtags[i].length() == 2) {
+                    bldr.append(LocaleUtils.toUpperString(subtags[i])).append(SEP);
+                } else {
+                    bldr.append(LocaleUtils.toLowerString(subtags[i])).append(SEP);
+                }
+            }
+            bldr.setLength(bldr.length() - 1); // Remove trailing '-'
+            return bldr.toString();
         }
         // Non-legacy tags
-        StringBuilder bldr = new StringBuilder(tag.length());
-        String[] subtags = tag.split("-");
         boolean privateFound = false;
         boolean singletonFound = false;
         boolean privUseVarFound = false;
@@ -435,7 +372,7 @@ public record LanguageTag(String language, String script, String region, String
                 bldr.append(subtag.toLowerCase(Locale.ROOT));
             }
             if (i != subtags.length-1) {
-                bldr.append("-");
+                bldr.append(SEP);
             }
         }
         return bldr.substring(0);
@@ -567,6 +504,47 @@ public record LanguageTag(String language, String script, String region, String
         return new LanguageTag(language, script, region, privateuse, EMPTY_SUBTAGS, variants, extensions);
     }
 
+    /*
+     * Converts a legacy tag to its preferred mapping if it exists, otherwise null.
+     * The keys are mapped and stored as lower case. (Folded on demand).
+     * See http://www.ietf.org/rfc/rfc5646.txt Section 2.1 and 2.2.8 for the
+     * full syntax and case accurate legacy tags.
+     */
+    private static String legacyToPreferred(String tag) {
+        if (tag.length() < 5) {
+            return null;
+        }
+        return switch (tag) {
+            case "art-lojban" ->  "jbo";
+            case "cel-gaulish" -> "xtg-x-cel-gaulish";   // fallback
+            case "en-gb-oed" ->   "en-GB-x-oed";         // fallback
+            case "i-ami" ->       "ami";
+            case "i-bnn" ->       "bnn";
+            case "i-default" ->   "en-x-i-default";      // fallback
+            case "i-enochian" ->  "und-x-i-enochian";    // fallback
+            case "i-hak",
+                 "zh-hakka" ->    "hak";
+            case "i-klingon" ->   "tlh";
+            case "i-lux" ->       "lb";
+            case "i-mingo" ->     "see-x-i-mingo";       // fallback
+            case "i-navajo" ->    "nv";
+            case "i-pwn" ->       "pwn";
+            case "i-tao" ->       "tao";
+            case "i-tay" ->       "tay";
+            case "i-tsu" ->       "tsu";
+            case "no-bok" ->      "nb";
+            case "no-nyn" ->      "nn";
+            case "sgn-be-fr" ->   "sfb";
+            case "sgn-be-nl" ->   "vgt";
+            case "sgn-ch-de" ->   "sgg";
+            case "zh-guoyu" ->    "cmn";
+            case "zh-min" ->      "nan-x-zh-min";        // fallback
+            case "zh-min-nan" ->  "nan";
+            case "zh-xiang" ->    "hsn";
+            default -> null;
+        };
+    }
+
     //
     // Language subtag syntax checking methods
     //
diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixPath.java b/src/java.base/unix/classes/sun/nio/fs/UnixPath.java
index 6b0cd1bbf63..b722c30db42 100644
--- a/src/java.base/unix/classes/sun/nio/fs/UnixPath.java
+++ b/src/java.base/unix/classes/sun/nio/fs/UnixPath.java
@@ -948,28 +948,51 @@ class UnixPath implements Path {
 
             // Obtain the stream of entries in the directory corresponding
             // to the path constructed thus far, and extract the entry whose
-            // key is equal to the key of the current element
+            // internal path bytes equal the internal path bytes of the current
+            // element, or whose key is equal to the key  of the current element
+            boolean found = false;
             DirectoryStream.Filter filter = (p) -> { return true; };
+            // compare path bytes until a match is found or no more entries
             try (DirectoryStream entries = new UnixDirectoryStream(path, dp, filter)) {
-                boolean found = false;
                 for (Path entry : entries) {
-                    UnixPath p = path.resolve(entry.getFileName());
-                    UnixFileAttributes attributes = null;
-                    try {
-                        attributes = UnixFileAttributes.get(p, false);
-                        UnixFileKey key = attributes.fileKey();
-                        if (key.equals(elementKey)) {
-                            path = path.resolve(entry);
-                            found = true;
-                            break;
+                    Path name = entry.getFileName();
+                    if (name.compareTo(element) == 0) {
+                        found = true;
+                        path = path.resolve(entry);
+                        break;
+                    }
+                }
+            }
+
+            // if no path match found, compare file keys
+            if (!found) {
+                try {
+                    dp = opendir(path);
+                } catch (UnixException x) {
+                    x.rethrowAsIOException(path);
+                }
+
+                try (DirectoryStream entries = new UnixDirectoryStream(path, dp, filter)) {
+                    for (Path entry : entries) {
+                        Path name = entry.getFileName();
+                        UnixPath p = path.resolve(name);
+                        UnixFileAttributes attributes = null;
+                        try {
+                            attributes = UnixFileAttributes.get(p, false);
+                            UnixFileKey key = attributes.fileKey();
+                            if (key.equals(elementKey)) {
+                                found = true;
+                                path = path.resolve(entry);
+                                break;
+                            }
+                        } catch (UnixException ignore) {
+                            continue;
                         }
-                    } catch (UnixException ignore) {
-                        continue;
                     }
                 }
 
-                // Fallback which should in theory never happen
                 if (!found) {
+                    // Fallback which should in theory never happen
                     path = path.resolve(element);
                 }
             }
diff --git a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c
index 60ccdfc45fc..4b5cfabebfb 100644
--- a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c
+++ b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c
@@ -55,6 +55,7 @@
 #include 
 
 #ifdef __linux__
+#include  // For uintXX_t types used in statx support
 #include 
 #include  // makedev macros
 #endif
@@ -70,19 +71,12 @@
 // by defining binary compatible statx structs in this file and
 // not relying on included headers.
 
-#ifndef __GLIBC__
-// Alpine doesn't know these types, define them
-typedef unsigned int       __uint32_t;
-typedef unsigned short     __uint16_t;
-typedef unsigned long int  __uint64_t;
-#endif
-
 /*
  * Timestamp structure for the timestamps in struct statx.
  */
 struct my_statx_timestamp {
         int64_t   tv_sec;
-        __uint32_t  tv_nsec;
+        uint32_t  tv_nsec;
         int32_t   __reserved;
 };
 
@@ -92,27 +86,27 @@ struct my_statx_timestamp {
  */
 struct my_statx
 {
-  __uint32_t stx_mask;
-  __uint32_t stx_blksize;
-  __uint64_t stx_attributes;
-  __uint32_t stx_nlink;
-  __uint32_t stx_uid;
-  __uint32_t stx_gid;
-  __uint16_t stx_mode;
-  __uint16_t __statx_pad1[1];
-  __uint64_t stx_ino;
-  __uint64_t stx_size;
-  __uint64_t stx_blocks;
-  __uint64_t stx_attributes_mask;
+  uint32_t stx_mask;
+  uint32_t stx_blksize;
+  uint64_t stx_attributes;
+  uint32_t stx_nlink;
+  uint32_t stx_uid;
+  uint32_t stx_gid;
+  uint16_t stx_mode;
+  uint16_t __statx_pad1[1];
+  uint64_t stx_ino;
+  uint64_t stx_size;
+  uint64_t stx_blocks;
+  uint64_t stx_attributes_mask;
   struct my_statx_timestamp stx_atime;
   struct my_statx_timestamp stx_btime;
   struct my_statx_timestamp stx_ctime;
   struct my_statx_timestamp stx_mtime;
-  __uint32_t stx_rdev_major;
-  __uint32_t stx_rdev_minor;
-  __uint32_t stx_dev_major;
-  __uint32_t stx_dev_minor;
-  __uint64_t __statx_pad2[14];
+  uint32_t stx_rdev_major;
+  uint32_t stx_rdev_minor;
+  uint32_t stx_dev_major;
+  uint32_t stx_dev_minor;
+  uint64_t __statx_pad2[14];
 };
 
 // statx masks, flags, constants
diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsConstants.java b/src/java.base/windows/classes/sun/nio/fs/WindowsConstants.java
index 47fa16695d2..0c09a80e99e 100644
--- a/src/java.base/windows/classes/sun/nio/fs/WindowsConstants.java
+++ b/src/java.base/windows/classes/sun/nio/fs/WindowsConstants.java
@@ -96,6 +96,7 @@ class WindowsConstants {
     public static final int ERROR_NOT_SAME_DEVICE       = 17;
     public static final int ERROR_NOT_READY             = 21;
     public static final int ERROR_SHARING_VIOLATION     = 32;
+    public static final int ERROR_NETWORK_ACCESS_DENIED = 65;
     public static final int ERROR_FILE_EXISTS           = 80;
     public static final int ERROR_INVALID_PARAMETER     = 87;
     public static final int ERROR_DISK_FULL             = 112;
diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsException.java b/src/java.base/windows/classes/sun/nio/fs/WindowsException.java
index f1eff69210b..532728f57ed 100644
--- a/src/java.base/windows/classes/sun/nio/fs/WindowsException.java
+++ b/src/java.base/windows/classes/sun/nio/fs/WindowsException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -76,20 +76,21 @@ class WindowsException extends Exception {
     }
 
     private IOException translateToIOException(String file, String other) {
-        // not created with last error
-        if (lastError() == 0)
-            return new IOException(errorString());
+        return switch (lastError()) {
+            // not created with last error
+            case 0 -> new IOException(errorString());
 
-        // handle specific cases
-        if (lastError() == ERROR_FILE_NOT_FOUND || lastError() == ERROR_PATH_NOT_FOUND)
-            return new NoSuchFileException(file, other, null);
-        if (lastError() == ERROR_FILE_EXISTS || lastError() == ERROR_ALREADY_EXISTS)
-            return new FileAlreadyExistsException(file, other, null);
-        if (lastError() == ERROR_ACCESS_DENIED)
-            return new AccessDeniedException(file, other, null);
+            // handle specific cases
+            case ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND
+                -> new NoSuchFileException(file, other, null);
+            case ERROR_FILE_EXISTS, ERROR_ALREADY_EXISTS
+                -> new FileAlreadyExistsException(file, other, null);
+            case ERROR_ACCESS_DENIED, ERROR_NETWORK_ACCESS_DENIED, ERROR_PRIVILEGE_NOT_HELD
+                -> new AccessDeniedException(file, other, null);
 
-        // fallback to the more general exception
-        return new FileSystemException(file, other, errorString());
+            // fallback to the more general exception
+            default -> new FileSystemException(file, other, errorString());
+        };
     }
 
     void rethrowAsIOException(String file) throws IOException {
diff --git a/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java b/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java
index 8bf09195627..0c2902ba74a 100644
--- a/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java
+++ b/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java
@@ -41,6 +41,7 @@ import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InvalidObjectException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.io.ObjectStreamException;
@@ -1549,33 +1550,19 @@ public sealed class ICC_Profile implements Serializable
     private void readObject(ObjectInputStream s)
             throws IOException, ClassNotFoundException {
         s.defaultReadObject();
-
-        String csName = (String) s.readObject();
-        byte[] data = (byte[]) s.readObject();
-
-        int cspace = 0;         // ColorSpace.CS_* constant if known
-        boolean isKnownPredefinedCS = false;
-        if (csName != null) {
-            isKnownPredefinedCS = true;
-            if (csName.equals("CS_sRGB")) {
-                cspace = ColorSpace.CS_sRGB;
-            } else if (csName.equals("CS_CIEXYZ")) {
-                cspace = ColorSpace.CS_CIEXYZ;
-            } else if (csName.equals("CS_PYCC")) {
-                cspace = ColorSpace.CS_PYCC;
-            } else if (csName.equals("CS_GRAY")) {
-                cspace = ColorSpace.CS_GRAY;
-            } else if (csName.equals("CS_LINEAR_RGB")) {
-                cspace = ColorSpace.CS_LINEAR_RGB;
-            } else {
-                isKnownPredefinedCS = false;
-            }
-        }
-
-        if (isKnownPredefinedCS) {
-            resolvedDeserializedProfile = getInstance(cspace);
-        } else {
-            resolvedDeserializedProfile = getInstance(data);
+        try {
+            String csName = (String) s.readObject();
+            byte[] data = (byte[]) s.readObject();
+            resolvedDeserializedProfile = switch (csName) {
+                case "CS_sRGB" -> getInstance(ColorSpace.CS_sRGB);
+                case "CS_CIEXYZ" -> getInstance(ColorSpace.CS_CIEXYZ);
+                case "CS_PYCC" -> getInstance(ColorSpace.CS_PYCC);
+                case "CS_GRAY" -> getInstance(ColorSpace.CS_GRAY);
+                case "CS_LINEAR_RGB" -> getInstance(ColorSpace.CS_LINEAR_RGB);
+                case null, default -> getInstance(data);
+            };
+        } catch (ClassCastException | IllegalArgumentException e) {
+            throw new InvalidObjectException("Invalid ICC Profile Data", e);
         }
     }
 
diff --git a/src/java.desktop/share/classes/java/beans/Beans.java b/src/java.desktop/share/classes/java/beans/Beans.java
index 313bfe98515..a95aeb45cbb 100644
--- a/src/java.desktop/share/classes/java/beans/Beans.java
+++ b/src/java.desktop/share/classes/java/beans/Beans.java
@@ -64,6 +64,22 @@ public class Beans {
      * 

* Instantiate a JavaBean. *

+ * The bean is created based on a name relative to a class-loader. + * This name should be a {@linkplain ClassLoader##binary-name binary name} of a class such as "a.b.C". + *

+ * The given name can indicate either a serialized object or a class. + * We first try to treat the {@code beanName} as a serialized object + * name then as a class name. + *

+ * When using the {@code beanName} as a serialized object name we convert the + * given {@code beanName} to a resource pathname and add a trailing ".ser" suffix. + * We then try to load a serialized object from that resource. + *

+ * For example, given a {@code beanName} of "x.y", {@code Beans.instantiate} would first + * try to read a serialized object from the resource "x/y.ser" and if + * that failed it would try to load the class "x.y" and create an + * instance of that class. + * * @return a JavaBean * @param cls the class-loader from which we should create * the bean. If this is null, then the system @@ -84,6 +100,22 @@ public class Beans { *

* Instantiate a JavaBean. *

+ * The bean is created based on a name relative to a class-loader. + * This name should be a {@linkplain ClassLoader##binary-name binary name} of a class such as "a.b.C". + *

+ * The given name can indicate either a serialized object or a class. + * We first try to treat the {@code beanName} as a serialized object + * name then as a class name. + *

+ * When using the {@code beanName} as a serialized object name we convert the + * given {@code beanName} to a resource pathname and add a trailing ".ser" suffix. + * We then try to load a serialized object from that resource. + *

+ * For example, given a {@code beanName} of "x.y", {@code Beans.instantiate} would first + * try to read a serialized object from the resource "x/y.ser" and if + * that failed it would try to load the class "x.y" and create an + * instance of that class. + * * @return a JavaBean * * @param cls the class-loader from which we should create diff --git a/src/java.desktop/share/classes/javax/swing/JSplitPane.java b/src/java.desktop/share/classes/javax/swing/JSplitPane.java index f21b2b3339a..86a1a2495c7 100644 --- a/src/java.desktop/share/classes/javax/swing/JSplitPane.java +++ b/src/java.desktop/share/classes/javax/swing/JSplitPane.java @@ -371,24 +371,31 @@ public class JSplitPane extends JComponent implements Accessible public void setComponentOrientation(ComponentOrientation orientation) { ComponentOrientation curOrn = this.getComponentOrientation(); super.setComponentOrientation(orientation); + Component comp = null; if (!orientation.equals(curOrn)) { Component leftComponent = this.getLeftComponent(); Component rightComponent = this.getRightComponent(); if (!this.getComponentOrientation().isLeftToRight()) { if (rightComponent != null) { - setLeftComponent(rightComponent); - } - if (leftComponent != null) { - setRightComponent(leftComponent); + comp = this.leftComponent; + this.leftComponent = this.rightComponent; + this.rightComponent = comp; + } else if (leftComponent != null) { + comp = this.rightComponent; + this.rightComponent = this.leftComponent; + this.leftComponent = comp; } } else { if (leftComponent != null) { - setLeftComponent(leftComponent); + this.leftComponent = rightComponent; } if (rightComponent != null) { - setRightComponent(rightComponent); + this.rightComponent = leftComponent; } } + firePropertyChange(ORIENTATION_PROPERTY, curOrn, orientation); + this.revalidate(); + this.repaint(); } } diff --git a/src/java.desktop/share/classes/javax/swing/SwingUtilities.java b/src/java.desktop/share/classes/javax/swing/SwingUtilities.java index ebf39ac0283..4633e9c4756 100644 --- a/src/java.desktop/share/classes/javax/swing/SwingUtilities.java +++ b/src/java.desktop/share/classes/javax/swing/SwingUtilities.java @@ -49,10 +49,6 @@ import sun.awt.AWTAccessor.MouseEventAccessor; */ public class SwingUtilities implements SwingConstants { - // These states are system-wide, rather than AppContext wide. - private static boolean canAccessEventQueue = false; - private static boolean eventQueueTested = false; - /** * Indicates if we should change the drop target when a * {@code TransferHandler} is set. diff --git a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLookAndFeel.java b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLookAndFeel.java index 41d90d31dd4..7b9a23aec5d 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLookAndFeel.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLookAndFeel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2291,8 +2291,11 @@ public class MetalLookAndFeel extends BasicLookAndFeel setUpdatePending(true); Runnable uiUpdater = new Runnable() { public void run() { - updateAllUIs(); - setUpdatePending(false); + try { + updateAllUIs(); + } finally { + setUpdatePending(false); + } } }; SwingUtilities.invokeLater(uiUpdater); diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthLookAndFeel.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthLookAndFeel.java index 6cb4803c65f..700e3ed8a38 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthLookAndFeel.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthLookAndFeel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -909,8 +909,11 @@ public class SynthLookAndFeel extends BasicLookAndFeel { Runnable uiUpdater = new Runnable() { @Override public void run() { - updateAllUIs(); - setUpdatePending(false); + try { + updateAllUIs(); + } finally { + setUpdatePending(false); + } } }; SwingUtilities.invokeLater(uiUpdater); diff --git a/src/java.desktop/share/classes/sun/swing/plaf/DesktopProperty.java b/src/java.desktop/share/classes/sun/swing/plaf/DesktopProperty.java index 86d240a91fc..8af75f98b83 100644 --- a/src/java.desktop/share/classes/sun/swing/plaf/DesktopProperty.java +++ b/src/java.desktop/share/classes/sun/swing/plaf/DesktopProperty.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -214,8 +214,11 @@ public class DesktopProperty implements UIDefaults.ActiveValue { setUpdatePending(true); Runnable uiUpdater = new Runnable() { public void run() { - updateAllUIs(); - setUpdatePending(false); + try { + updateAllUIs(); + } finally { + setUpdatePending(false); + } } }; SwingUtilities.invokeLater(uiUpdater); diff --git a/src/java.desktop/unix/classes/sun/print/PrintServiceLookupProvider.java b/src/java.desktop/unix/classes/sun/print/PrintServiceLookupProvider.java index d96894660b9..f9445a78ede 100644 --- a/src/java.desktop/unix/classes/sun/print/PrintServiceLookupProvider.java +++ b/src/java.desktop/unix/classes/sun/print/PrintServiceLookupProvider.java @@ -876,12 +876,16 @@ public final class PrintServiceLookupProvider extends PrintServiceLookup FileReader reader = new FileReader(f); bufferedReader = new BufferedReader(reader); String line; + results = new ArrayList<>(); while ((line = bufferedReader.readLine()) != null) { results.add(line); } } - } finally { + } catch (Exception e) { + // Print exception for tracking printer command errors + IPPPrintService.debug_println("Printer command error: " + e); + } finally { f.delete(); // promptly close all streams. if (bufferedReader != null) { diff --git a/src/java.scripting/share/classes/com/sun/tools/script/shell/Main.java b/src/java.scripting/share/classes/com/sun/tools/script/shell/Main.java deleted file mode 100644 index 96fd6d010ac..00000000000 --- a/src/java.scripting/share/classes/com/sun/tools/script/shell/Main.java +++ /dev/null @@ -1,595 +0,0 @@ -/* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.sun.tools.script.shell; - -import java.io.*; -import java.net.*; -import java.nio.charset.Charset; -import java.text.*; -import java.util.*; -import javax.script.*; - -/** - * This is the main class for Java script shell. - */ -public class Main { - /** - * main entry point to the command line tool - * @param args command line argument array - */ - public static void main(String[] args) { - // print deprecation warning - getError().println(getMessage("deprecated.warning", - new Object[] { PROGRAM_NAME })); - - // parse command line options - String[] scriptArgs = processOptions(args); - - // process each script command - for (Command cmd : scripts) { - cmd.run(scriptArgs); - } - - System.exit(EXIT_SUCCESS); - } - - // Each -e or -f or interactive mode is represented - // by an instance of Command. - private static interface Command { - public void run(String[] arguments); - } - - /** - * Parses and processes command line options. - * @param args command line argument array - */ - private static String[] processOptions(String[] args) { - // current scripting language selected - String currentLanguage = DEFAULT_LANGUAGE; - // current script file encoding selected - String currentEncoding = null; - - // check for -classpath or -cp first - checkClassPath(args); - - // have we seen -e or -f ? - boolean seenScript = false; - // have we seen -f - already? - boolean seenStdin = false; - for (int i=0; i < args.length; i++) { - String arg = args[i]; - if (arg.equals("-classpath") || - arg.equals("-cp")) { - // handled already, just continue - i++; - continue; - } - - // collect non-option arguments and pass these as script arguments - if (!arg.startsWith("-")) { - int numScriptArgs; - int startScriptArg; - if (seenScript) { - // if we have seen -e or -f already all non-option arguments - // are passed as script arguments - numScriptArgs = args.length - i; - startScriptArg = i; - } else { - // if we have not seen -e or -f, first non-option argument - // is treated as script file name and rest of the non-option - // arguments are passed to script as script arguments - numScriptArgs = args.length - i - 1; - startScriptArg = i + 1; - ScriptEngine se = getScriptEngine(currentLanguage); - addFileSource(se, args[i], currentEncoding); - } - // collect script arguments and return to main - String[] result = new String[numScriptArgs]; - System.arraycopy(args, startScriptArg, result, 0, numScriptArgs); - return result; - } - - if (arg.startsWith("-D")) { - String value = arg.substring(2); - int eq = value.indexOf('='); - if (eq != -1) { - System.setProperty(value.substring(0, eq), - value.substring(eq + 1)); - } else { - if (!value.isEmpty()) { - System.setProperty(value, ""); - } else { - // do not allow empty property name - usage(EXIT_CMD_NO_PROPNAME); - } - } - continue; - } else if (arg.equals("-?") || - arg.equals("-h") || - arg.equals("--help") || - // -help: legacy. - arg.equals("-help")) { - usage(EXIT_SUCCESS); - } else if (arg.equals("-e")) { - seenScript = true; - if (++i == args.length) - usage(EXIT_CMD_NO_SCRIPT); - - ScriptEngine se = getScriptEngine(currentLanguage); - addStringSource(se, args[i]); - continue; - } else if (arg.equals("-encoding")) { - if (++i == args.length) - usage(EXIT_CMD_NO_ENCODING); - currentEncoding = args[i]; - continue; - } else if (arg.equals("-f")) { - seenScript = true; - if (++i == args.length) - usage(EXIT_CMD_NO_FILE); - ScriptEngine se = getScriptEngine(currentLanguage); - if (args[i].equals("-")) { - if (seenStdin) { - usage(EXIT_MULTIPLE_STDIN); - } else { - seenStdin = true; - } - addInteractiveMode(se); - } else { - addFileSource(se, args[i], currentEncoding); - } - continue; - } else if (arg.equals("-l")) { - if (++i == args.length) - usage(EXIT_CMD_NO_LANG); - currentLanguage = args[i]; - continue; - } else if (arg.equals("-q")) { - listScriptEngines(); - } - // some unknown option... - usage(EXIT_UNKNOWN_OPTION); - } - - if (! seenScript) { - ScriptEngine se = getScriptEngine(currentLanguage); - addInteractiveMode(se); - } - return new String[0]; - } - - /** - * Adds interactive mode Command - * @param se ScriptEngine to use in interactive mode. - */ - private static void addInteractiveMode(final ScriptEngine se) { - scripts.add(new Command() { - public void run(String[] args) { - setScriptArguments(se, args); - processSource(se, "-", null); - } - }); - } - - /** - * Adds script source file Command - * @param se ScriptEngine used to evaluate the script file - * @param fileName script file name - * @param encoding script file encoding - */ - private static void addFileSource(final ScriptEngine se, - final String fileName, - final String encoding) { - scripts.add(new Command() { - public void run(String[] args) { - setScriptArguments(se, args); - processSource(se, fileName, encoding); - } - }); - } - - /** - * Adds script string source Command - * @param se ScriptEngine to be used to evaluate the script string - * @param source Script source string - */ - private static void addStringSource(final ScriptEngine se, - final String source) { - scripts.add(new Command() { - public void run(String[] args) { - setScriptArguments(se, args); - String oldFile = setScriptFilename(se, ""); - try { - evaluateString(se, source); - } finally { - setScriptFilename(se, oldFile); - } - } - }); - } - - /** - * Prints list of script engines available and exits. - */ - private static void listScriptEngines() { - List factories = engineManager.getEngineFactories(); - for (ScriptEngineFactory factory: factories) { - getError().println(getMessage("engine.info", - new Object[] { factory.getLanguageName(), - factory.getLanguageVersion(), - factory.getEngineName(), - factory.getEngineVersion() - })); - } - System.exit(EXIT_SUCCESS); - } - - /** - * Processes a given source file or standard input. - * @param se ScriptEngine to be used to evaluate - * @param filename file name, can be null - * @param encoding script file encoding, can be null - */ - private static void processSource(ScriptEngine se, String filename, - String encoding) { - if (filename.equals("-")) { - Charset charset = Charset.forName(System.getProperty("stdin.encoding"), Charset.defaultCharset()); - BufferedReader in = new BufferedReader(new InputStreamReader(System.in, charset)); - boolean hitEOF = false; - String prompt = getPrompt(se); - se.put(ScriptEngine.FILENAME, ""); - while (!hitEOF) { - getError().print(prompt); - String source = ""; - try { - source = in.readLine(); - } catch (IOException ioe) { - getError().println(ioe.toString()); - } - if (source == null) { - hitEOF = true; - break; - } - Object res = evaluateString(se, source, false); - if (res != null) { - res = res.toString(); - if (res == null) { - res = "null"; - } - getError().println(res); - } - } - } else { - FileInputStream fis = null; - try { - fis = new FileInputStream(filename); - } catch (FileNotFoundException fnfe) { - getError().println(getMessage("file.not.found", - new Object[] { filename })); - System.exit(EXIT_FILE_NOT_FOUND); - } - evaluateStream(se, fis, filename, encoding); - } - } - - /** - * Evaluates given script source - * @param se ScriptEngine to evaluate the string - * @param script Script source string - * @param exitOnError whether to exit the process on script error - */ - private static Object evaluateString(ScriptEngine se, - String script, boolean exitOnError) { - try { - return se.eval(script); - } catch (ScriptException sexp) { - getError().println(getMessage("string.script.error", - new Object[] { sexp.getMessage() })); - if (exitOnError) - System.exit(EXIT_SCRIPT_ERROR); - } catch (Exception exp) { - exp.printStackTrace(getError()); - if (exitOnError) - System.exit(EXIT_SCRIPT_ERROR); - } - - return null; - } - - /** - * Evaluate script string source and exit on script error - * @param se ScriptEngine to evaluate the string - * @param script Script source string - */ - private static void evaluateString(ScriptEngine se, String script) { - evaluateString(se, script, true); - } - - /** - * Evaluates script from given reader - * @param se ScriptEngine to evaluate the string - * @param reader Reader from which is script is read - * @param name file name to report in error. - */ - private static Object evaluateReader(ScriptEngine se, - Reader reader, String name) { - String oldFilename = setScriptFilename(se, name); - try { - return se.eval(reader); - } catch (ScriptException sexp) { - getError().println(getMessage("file.script.error", - new Object[] { name, sexp.getMessage() })); - System.exit(EXIT_SCRIPT_ERROR); - } catch (Exception exp) { - exp.printStackTrace(getError()); - System.exit(EXIT_SCRIPT_ERROR); - } finally { - setScriptFilename(se, oldFilename); - } - return null; - } - - /** - * Evaluates given input stream - * @param se ScriptEngine to evaluate the string - * @param is InputStream from which script is read - * @param name file name to report in error - */ - private static Object evaluateStream(ScriptEngine se, - InputStream is, String name, - String encoding) { - BufferedReader reader = null; - if (encoding != null) { - try { - reader = new BufferedReader(new InputStreamReader(is, - encoding)); - } catch (UnsupportedEncodingException uee) { - getError().println(getMessage("encoding.unsupported", - new Object[] { encoding })); - System.exit(EXIT_NO_ENCODING_FOUND); - } - } else { - reader = new BufferedReader(new InputStreamReader(is)); - } - return evaluateReader(se, reader, name); - } - - /** - * Prints usage message and exits - * @param exitCode process exit code - */ - private static void usage(int exitCode) { - getError().println(getMessage("main.usage", - new Object[] { PROGRAM_NAME })); - System.exit(exitCode); - } - - /** - * Gets prompt for interactive mode - * @return prompt string to use - */ - private static String getPrompt(ScriptEngine se) { - List names = se.getFactory().getNames(); - return names.get(0) + "> "; - } - - /** - * Get formatted, localized error message - */ - private static String getMessage(String key, Object[] params) { - return MessageFormat.format(msgRes.getString(key), params); - } - - // stream to print error messages - private static PrintStream getError() { - return System.err; - } - - // get current script engine - private static ScriptEngine getScriptEngine(String lang) { - ScriptEngine se = engines.get(lang); - if (se == null) { - se = engineManager.getEngineByName(lang); - if (se == null) { - getError().println(getMessage("engine.not.found", - new Object[] { lang })); - System.exit(EXIT_ENGINE_NOT_FOUND); - } - - // initialize the engine - initScriptEngine(se); - // to avoid re-initialization of engine, store it in a map - engines.put(lang, se); - } - return se; - } - - // initialize a given script engine - private static void initScriptEngine(ScriptEngine se) { - // put engine global variable - se.put("engine", se); - - // load init. file from resource - List exts = se.getFactory().getExtensions(); - InputStream sysIn = null; - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - for (String ext : exts) { - try { - sysIn = Main.class.getModule().getResourceAsStream("com/sun/tools/script/shell/init." + ext); - } catch (IOException ioe) { - throw new RuntimeException(ioe); - } - if (sysIn != null) break; - } - if (sysIn != null) { - evaluateStream(se, sysIn, "", null); - } - } - - /** - * Checks for -classpath, -cp in command line args. Creates a ClassLoader - * and sets it as Thread context loader for current thread. - * - * @param args command line argument array - */ - private static void checkClassPath(String[] args) { - String classPath = null; - for (int i = 0; i < args.length; i++) { - if (args[i].equals("-classpath") || - args[i].equals("-cp")) { - if (++i == args.length) { - // just -classpath or -cp with no value - usage(EXIT_CMD_NO_CLASSPATH); - } else { - classPath = args[i]; - } - } - } - - if (classPath != null) { - /* We create a class loader, configure it with specified - * classpath values and set the same as context loader. - * Note that ScriptEngineManager uses context loader to - * load script engines. So, this ensures that user defined - * script engines will be loaded. For classes referred - * from scripts, Rhino engine uses thread context loader - * but this is script engine dependent. We don't have - * script engine independent solution anyway. Unless we - * know the class loader used by a specific engine, we - * can't configure correct loader. - */ - URL[] urls = pathToURLs(classPath); - URLClassLoader loader = new URLClassLoader(urls); - Thread.currentThread().setContextClassLoader(loader); - } - - // now initialize script engine manager. Note that this has to - // be done after setting the context loader so that manager - // will see script engines from user specified classpath - engineManager = new ScriptEngineManager(); - } - - /** - * Utility method for converting a search path string to an array - * of directory and JAR file URLs. - * - * @param path the search path string - * @return the resulting array of directory and JAR file URLs - */ - private static URL[] pathToURLs(String path) { - String[] components = path.split(File.pathSeparator); - URL[] urls = new URL[components.length]; - int count = 0; - while(count < components.length) { - URL url = fileToURL(new File(components[count])); - if (url != null) { - urls[count++] = url; - } - } - if (urls.length != count) { - URL[] tmp = new URL[count]; - System.arraycopy(urls, 0, tmp, 0, count); - urls = tmp; - } - return urls; - } - - /** - * Returns the directory or JAR file URL corresponding to the specified - * local file name. - * - * @param file the File object - * @return the resulting directory or JAR file URL, or null if unknown - */ - private static URL fileToURL(File file) { - String name; - try { - name = file.getCanonicalPath(); - } catch (IOException e) { - name = file.getAbsolutePath(); - } - name = name.replace(File.separatorChar, '/'); - if (!name.startsWith("/")) { - name = "/" + name; - } - // If the file does not exist, then assume that it's a directory - if (!file.isFile()) { - name = name + "/"; - } - try { - @SuppressWarnings("deprecation") - var result = new URL("file", "", name); - return result; - } catch (MalformedURLException e) { - throw new IllegalArgumentException("file"); - } - } - - private static void setScriptArguments(ScriptEngine se, String[] args) { - se.put("arguments", args); - se.put(ScriptEngine.ARGV, args); - } - - private static String setScriptFilename(ScriptEngine se, String name) { - String oldName = (String) se.get(ScriptEngine.FILENAME); - se.put(ScriptEngine.FILENAME, name); - return oldName; - } - - // exit codes - private static final int EXIT_SUCCESS = 0; - private static final int EXIT_CMD_NO_CLASSPATH = 1; - private static final int EXIT_CMD_NO_FILE = 2; - private static final int EXIT_CMD_NO_SCRIPT = 3; - private static final int EXIT_CMD_NO_LANG = 4; - private static final int EXIT_CMD_NO_ENCODING = 5; - private static final int EXIT_CMD_NO_PROPNAME = 6; - private static final int EXIT_UNKNOWN_OPTION = 7; - private static final int EXIT_ENGINE_NOT_FOUND = 8; - private static final int EXIT_NO_ENCODING_FOUND = 9; - private static final int EXIT_SCRIPT_ERROR = 10; - private static final int EXIT_FILE_NOT_FOUND = 11; - private static final int EXIT_MULTIPLE_STDIN = 12; - - // default scripting language - private static final String DEFAULT_LANGUAGE = "js"; - // list of scripts to process - private static List scripts; - // the script engine manager - private static ScriptEngineManager engineManager; - // map of engines we loaded - private static Map engines; - // error messages resource - private static ResourceBundle msgRes; - private static String BUNDLE_NAME = "com.sun.tools.script.shell.messages"; - private static String PROGRAM_NAME = "jrunscript"; - - static { - scripts = new ArrayList(); - engines = new HashMap(); - msgRes = ResourceBundle.getBundle(BUNDLE_NAME, Locale.getDefault()); - } -} diff --git a/src/java.scripting/share/classes/com/sun/tools/script/shell/init.js b/src/java.scripting/share/classes/com/sun/tools/script/shell/init.js deleted file mode 100644 index ced3ba06367..00000000000 --- a/src/java.scripting/share/classes/com/sun/tools/script/shell/init.js +++ /dev/null @@ -1,927 +0,0 @@ -/* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/** - * jrunscript JavaScript built-in functions and objects. - */ - -/** - * Creates an object that delegates all method calls on - * it to the 'invoke' method on the given delegate object.
- * - * Example: - *

- * 
- *     var x  = { invoke: function(name, args) { //code...}
- *     var y = new JSInvoker(x);
- *     y.func(3, 3); // calls x.invoke('func', args); where args is array of arguments
- * 
- * 
- * @param obj object to be wrapped by JSInvoker - * @constructor - */ -function JSInvoker(obj) { - return new JSAdapter({ - __get__ : function(name) { - return function() { - return obj.invoke(name, arguments); - } - } - }); -} - -/** - * This variable represents OS environment. Environment - * variables can be accessed as fields of this object. For - * example, env.PATH will return PATH value configured. - */ -var env = new JSAdapter({ - __get__ : function (name) { - return java.lang.System.getenv(name); - }, - __has__ : function (name) { - return java.lang.System.getenv().containsKey(name); - }, - __getIds__ : function() { - return java.lang.System.getenv().keySet().toArray(); - }, - __delete__ : function(name) { - println("can't delete env item"); - }, - __put__ : function (name, value) { - println("can't change env item"); - }, - toString: function() { - return java.lang.System.getenv().toString(); - } -}); - -/** - * Creates a convenient script object to deal with java.util.Map instances. - * The result script object's field names are keys of the Map. For example, - * scriptObj.keyName can be used to access value associated with given key.
- * Example: - *
- * 
- *     var x = java.lang.SystemProperties();
- *     var y = jmap(x);
- *     println(y['java.class.path']); // prints java.class.path System property
- *     delete y['java.class.path']; // remove java.class.path System property
- * 
- * 
- * - * @param map java.util.Map instance that will be wrapped - * @constructor - */ -function jmap(map) { - return new JSAdapter({ - __get__ : function(name) { - if (map.containsKey(name)) { - return map.get(name); - } else { - return undefined; - } - }, - __has__ : function(name) { - return map.containsKey(name); - }, - - __delete__ : function (name) { - return map.remove(name); - }, - __put__ : function(name, value) { - map.put(name, value); - }, - __getIds__ : function() { - return map.keySet().toArray(); - }, - toString: function() { - return map.toString(); - } - }); -} - -/** - * Creates a convenient script object to deal with java.util.List instances. - * The result script object behaves like an array. For example, - * scriptObj[index] syntax can be used to access values in the List instance. - * 'length' field gives size of the List.
- * - * Example: - *
- * 
- *    var x = new java.util.ArrayList(4);
- *    x.add('Java');
- *    x.add('JavaScript');
- *    x.add('SQL');
- *    x.add('XML');
- *
- *    var y = jlist(x);
- *    println(y[2]); // prints third element of list
- *    println(y.length); // prints size of the list
- *
- * @param map java.util.List instance that will be wrapped
- * @constructor
- */
-function jlist(list) {
-    function isValid(index) {
-        return typeof(index) == 'number' &&
-            index > -1 && index < list.size();
-    }
-    return new JSAdapter({
-        __get__ :  function(name) {
-            if (isValid(name)) {
-                return list.get(name);
-            } else if (name == 'length') {
-                return list.size();
-            } else {
-                return undefined;
-            }
-        },
-        __has__ : function (name) {
-            return isValid(name) || name == 'length';
-        },
-        __delete__ : function(name) {
-            if (isValid(name)) {
-                list.remove(name);
-            }
-        },
-        __put__ : function(name, value) {
-            if (isValid(name)) {
-                list.set(name, value);
-            }
-        },
-        __getIds__: function() {
-            var res = new Array(list.size());
-            for (var i = 0; i < res.length; i++) {
-                res[i] = i;
-            }
-            return res;
-        },
-        toString: function() {
-            return list.toString();
-        }
-    });
-}
-
-/**
- * This is java.lang.System properties wrapped by JSAdapter.
- * For eg. to access java.class.path property, you can use
- * the syntax sysProps["java.class.path"]
- */
-var sysProps = new JSAdapter({
-    __get__ : function (name) {
-        return java.lang.System.getProperty(name);
-    },
-    __has__ : function (name) {
-        return java.lang.System.getProperty(name) != null;
-    },
-    __getIds__ : function() {
-        return java.lang.System.getProperties().keySet().toArray();
-    },
-    __delete__ : function(name) {
-        java.lang.System.clearProperty(name);
-        return true;
-    },
-    __put__ : function (name, value) {
-        java.lang.System.setProperty(name, value);
-    },
-    toString: function() {
-        return "";
-    }
-});
-
-// stdout, stderr & stdin
-var out = java.lang.System.out;
-var err = java.lang.System.err;
-// can't use 'in' because it is a JavaScript keyword :-(
-var inp = java.lang.System["in"];
-
-var BufferedInputStream = java.io.BufferedInputStream;
-var BufferedOutputStream = java.io.BufferedOutputStream;
-var BufferedReader = java.io.BufferedReader;
-var DataInputStream = java.io.DataInputStream;
-var File = java.io.File;
-var FileInputStream = java.io.FileInputStream;
-var FileOutputStream = java.io.FileOutputStream;
-var InputStream = java.io.InputStream;
-var InputStreamReader = java.io.InputStreamReader;
-var OutputStream = java.io.OutputStream;
-var Reader = java.io.Reader;
-var URL = java.net.URL;
-
-/**
- * Generic any object to input stream mapper
- * @param str input file name, URL or InputStream
- * @return InputStream object
- * @private
- */
-function inStream(str) {
-    if (typeof(str) == "string") {
-        // '-' means standard input
-        if (str == '-') {
-            return java.lang.System["in"];
-        }
-        // try file first
-        var file = null;
-        try {
-            file = pathToFile(str);
-        } catch (e) {
-        }
-        if (file && file.exists()) {
-            return new FileInputStream(file);
-        } else {
-            try {
-                // treat the string as URL
-                return new URL(str).openStream();
-            } catch (e) {
-                throw 'file or URL ' + str + ' not found';
-            }
-        }
-    } else {
-        if (str instanceof InputStream) {
-            return str;
-        } else if (str instanceof URL) {
-            return str.openStream();
-        } else if (str instanceof File) {
-            return new FileInputStream(str);
-        }
-    }
-    // everything failed, just give input stream
-    return java.lang.System["in"];
-}
-
-/**
- * Generic any object to output stream mapper
- *
- * @param out output file name or stream
- * @return OutputStream object
- * @private
- */
-function outStream(out) {
-    if (typeof(out) == "string") {
-        if (out == '>') {
-            return java.lang.System.out;
-        } else {
-            // treat it as file
-            return new FileOutputStream(pathToFile(out));
-        }
-    } else {
-        if (out instanceof OutputStream) {
-            return out;
-        } else if (out instanceof File) {
-            return new FileOutputStream(out);
-        }
-    }
-
-    // everything failed, just return System.out
-    return java.lang.System.out;
-}
-
-/**
- * stream close takes care not to close stdin, out & err.
- * @private
- */
-function streamClose(stream) {
-    if (stream) {
-        if (stream != java.lang.System["in"] &&
-            stream != java.lang.System.out &&
-            stream != java.lang.System.err) {
-            try {
-                stream.close();
-            } catch (e) {
-                println(e);
-            }
-        }
-    }
-}
-
-/**
- * Loads and evaluates JavaScript code from a stream or file or URL
- * - * Examples: - *
- * 
- *    load('test.js'); // load script file 'test.js'
- *    load('http://java.sun.com/foo.js'); // load from a URL
- * 
- * 
- * - * @param str input from which script is loaded and evaluated - */ -if (typeof(load) == 'undefined') { - this.load = function(str) { - var stream = inStream(str); - var bstream = new BufferedInputStream(stream); - var reader = new BufferedReader(new InputStreamReader(bstream)); - var oldFilename = engine.get(engine.FILENAME); - engine.put(engine.FILENAME, str); - try { - engine.eval(reader); - } finally { - engine.put(engine.FILENAME, oldFilename); - streamClose(stream); - } - } -} - -// file system utilities - -/** - * Creates a Java byte[] of given length - * @param len size of the array to create - * @private - */ -function javaByteArray(len) { - return java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, len); -} - -var curDir = new File('.'); - -/** - * Print present working directory - */ -function pwd() { - println(curDir.getAbsolutePath()); -} - -/** - * Changes present working directory to given directory - * @param target directory to change to. optional, defaults to user's HOME - */ -function cd(target) { - if (target == undefined) { - target = sysProps["user.home"]; - } - if (!(target instanceof File)) { - target = pathToFile(target); - } - if (target.exists() && target.isDirectory()) { - curDir = target; - } else { - println(target + " is not a directory"); - } -} - -/** - * Converts path to java.io.File taking care of shell present working dir - * - * @param pathname file path to be converted - * @private - */ -function pathToFile(pathname) { - var tmp = pathname; - if (!(tmp instanceof File)) { - tmp = new File(tmp); - } - if (!tmp.isAbsolute()) { - return new File(curDir, pathname); - } else { - return tmp; - } -} - -/** - * Copies a file or URL or stream to another file or stream - * - * @param from input file or URL or stream - * @param to output stream or file - */ -function cp(from, to) { - if (from == to) { - println("file " + from + " cannot be copied onto itself!"); - return; - } - var inp = inStream(from); - var out = outStream(to); - var binp = new BufferedInputStream(inp); - var bout = new BufferedOutputStream(out); - var buff = javaByteArray(1024); - var len; - while ((len = binp.read(buff)) > 0 ) - bout.write(buff, 0, len); - - bout.flush(); - streamClose(inp); - streamClose(out); -} - -/** - * Shows the content of a file or URL or any InputStream
- * Examples: - *
- * 
- *    cat('test.txt'); // show test.txt file contents
- *    cat('http://java.net'); // show the contents from the URL http://java.net
- * 
- * 
- * @param obj input to show - * @param pattern optional. show only the lines matching the pattern - */ -function cat(obj, pattern) { - if (obj instanceof File && obj.isDirectory()) { - ls(obj); - return; - } - - var inp = null; - if (!(obj instanceof Reader)) { - inp = inStream(obj); - obj = new BufferedReader(new InputStreamReader(inp)); - } - var line; - if (pattern) { - var count = 1; - while ((line=obj.readLine()) != null) { - if (line.match(pattern)) { - println(count + "\t: " + line); - } - count++; - } - } else { - while ((line=obj.readLine()) != null) { - println(line); - } - } -} - -/** - * Returns directory part of a filename - * - * @param pathname input path name - * @return directory part of the given file name - */ -function dirname(pathname) { - var dirName = "."; - // Normalize '/' to local file separator before work. - var i = pathname.replace('/', File.separatorChar ).lastIndexOf( - File.separator ); - if ( i != -1 ) - dirName = pathname.substring(0, i); - return dirName; -} - -/** - * Creates a new dir of given name - * - * @param dir name of the new directory - */ -function mkdir(dir) { - dir = pathToFile(dir); - println(dir.mkdir()? "created" : "can not create dir"); -} - -/** - * Creates the directory named by given pathname, including - * any necessary but nonexistent parent directories. - * - * @param dir input path name - */ -function mkdirs(dir) { - dir = pathToFile(dir); - println(dir.mkdirs()? "created" : "can not create dirs"); -} - -/** - * Removes a given file - * - * @param pathname name of the file - */ -function rm(pathname) { - var file = pathToFile(pathname); - if (!file.exists()) { - println("file not found: " + pathname); - return false; - } - // note that delete is a keyword in JavaScript! - println(file["delete"]()? "deleted" : "can not delete"); -} - -/** - * Removes a given directory - * - * @param pathname name of the directory - */ -function rmdir(pathname) { - rm(pathname); -} - -/** - * Synonym for 'rm' - */ -function del(pathname) { - rm(pathname); -} - -/** - * Moves a file to another - * - * @param from original name of the file - * @param to new name for the file - */ -function mv(from, to) { - println(pathToFile(from).renameTo(pathToFile(to))? - "moved" : "can not move"); -} - -/** - * Synonym for 'mv'. - */ -function ren(from, to) { - mv(from, to); -} - -var months = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]; - -/** - * Helper function called by ls - * @private - */ -function printFile(f) { - var sb = new java.lang.StringBuffer(); - sb.append(f.isDirectory()? "d" : "-"); - sb.append(f.canRead() ? "r": "-" ); - sb.append(f.canWrite() ? "w": "-" ); - sb.append(" "); - - var d = new java.util.Date(f.lastModified()); - var c = new java.util.GregorianCalendar(); - c.setTime(d); - var day = c.get(java.util.Calendar.DAY_OF_MONTH); - sb.append(months[c.get(java.util.Calendar.MONTH)] - + " " + day ); - if (day < 10) { - sb.append(" "); - } - - // to get fixed length 'length' field - var fieldlen = 8; - var len = new java.lang.StringBuffer(); - for(var j=0; j - * - * Examples: - *
- * 
- *    find('.')
- *    find('.', '.*\.class', rm);  // remove all .class files
- *    find('.', '.*\.java');       // print fullpath of each .java file
- *    find('.', '.*\.java', cat);  // print all .java files
- * 
- * 
- * - * @param dir directory to search files - * @param pattern to search in the files - * @param callback function to call for matching files - */ -function find(dir, pattern, callback) { - dir = pathToFile(dir); - if (!callback) callback = print; - var files = dir.listFiles(); - for (var f in files) { - var file = files[f]; - if (file.isDirectory()) { - find(file, pattern, callback); - } else { - if (pattern) { - if (file.getName().match(pattern)) { - callback(file); - } - } else { - callback(file); - } - } - } -} - -// process utilities - -/** - * Exec's a child process, waits for completion & returns exit code - * - * @param cmd command to execute in child process - */ -function exec(cmd) { - var process = java.lang.Runtime.getRuntime().exec(cmd); - var inp = new DataInputStream(process.getInputStream()); - var line = null; - while ((line = inp.readLine()) != null) { - println(line); - } - process.waitFor(); - $exit = process.exitValue(); -} - -if (typeof(exit) == 'undefined') { - /** - * Exit the shell program. - * - * @param exitCode integer code returned to OS shell. - * optional, defaults to 0 - */ - this.exit = function (code) { - if (code) { - java.lang.System.exit(code + 0); - } else { - java.lang.System.exit(0); - } - } -} - -if (typeof(quit) == 'undefined') { - /** - * synonym for exit - */ - this.quit = function (code) { - exit(code); - } -} - -// XML utilities - -/** - * Converts input to DOM Document object - * - * @param inp file or reader. optional, without this param, - * this function returns a new DOM Document. - * @return returns a DOM Document object - */ -function XMLDocument(inp) { - var factory = javax.xml.parsers.DocumentBuilderFactory.newInstance(); - var builder = factory.newDocumentBuilder(); - if (inp) { - if (typeof(inp) == "string") { - return builder.parse(pathToFile(inp)); - } else { - return builder.parse(inp); - } - } else { - return builder.newDocument(); - } -} - -/** - * Converts arbitrary stream, file, URL to XMLSource - * - * @param inp input stream or file or URL - * @return XMLSource object - */ -function XMLSource(inp) { - if (inp instanceof javax.xml.transform.Source) { - return inp; - } else if (inp instanceof Packages.org.w3c.dom.Document) { - return new javax.xml.transform.dom.DOMSource(inp); - } else { - inp = new BufferedInputStream(inStream(inp)); - return new javax.xml.transform.stream.StreamSource(inp); - } -} - -/** - * Converts arbitrary stream, file to XMLResult - * - * @param inp output stream or file - * @return XMLResult object - */ -function XMLResult(out) { - if (out instanceof javax.xml.transform.Result) { - return out; - } else if (out instanceof Packages.org.w3c.dom.Document) { - return new javax.xml.transform.dom.DOMResult(out); - } else { - out = new BufferedOutputStream(outStream(out)); - return new javax.xml.transform.stream.StreamResult(out); - } -} - -/** - * Perform XSLT transform - * - * @param inp Input XML to transform (URL, File or InputStream) - * @param style XSL Stylesheet to be used (URL, File or InputStream). optional. - * @param out Output XML (File or OutputStream - */ -function XSLTransform(inp, style, out) { - switch (arguments.length) { - case 2: - inp = arguments[0]; - out = arguments[1]; - break; - case 3: - inp = arguments[0]; - style = arguments[1]; - out = arguments[2]; - break; - default: - println("XSL transform requires 2 or 3 arguments"); - return; - } - - var factory = javax.xml.transform.TransformerFactory.newInstance(); - var transformer; - if (style) { - transformer = factory.newTransformer(XMLSource(style)); - } else { - transformer = factory.newTransformer(); - } - var source = XMLSource(inp); - var result = XMLResult(out); - transformer.transform(source, result); - if (source.getInputStream) { - streamClose(source.getInputStream()); - } - if (result.getOutputStream) { - streamClose(result.getOutputStream()); - } -} - -// miscellaneous utilities - -/** - * Prints which command is selected from PATH - * - * @param cmd name of the command searched from PATH - */ -function which(cmd) { - var st = new java.util.StringTokenizer(env.PATH, File.pathSeparator); - while (st.hasMoreTokens()) { - var file = new File(st.nextToken(), cmd); - if (file.exists()) { - println(file.getAbsolutePath()); - return; - } - } -} - -/** - * Prints IP addresses of given domain name - * - * @param name domain name - */ -function ip(name) { - var addrs = InetAddress.getAllByName(name); - for (var i in addrs) { - println(addrs[i]); - } -} - -/** - * Prints current date in current locale - */ -function date() { - println(new Date().toLocaleString()); -} - -/** - * Echoes the given string arguments - */ -function echo(x) { - for (var i = 0; i < arguments.length; i++) { - println(arguments[i]); - } -} - -if (typeof(printf) == 'undefined') { - /** - * This is C-like printf - * - * @param format string to format the rest of the print items - * @param args variadic argument list - */ - this.printf = function (format, args/*, more args*/) { - var array = java.lang.reflect.Array.newInstance(java.lang.Object, - arguments.length - 1); - for (var i = 0; i < array.length; i++) { - array[i] = arguments[i+1]; - } - java.lang.System.out.printf(format, array); - } -} - -/** - * Reads one or more lines from stdin after printing prompt - * - * @param prompt optional, default is '>' - * @param multiline to tell whether to read single line or multiple lines - */ -function read(prompt, multiline) { - if (!prompt) { - prompt = '>'; - } - var inp = java.lang.System["in"]; - var reader = new BufferedReader(new InputStreamReader(inp)); - if (multiline) { - var line = ''; - while (true) { - java.lang.System.err.print(prompt); - java.lang.System.err.flush(); - var tmp = reader.readLine(); - if (tmp == '' || tmp == null) break; - line += tmp + '\n'; - } - return line; - } else { - java.lang.System.err.print(prompt); - java.lang.System.err.flush(); - return reader.readLine(); - } -} - -if (typeof(println) == 'undefined') { - // just synonym to print - this.println = print; -} - diff --git a/src/java.scripting/share/classes/com/sun/tools/script/shell/messages.properties b/src/java.scripting/share/classes/com/sun/tools/script/shell/messages.properties deleted file mode 100644 index a6c7762ed34..00000000000 --- a/src/java.scripting/share/classes/com/sun/tools/script/shell/messages.properties +++ /dev/null @@ -1,68 +0,0 @@ -# -# Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -string.script.error=\ - script error: {0} - -file.script.error=\ - script error in file {0} : {1} - -file.not.found=\ - script file {0} is not found - -engine.not.found=\ - script engine for language {0} can not be found - -engine.info=\ - Language {0} {1} implementation "{2}" {3} - -encoding.unsupported=\ - encoding {0} is not supported - -main.usage=\ -Usage: {0} [options] [arguments...]\n\ -\n\ -where [options] include:\n\ -\ \-classpath Specify where to find user class files \n\ -\ \-cp Specify where to find user class files \n\ -\ \-D= Set a system property \n\ -\ \-J Pass directly to the runtime system \n\ -\ \-l Use specified scripting language \n\ -\ \-e